简单的TcpServer(英译中)

目录

  • 一、TCP socket API 详解
    • 1.1 socket()
    • 1.2 bind()
    • 1.3 listen()
    • 1.4 accept()
    • 1.5 connect
  • 二、TcpServer(英译中)
    • 2.1 TcpServer.hpp
    • 2.2 TcpClient.cc
    • 2.3 Task.hpp
    • 2.4 Thread.hpp
    • 2.5 ThreadPool.hpp
    • 2.6 makefile
    • 2.7 Main.cc
    • 2.8 log.hpp
    • 2.9 Init.hpp
    • 2.10 dict.txt
    • 2.11 Daemon.hpp

一、TCP socket API 详解

实现一个简单的英译汉的功能。
下面介绍程序中用到的socket API,这些函数都在sys/socket.h中。

1.1 socket()

在这里插入图片描述

socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符;
应用程序可以像读写文件一样用read/write在网络上收发数据;
如果socket()调用出错则返回-1;
对于IPv4, family参数指定为AF_INET;
对于TCP协议,type参数指定为SOCK_STREAM, 表示面向流的传输协议;
protocol参数的就不做介绍了,指定为0即可。

1.2 bind()

在这里插入图片描述
服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接; 服务器需要调用bind绑定一个固定的网络地址和端口号;

bind()成功返回0,失败返回-1。

bind()的作用是将参数sockfd和myaddr绑定在一起, 使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址和端口号;

前面讲过,struct sockaddr *是一个通用指针类型,myaddr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度;

我们的程序中对myaddr参数是这样初始化的:
在这里插入图片描述

  1. 将整个结构体清零;
  2. 设置地址类型为AF_INET;
  3. 网络地址为INADDR_ANY, 这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP 地址, 这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP 地址;
  4. 端口号为SERV_PORT, 我们定义为8080;

1.3 listen()

在这里插入图片描述
listen()声明sockfd处于监听状态, 并且最多允许有backlog个客户端处于连接等待状态, 如果接收到更多的连接请求就忽略, 这里设置不会太大(一般是5),listen()成功返回0,失败返回-1;

1.4 accept()

在这里插入图片描述
1、三次握手完成后, 服务器调用accept()接受连接;
2、如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来;addr是一个传出参数,
accept()返回时传出客户端的地址和端口号;
3、如果给addr 参数传NULL,表示不关心客户端的地址;
4、addrlen参数是一个传入传出参数(value-result argument), 传入的是调用者提供的, 缓冲区addr的长度以避免缓冲区溢出问题, 传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区);

1.5 connect

在这里插入图片描述
1、客户端需要调用connect()连接服务器;
2、connect和bind的参数形式一致, 区别在于bind的参数是自己的地址, 而connect的参数是对方的地址;
3、connect()成功返回0,出错返回-1;

二、TcpServer(英译中)

2.1 TcpServer.hpp

#pragma once#include <iostream>
using namespace std;#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"
#include <strings.h>
#include <functional>
#include <cstring>
#include <unordered_map>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "Daemon.hpp"const static int NUM = 1024;
const static string DEFAULT_IP = "0.0.0.0";
const static uint16_t DEFAULT_PORT = 8080;using func_t = function<string(string)>;enum
{SOCKET_ERROR = 3,BIND_ERROR,LISTEN_ERROR,ACCEPT_ERRNO,READ_ERROR
};extern Log log;// class ThreadData
// {
// public:
//     ThreadData(int sockfd, string clientip, uint16_t clientport, void *argv)
//         : _sockfd(sockfd), _clientip(clientip), _clientport(clientport), _argv(argv)
//     {
//     }// public:
//     int _sockfd;
//     string _clientip;
//     uint16_t _clientport;
//     void *_argv;
// };class TcpServer
{
public:TcpServer(func_t func, const uint16_t &port = DEFAULT_PORT, const string &ip = DEFAULT_IP): _port(port), _ip(ip), _func(func){}~TcpServer(){if (_listen_sockfd > 0){close(_listen_sockfd);}}void Init(){// 创建套接字_listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listen_sockfd < 0){log(Fatal, "create _listen_socket failed,errno code:%d,error code string:%s", errno, strerror(errno));exit(SOCKET_ERROR);}log(Info, "create _listen_socket successed,_listen_sockfd:%d", _listen_sockfd);//设置端口复用int opt=1;setsockopt(_listen_sockfd,SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,&opt,sizeof(opt));// 绑定struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY;if (bind(_listen_sockfd, (struct sockaddr *)(&local), sizeof(local)) < 0){log(Fatal, "bind failed,errno code:%d,error code string:%s", errno, strerror(errno));exit(BIND_ERROR);}log(Info, "bind successed...,_listen_socket:%d",_listen_sockfd);// 监听if (listen(_listen_sockfd, 5) < 0){log(Fatal, "set _listen_sockfd failed,errno code:%d,error code string:%s", errno, strerror(errno));exit(LISTEN_ERROR);}log(Info, "set _listen_sockfd successed...,_listen_socket:%d",_listen_sockfd);}// static void* Service(void* argv)// {//     pthread_detach(pthread_self());//     ThreadData* td=static_cast<ThreadData*>(argv);//     char buffer[NUM];//     bzero(buffer, sizeof(buffer));//     while (true)//     {//         ssize_t s = read(td->_sockfd, buffer, sizeof(buffer) - 1);//         if (s > 0)//         {//             buffer[s] = '\0';//             cout << "client[ip:" << td->_clientip << " port:" << td->_clientport << "]# " << buffer << endl;//             // string message = _func(buffer);//             // write(sockfd, message.c_str(), message.size());//             write(td->_sockfd, buffer, sizeof(buffer));//             cout << "write done..." << endl;//         }//         else if (s == 0)//         {//             close(td->_sockfd);//             log(Info, "client exit... close sockfd:%d", td->_sockfd);//             break;//         }//         else//         {//             close(td->_sockfd);//             log(Fatal, "read failed,errno code:%d,error code string:%s,close sockfd:%d",//                 errno, strerror(errno), td->_sockfd);//             break;//         }//     }//     close(td->_sockfd);//     log(Info,"close sockfd:%d\n",td->_sockfd);// }// void Service(const int &sockfd, const string &clientip, const uint16_t &clientport)// {//     char buffer[NUM];//     bzero(buffer, sizeof(buffer));//     while (true)//     {//         ssize_t s = read(sockfd, buffer, sizeof(buffer) - 1);//         if (s > 0)//         {//             buffer[s] = '\0';//             //cout << "client[ip:" << clientip << " port:" << clientport << "]# " << buffer << endl;//             cout << "client[ip:" << clientip << " port:" << clientport << " sockfd:"<<sockfd<<"]# " << buffer << endl;//             // string message = _func(buffer);//             // write(sockfd, message.c_str(), message.size());//             // 这里不能写sizeof(buffer),不然可能会因为缓冲区问题导致client端不能正确打印//             write(sockfd, buffer, strlen(buffer));//             cout << "write done..." << endl;//         }//         else if (s == 0)//         {//             close(sockfd);//             log(Info, "client exit... ");//             break;//         }//         else//         {//             close(sockfd);//             log(Fatal, "read failed,errno code:%d,error code string:%s",//                 errno, strerror(errno));//             break;//         }//     }//     close(sockfd);//     log(Info, "close sockfd:%d", sockfd);// }// static void *Rontiue(void *argv)// {//     pthread_detach(pthread_self());//     ThreadData *td = static_cast<ThreadData *>(argv);//     TcpServer *p = static_cast<TcpServer *>(td->_argv);//     p->Service(td->_sockfd, td->_clientip, td->_clientport);//     delete td;// }void Run(){//守护进程化Daemon();//启动线程池ThreadPool<Task>::GetThreadPool()->Run();while (true){// 获取连接struct sockaddr_in client;bzero(&client, sizeof(client));socklen_t len = sizeof(client);int sockfd = accept(_listen_sockfd, (struct sockaddr *)(&client), &len);if (sockfd < 0){log(Warning, "accept sockfd failed,errno code:%d,error code string:%s", errno, strerror(errno));sleep(1);continue;}//不建议用inet_ntoa,因为inet_ntoa用的是一个静态的缓冲区,多次调用可能会导致覆盖问题char clientip[32];inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(client));uint16_t clientport = ntohs(client.sin_port);log(Info, "accept sockfd successed,get a new link...[clientip:%s,clientport:%d],sockfd:%d", clientip, clientport, sockfd);//============================================================// // 单进程版// Service(sockfd, clientip, clientport);//=============================================================//=============================================================// // 多进程版// pid_t id = fork();// if (id == 0)// {//     close(_listen_sockfd);//     // 子进程//     if (fork() > 0)//     {//         exit(0);//     }//     Service(sockfd, clientip, clientport);//     exit(5);// }// // 这里必须关闭,因为在上面的子进程已经拿到了文件描述符了,所以父进程// // 就要关闭这个文件描述符,其实父进程关闭的时候这个文件描述符并不会真正// // 地被释放,因为只是引用计数减一,只有子进程结束的时候释放该文件描述符// // 才会真正地被释放// close(sockfd);// // 这里不会被阻塞住,因为是孙子进程执行任务,儿子进程一下子就退出啦,即waitpid不会阻塞很久// waitpid(id, nullptr, 0);//=================================================================//=================================================================// // 多线程版// pthread_t tid;// ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);// pthread_create(&tid, nullptr, Rontiue, td);//==================================================================//线程池版Task t(sockfd, clientip, clientport);ThreadPool<Task>::GetThreadPool()->Push(t);//===================================================================}}private:int _listen_sockfd;    //监听套接字uint16_t _port;        //服务器端口号string _ip;            //服务器ip地址func_t _func;          //回调函数,服务器处理客户需求的方法可以自定义
};

2.2 TcpClient.cc


#include <iostream>
using namespace std;#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <pthread.h>
#include <cstring>
#include "log.hpp"const static int NUM = 1024;enum
{SOCKET_ERROR = 3,CONNECT_ERROR
};void Usage(string argv)
{cout << "\n\t"<< "Usage:" << argv << " ServerIp ServerPort" << endl<< endl;
}Log log;// int main(int argc, char *argv[])
// {
//     if (argc != 3)
//     {
//         Usage(argv[0]);
//         exit(1);
//     }//     string ServerIp = argv[1];
//     string str = argv[2];
//     uint16_t ServerPort = (uint16_t)stoi(str.c_str());//     // 创建套接字
//     int sockfd = socket(AF_INET, SOCK_STREAM, 0);
//     if (sockfd < 0)
//     {
//         log(Fatal, "create socket failed,errno code:%d,error code string:%s", errno, strerror(errno));
//         exit(SOCKET_ERROR);
//     }
//     log(Info, "create socket successed,sockfd:%d", sockfd);//     struct sockaddr_in server;
//     server.sin_family = AF_INET;
//     server.sin_addr.s_addr = inet_addr(ServerIp.c_str());
//     server.sin_port = htons(ServerPort);
//     socklen_t len = sizeof(server);//     // 建立连接
//     if (connect(sockfd, (struct sockaddr *)&server, len) < 0)
//     {
//         log(Fatal, "connect failed,errno code:%d,error code string:%s", errno, strerror(errno));
//         exit(CONNECT_ERROR);
//     }
//     log(Info, "connect successed...");//     string buffer;
//     while (true)
//     {
//         cout << "Please Enter# ";
//         getline(cin, buffer);//         write(sockfd, buffer.c_str(), buffer.size());//         //这里的message缓冲区的大小不能少于1024,否则会出错,具有原因有待查明,
//         //原因找到了,是服务端write的时候写了sizeof(buffer),导致缓冲区出现了问题
//         char message[1024];
//         bzero(message,sizeof(message));
//         ssize_t s = read(sockfd, message, sizeof(message)-1);
//         cout<<"read done..."<<endl;
//         if (s > 0)
//         {
//             message[s]='\0';
//             cout << "Server say# " << message << endl;
//         }
//         else if (s == 0)
//         {
//             log(Info, "server exit,close sockfd:%d", sockfd);
//             break;
//         }
//         else
//         {
//             log(Info, "read failed,close sockfd:%d", sockfd);
//             break;
//         }
//     }//     close(sockfd);
// }//带入了重连机制
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}string ServerIp = argv[1];string str = argv[2];uint16_t ServerPort = (uint16_t)stoi(str.c_str());//初始化服务器的结构体信息struct sockaddr_in server;server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(ServerIp.c_str());server.sin_port = htons(ServerPort);socklen_t len = sizeof(server);while (true){bool isreconnect = false;// 创建套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){log(Fatal, "create socket failed,errno code:%d,error code string:%s", errno, strerror(errno));}log(Info, "create socket successed,sockfd:%d", sockfd);int cnt = 10;do{// 建立连接if (connect(sockfd, (struct sockaddr *)&server, len) < 0){//如果连接失败,那么isreconnect就会被置为true,然后就进入重连状态cnt--;isreconnect = true;log(Warning, "connect failed,errcode:%d,errstring:%s,reconnect:%d", errno, strerror(errno), cnt);sleep(2);}else{//连接成功就跳出do while循环break;}} while (cnt && isreconnect);//最多重连cnt次,当cnt变为0的时候,就会自动跳出do while循环if (cnt == 0){//重连失败log(Warning, "connect failed,errcode:%d,errstring:%s,reconnect fail...", errno, strerror(errno));break;}log(Info, "connect successed...");string buffer;// while (true)// {cout << "Please Enter# ";getline(cin, buffer);write(sockfd, buffer.c_str(), buffer.size());// 这里的message缓冲区的大小不能少于1024,否则会出错,具有原因有待查明,// 原因找到了,是服务端write的时候写了sizeof(buffer),导致缓冲区出现了问题char message[1024];bzero(message, sizeof(message));ssize_t s = read(sockfd, message, sizeof(message) - 1);// cout << "read done..." << endl;if (s > 0){message[s] = '\0';cout << "Server say# " << message << endl;}else if (s == 0){log(Info, "server exit,close sockfd:%d", sockfd);// break;}else{log(Info, "read failed,close sockfd:%d", sockfd);// break;}// }close(sockfd);}
}

由于客户端不需要固定的端口号,因此不必调用bind(),客户端的端口号由内核自动分配。
注意:
客户端不是不允许调用bind(), 只是没有必要调用bind()固定一个端口号. 否则如果在同一台机器上启动多个客户端, 就会出现端口号被占用导致不能正确建立连接;
服务器也不是必须调用bind(), 但如果服务器不调用bind(), 内核会自动给服务器分配监听端口, 每次启动服务器时端口号都不一样, 客户端要连接服务器就会很麻烦了。

2.3 Task.hpp

#pragma once#include <stdio.h>
#include "log.hpp"
#include "Init.hpp"
#include <cstring>
#include <strings.h>Log log;//定义一个全局变量,在main函数之前就定义好了,即初始化好了
Init dict;class Task
{
public:Task(int sockfd, const std::string &clientip, const uint16_t &clientport): _sockfd(sockfd), _clientip(clientip), _clientport(clientport){}Task(){}void run(const string& threadName){// // 测试代码// char buffer[4096];// ssize_t n = read(sockfd_, buffer, sizeof(buffer));// if (n > 0)// {//     buffer[n] = 0;//     std::cout << "client key# " << buffer << std::endl;//     std::string echo_string = buffer;//     // sleep(5);//     // // close(sockfd_);//     // lg(Warning, "close sockfd %d done", sockfd_);//     // sleep(2);//     n = write(sockfd_, echo_string.c_str(), echo_string.size()); // 100 fd 不存在//     if(n < 0)//     {//         lg(Warning, "write error, errno : %d, errstring: %s", errno, strerror(errno));//     }// }// else if (n == 0)// {//     lg(Info, "%s:%d quit, server close sockfd: %d", clientip_.c_str(), clientport_, sockfd_);// }// else// {//     lg(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", sockfd_, clientip_.c_str(), clientport_);// }// close(sockfd_);char buffer[4096];bzero(buffer, sizeof(buffer));ssize_t s = read(_sockfd, buffer, sizeof(buffer) - 1);if (s > 0){// buffer[s] = '\0';// // cout << "client[ip:" << clientip << " port:" << clientport << "]# " << buffer << endl;// cout <<threadName<< "收到一条信息:client[ip:" << _clientip << " port:" << _clientport << " sockfd:" << _sockfd << "]# " << buffer << endl;// // string message = _func(buffer);// // write(sockfd, message.c_str(), message.size());// // 这里不能写sizeof(buffer),不然可能会因为缓冲区问题导致client端不能正确打印// write(_sockfd, buffer, strlen(buffer));// cout << "write done..." << endl;cout <<threadName<< "收到一条信息:client[ip:" << _clientip << " port:" << _clientport << " sockfd:" << _sockfd << "]# " << buffer << endl;string resp=dict.translation(buffer);// 这里不能写sizeof(buffer),不然可能会因为缓冲区问题导致client端不能正确打印write(_sockfd, resp.c_str(), resp.size());}else if (s == 0){close(_sockfd);log(Info, "client exit... ");}else{close(_sockfd);log(Fatal, "read failed,errno code:%d,error code string:%s",errno, strerror(errno));}close(_sockfd);log(Info, "close sockfd:%d", _sockfd);}void operator()(const string& threadName){run(threadName);}~Task(){}private:int _sockfd;std::string _clientip;uint16_t _clientport;
};

2.4 Thread.hpp

#pragma once#include <iostream>
using namespace std;
#include <functional>typedef void *(*fun_t)(void *);struct ThreadData
{
public:ThreadData(int num, void *args): _args(args){_name = "Thread-" + to_string(num);}public:string _name;void *_args;
};class Thread
{
public:Thread(int num, fun_t cb, void *args): _cb(cb), _data(num, args){}void Start(){pthread_create(&_tid, nullptr, _cb, &_data);}private:pthread_t _tid;fun_t _cb;ThreadData _data;
};

2.5 ThreadPool.hpp

#pragma once#include <iostream>
using namespace std;#include <vector>
#include <queue>
#include <mutex>
#include <unistd.h>
#include "Thread.hpp"// const static int default_num = 5;// template <class T>
// class ThreadPool
// {
// private:
//     void Lock()
//     {
//         pthread_mutex_lock(&_mtx);
//     }
//     void UnLock()
//     {
//         pthread_mutex_unlock(&_mtx);
//     }
//     void WakeUp()
//     {
//         pthread_cond_signal(&_cond);
//     }
//     void ThreadSleep()
//     {
//         pthread_cond_wait(&_cond, &_mtx);
//     }
//     bool IsQueueEmpty()
//     {
//         return _q.empty();
//     }// private:
//     ThreadPool(const int num = default_num)
//         : _num(num)
//     {
//         pthread_mutex_init(&_mtx, nullptr);
//         pthread_cond_init(&_cond, nullptr);
//     }// public:
//     static ThreadPool<T>* GetInstance()
//     {
//         if(nullptr==_Inst)
//         {
//             pthread_mutex_lock(&_Inst_mtx);
//             if(nullptr==_Inst)
//             {
//                 _Inst=new ThreadPool<T>();
//             }
//             pthread_mutex_unlock(&_Inst_mtx);
//         }
//         return _Inst;
//     }//     ~ThreadPool()
//     {
//         pthread_mutex_destroy(&_mtx);
//         pthread_cond_destroy(&_cond);
//     }//     static void *Routine(void *args)
//     {
//         ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
//         pthread_detach(pthread_self());
//         while (true)
//         {
//             // cout<<"进入routine"<<endl;//             //pop的锁必须在这里加,不能在pop函数中加,因为这里的ThreadSleep函数等待时需要先释放锁
//             //如果锁在pop函数中加的话,这里的ThreadSleep函数将无法释放锁,导致进程卡死
//             tp->Lock();
//             while (tp->IsQueueEmpty())
//             {
//                 tp->ThreadSleep();
//             }//             usleep(1000);
//             T t=tp->Pop();
//             tp->UnLock();//             //cout << "从任务队列中消费了一个数据:" << t << endl;
//             t();
//         }//         return nullptr;
//     }//     void Run()
//     {
//         for (int i = 0; i < _num; i++)
//         {
//             pthread_t tid;
//             // cout<<"Run"<<endl;
//             pthread_create(&tid, nullptr, Routine, this);
//             _threads.push_back(tid);
//         }
//     }//     void Push(const T &in)
//     {
//         Lock();
//         _q.push(in);
//         WakeUp();
//         UnLock();
//     }//     T Pop()
//     {
//         T out=_q.front();
//         _q.pop();
//         return out;
//     }// private:
//     vector<pthread_t> _threads;
//     queue<T> _q;
//     int _num;
//     pthread_mutex_t _mtx;
//     pthread_cond_t _cond;
//     static ThreadPool<T>* _Inst;
//     static pthread_mutex_t _Inst_mtx;
// };// template<class T>
// ThreadPool<T>* ThreadPool<T>::_Inst=nullptr;// template<class T>
// pthread_mutex_t ThreadPool<T>::_Inst_mtx=PTHREAD_MUTEX_INITIALIZER;const static int default_num = 5;template <class T>
class ThreadPool
{
private:void Lock(){pthread_mutex_lock(&_mtx);}void UnLock(){pthread_mutex_unlock(&_mtx);}void WakeUp(){pthread_cond_signal(&_cond);}void ThreadSleep(){pthread_cond_wait(&_cond, &_mtx);}bool IsQueueEmpty(){return _q.empty();}private:ThreadPool(const int num = default_num): _num(num){//构造函数中必须把所有的线程对象先创建好,否则程序会崩溃for(int i=0;i<_num;i++){Thread thread(i+1,Routine,this);_threads.push_back(thread);}pthread_mutex_init(&_mtx, nullptr);pthread_cond_init(&_cond, nullptr);}public:static ThreadPool<T>* GetThreadPool(){if(nullptr==_Inst){pthread_mutex_lock(&_Inst_mtx);if(nullptr==_Inst){_Inst=new ThreadPool<T>();}pthread_mutex_unlock(&_Inst_mtx);}return _Inst;}~ThreadPool(){pthread_mutex_destroy(&_mtx);pthread_cond_destroy(&_cond);}static void *Routine(void *args){//ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);ThreadData* ptd=static_cast<ThreadData*>(args);ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(ptd->_args);pthread_detach(pthread_self());while (true){//cout<<"进入routine"<<endl;//pop的锁必须在这里加,不能在pop函数中加,因为这里的ThreadSleep函数等待时需要先释放锁//如果锁在pop函数中加的话,这里的ThreadSleep函数将无法释放锁,导致进程卡死tp->Lock();while (tp->IsQueueEmpty()){tp->ThreadSleep();}//cout<<"进入routine"<<endl;usleep(1000);T t=tp->Pop();tp->UnLock();//cout << "从任务队列中消费了一个数据:" << t << endl;//cout<<ptd->_name<<":";t(ptd->_name);}return nullptr;}void Run(){for (int i = 0; i < _num; i++){//pthread_t tid;// cout<<"Run"<<endl;//pthread_create(&tid, nullptr, Routine, this);_threads[i].Start();}}void Push(const T &in){Lock();_q.push(in);WakeUp();UnLock();}T Pop(){T out=_q.front();_q.pop();return out;}private:vector<Thread> _threads;queue<T> _q;int _num;pthread_mutex_t _mtx;pthread_cond_t _cond;static ThreadPool<T>* _Inst;static pthread_mutex_t _Inst_mtx;
};template<class T>
ThreadPool<T>* ThreadPool<T>::_Inst=nullptr;template<class T>
pthread_mutex_t ThreadPool<T>::_Inst_mtx=PTHREAD_MUTEX_INITIALIZER;

2.6 makefile

.PHONY:all
all:Client ServerClient:TcpClient.ccg++ -o $@ $^ -std=c++11 -lpthreadServer:Main.ccg++ -o $@ $^ -std=c++11 -lpthread.PHONY:clean
clean:rm -f Client Server

2.7 Main.cc

#include "TcpServer.hpp"
#include <memory>string func(string s)
{return s + " already handled\n";
}void Usage(string argv)
{cout << "\n\t"<< "Usage:" << argv << " ServerPort" << endl<< endl;
}
int main(int argc, char* argv[]) 
{srand((unsigned int)time(nullptr));if (argc != 2){Usage(argv[0]);exit(1);}string str = argv[1];uint16_t ServerPort = (uint16_t)stoi(str.c_str());unique_ptr<TcpServer> svr(new TcpServer(func,ServerPort));//unique_ptrsvr->Init();svr->Run();return 0;
}

2.8 log.hpp

#pragma once#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string>
#include <time.h>
#include <stdarg.h>// 日志等级
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define OneFile 2
//向多个文件打印
#define Classfile 3
#define SIZE 1024#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int mothod){printMethod = mothod;}string LevelToString(int level){switch (level){case Info:{return "Info";}case Debug:{return "Debug";}case Warning:{return "Warning";}case Error:{return "Error";}case Fatal:{return "Fatal";}default:{return "None";}}}void printlog(int level,const string& logtxt){switch(printMethod){case Screen:{cout<<logtxt<<endl;break;}case OneFile:{PrintOneFile(LogFile,logtxt);break;}case Classfile:{PrintClassfile(level,logtxt);break;}default:{break;}}}void PrintOneFile(const string& logname,const string& logtxt){string _logname=path+logname;int fd=open(_logname.c_str(),O_WRONLY|O_CREAT|O_APPEND,0666);if(fd<0){perror("open fail");return;}write(fd,logtxt.c_str(),logtxt.size());close(fd);}void PrintClassfile(int level,const string& logtxt){string filename=LogFile;filename+='.';filename+=LevelToString(level);PrintOneFile(filename,logtxt);}void operator()(int level,const char* format,...){time_t t=time(nullptr);struct tm* ctime=localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer,SIZE,"[%s][%d-%d-%d %d:%d:%d]",LevelToString(level).c_str(),ctime->tm_year+1900,ctime->tm_mon+1,ctime->tm_mday,ctime->tm_hour,ctime->tm_min,ctime->tm_sec);va_list s;va_start(s,format);char rightbuffer[SIZE]={0};vsnprintf(rightbuffer,SIZE,format,s);va_end(s);char logtxt[SIZE*2];snprintf(logtxt,sizeof(logtxt),"%s %s",leftbuffer,rightbuffer);printlog(level,logtxt);}~Log(){}private:// 打印方法int printMethod;string path;
};

2.9 Init.hpp

#pragma once#include <iostream>
using namespace std;#include <unordered_map>
#include <fstream>
#include "log.hpp"const string dictname = "./dict.txt";
const string sep = ":";extern Log log;//把字符串分成key和val
static bool Split(const string &line, string &part1, string &part2)
{size_t pos = line.find(sep);if (pos == string::npos){return false;}part1 = line.substr(0, pos);part2 = line.substr(pos + 2);return true;
}class Init
{
public:Init(){//创建对象就会自动打开对应的文件ifstream in(dictname);if (!in.is_open()){log(Fatal, "open the dict failed...");exit(1);}log(Info, "open the dict successed...");string line;while (getline(in, line)){string part1, part2;if (Split(line, part1, part2)){//把所有的英文单词及其中文都映射到哈希表中_dict[part1] = part2;}else{log(Warning, "format error: %s", line.c_str());}}log(Info,"load dict success...");//关闭文件in.close();}string translation(const string& key){auto it=_dict.find(key);if(it!=_dict.end()){return it->second;}else{return "Unknown this word";}}private:unordered_map<string, string> _dict;
};

2.10 dict.txt

word: 单词
sentence: 句子
paragraph: 段落
book: 书
chapter: 章节
page: 页
article: 文章
dictionary: 字典
thesaurus: 同义词词典
encyclopedia: 百科全书
novel: 小说
short story: 短篇小说
poem: 诗
play: 剧本
film: 电影
song: 歌曲
painting: 画作
sculpture: 雕塑
computer: 电脑
phone: 手机
TV: 电视
radio: 收音机
car: 汽车
bus: 公交车
train: 火车
plane: 飞机
ship: 船
sun: 太阳
moon: 月亮
planet: 行星
star: 星星
sky: 天空
cloud: 云彩
rainbow: 彩虹
snow: 雪
wind: 风
fire: 火
water: 水
ice: 冰
mountain: 山峰
hill: 小山丘
forest: 森林
tree: 树木
flower: 花儿
grass: 草地
bird: 鸟类
cat: 猫科动物
dog: 狗科动物
fish: 鱼类动物
human being: 人类
happiness: 幸福
sadness: 悲伤
love: 爱意
fear: 恐惧感
joy: 喜悦感
anger: 愤怒感
surprise: 惊讶感
disgust: 厌恶感
guilt: 内疚感
shame: 自卑感
pride: 自尊感
hope: 希望感
disappointment: 失望感
excitement: 兴奋感
boredom: 厌倦感
trust: 信任感
surprise: 惊讶感
gratitude: 感激感
friendship: 友谊感
family love: 亲情爱意
romantic love: 浪漫爱情
school: 学校教育场所
workplace: 工作场所地
market place: 市场场所地

2.11 Daemon.hpp

#pragma once#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>//这是系统提供的一个类似于垃圾桶的文件,当我们向这
//个文件写入的时候,该文件就会把内容自动地丢弃
const std::string nullfile="/dev/null";//守护进程化
void Daemon(const std::string& cmd="")
{//1、忽略其它异常信号signal(SIGPIPE,SIG_IGN);signal(SIGCHLD,SIG_IGN);signal(SIGSTOP,SIG_IGN);//2、先创建子进程,子进程再将自己变成独立的会话if(fork()>0){exit(0);}setsid();//将自己变成独立的会话//3、更改当前调用进程的工作目录if(!cmd.empty()){chdir(cmd.c_str());}//4、标准输入,标准输出,标准错误重定向到 /dev/nullint fd=open(nullfile.c_str(),O_RDWR);if(fd>0){dup2(fd,0);dup2(fd,1);dup2(fd,2);}}

以上就是简单的用于中英文翻译的TCP服务器的代码实现,你学会了吗?如果感觉到有所收获,那就点点赞,点点关注呗,后期还会持续更新网络编程的相关知识哦,我们下期见!!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/252901.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3项目中的404页面

vue3项目中的404页面 春节前的最后一篇技术博客了 写了不少vue项目&#xff0c;发现一直没有正确处理404页面。404页面的出现有这么几种可能&#xff1a; 错误输入了页面地址路由连接跳转时&#xff0c;某些路由已经不存在了&#xff0c;而程序员并没有正确处理 也就是说40…

PDF文件格式(一):交叉引用流

在PDF-1.5版本之前&#xff0c;对象的交叉引用信息是存储在交叉引用表(cross-reference table)中的。在PDF-1.5版本之后&#xff0c;引进了交叉引用流(cross-reference stream)对象&#xff0c;可以用它来存储对象的交叉引用信息&#xff0c;就像交叉引用表的功能一样。 采用交…

小白代码审计入门

最近小白一直在学习代码审计,对于我这个没有代码审计的菜鸟来说确实是一件无比艰难的事情。但是着恰恰应了一句老话:万事开头难。但是小白我会坚持下去。何况现在已经喜欢上了代码审计,下面呢小白就说一下appcms后台模板Getshell以及读取任意文件,影响的版本是2.0.101版本。…

Pyhton专项进阶——http协议、cookie、session和认证-3

关于cookie的报文首部相关属性熟悉后&#xff0c;下面就是实际应用。 使用cookie实现用户登录验证&#xff08;初步&#xff09;&#xff1a; 思路&#xff08;一&#xff09;&#xff1a;显示登录页面&#xff0c;输入用户和密码&#xff0c;后端验证&#xff0c;如果验证通…

【TCP】四次挥手(终止连接)

前言 TCP&#xff08;传输控制协议&#xff09;是互联网协议&#xff08;IP&#xff09;中的一种重要传输层协议&#xff0c;用于在通信的计算机之间建立可靠的、有序的和错误校验的数据传输。在TCP连接中&#xff0c;数据传输是双向的&#xff0c;因此需要一种机制来开始和结…

Mac上新版InfluxDB使用教程

一、简介 官网&#xff1a;influxdb 二、influxdb安装 建议使用Homebrew在 macOS 上安装 InfluxDB v2&#xff1a; brew install influxdb启动influxdb服务&#xff1a;brew services start influxdb 停止influxdb服务&#xff1a;brew services stop influxdb 查看是否启…

docker 运行jar包 指定配置文件

要在Docker中运行JAR包并指定配置文件&#xff0c;你可以创建一个Dockerfile来定义你的容器环境&#xff0c;并在其中指定如何运行JAR包和配置文件。下面是一个简单的例子&#xff0c;展示了如何在Dockerfile中设置这些配置&#xff1a; 第一步&#xff1a;创建 Dockerfile文件…

《MySQL》超详细笔记

目录 基本知识 主流数据库 数据库基本概念 MySQL启动 数据库基本命令 数据库 启动数据库 显示数据库 创建数据库 删除数据库 使用数据库 查询当前数据库信息 显示数据库中的表 导入数据库脚本 表 查看表的结构 查看创建某个表的SQL语句 数据库的查询命令 查询…

人工智能(pytorch)搭建模型24-SKAttention注意力机制模型的搭建与应用场景

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能(pytorch)搭建模型24-SKAttention注意力机制模型的搭建与应用场景&#xff0c;本文将介绍关于SKAttention注意力机制模型的搭建&#xff0c;SKAttention机制具有灵活性和通用性&#xff0c;可应用于计算机视…

canvas缩放坐标系(scale)

查看专栏目录 canvas实例应用100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

2024最新版鸿蒙HarmonyOS开发工具安装使用指南

2024最新版鸿蒙HarmonyOS开发工具安装使用指南 By JacksonML 0. 什么是鸿蒙Harmony OS&#xff1f; 华为鸿蒙系统&#xff08;HUAWEI Harmony OS&#xff09;&#xff0c;是华为公司在2019年8月9日于东莞举行的华为开发者大会&#xff08;HDC.2019&#xff09;上正式发布的分…

Java20:新特性

一&#xff1a;Lambda表达式&#xff1a; 1. Lambda表达式使用前后对比&#xff1a; 举类一&#xff1a; Testpublic void test(){ Runnable r1 new Runnable() {Overridepublic void run() {System.out.println("我爱北京天安门&#xff01;");} };r1.run();Syst…

基于SSM的协同过滤技术的网上书城(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的协同过滤技术的网上书城&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Sp…

手撕spring bean的加载过程

这里我们采用手撕源码的方式&#xff0c;开始探索spring boot源码中最有意思的部分-bean的生命周期&#xff0c;也可以通过其中的原理理解很多面试以及工作中偶发遇到的问题。 springboot基于约定大于配置的思想对spring进行优化&#xff0c;使得这个框架变得更加轻量化&#…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Radio组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Radio组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Radio组件 单选框&#xff0c;提供相应的用户交互选择项。 子组件 无。 接口 …

第十五篇【传奇开心果系列】Python的OpenCV库技术点案例示例:图像配准

传奇开心果短博文系列 系列短博文目录Python的OpenCV库技术点案例示例系列短博文目录前言一、常见的图像配准任务介绍二、图像配准任务:图像拼接介绍和示例代码三、图像配准任务:图像校正介绍和示例代码四、图像配准任务:图像配准介绍和示例代码五、基于特征点的配准方法介绍…

面试150 颠倒二进制位 位运算分治 逻辑右移

Problem: 190. 颠倒二进制位 文章目录 思路复杂度位运算分治法 思路 &#x1f468;‍&#x1f3eb; 参考题解 >>>&#xff1a;逻辑右移&#xff08;符号位一起移动&#xff0c;高位补零&#xff09; 复杂度 时间复杂度: O ( log ⁡ n ) O(\log{n}) O(logn) 空间…

Nacos1.X源码解读(待完善)

下载源码 1. 克隆git地址到本地 # 下载nacos源码 git clone https://github.com/alibaba/nacos.git 2. 切换分支到1.4.7, maven编译(3.5.1) 3. 找到启动类com.alibaba.nacos.Nacos 4. 启动VM参数设置单机模式, RUN 启动类 -Dnacos.standalonetrue 5. 启动本地服务注册到本…

3、生成式 AI 如何帮助您改进数据可视化图表

生成式 AI 如何帮助您改进数据可视化图表 使用生成式 AI 加速和增强数据可视化。 图像来源:DALLE 3 5 个关键要点: 数据可视化图表的基本结构使用 Python Altair 构建数据可视化图表使用 GitHub Copilot 加快图表生成速度使用 ChatGPT 为您的图表生成相关内容使用 DALL-E 将…

elementPlus实现动态表格单元格合并span-method方法总结

最近在做PC端需求的时候&#xff0c;需要把首列中相邻的同名称单元格合并。 我看了一下elementPlus官网中的table表格&#xff0c;span-method可以实现单元格合并。 我们先看一下官网的例子&#xff1a; 合并行或列 多行或多列共用一个数据时&#xff0c;可以合并行或列。 …