> 技术文档 > 【在Linux世界中追寻伟大的One Piece】Socket编程TCP(续)

【在Linux世界中追寻伟大的One Piece】Socket编程TCP(续)

目录

1 -> V2 -Echo Server多进程版本

2 -> V3 -Echo Server多线程版本

3 -> V3-1 -多线程远程命令执行

4 -> V4 -Echo Server线程池版本


1 -> V2 -Echo Server多进程版本

通过每个请求,创建子进程的方式来支持多连接。

InetAddr.hpp

#pragma once#include #include #include #include #include #include class InetAddr{public:InetAddr(struct sockaddr_in& addr) :_addr(addr){_port = ntohs(_addr.sin_port);_ip = inet_ntoa(_addr.sin_addr);}std::string Ip() { return _ip; }uint16_t Port() { return _port; };std::string PrintDebug(){std::string info = _ip;info += \":\";info += std::to_string(_port); // \"127.0.0.1:4444\"return info;}const struct sockaddr_in& GetAddr(){return _addr;}bool operator == (const InetAddr & addr){//other codereturn this->_ip == addr._ip && this->_port == addr._port;}~InetAddr() {}private:std::string _ip;uint16_t _port;struct sockaddr_in _addr;};

TcpSever.hpp

#pragma once#include #include #include #include #include #include #include #include #include #include #include \"Log.hpp\"#include \"nocopy.hpp\"#include \"Comm.hpp\"#include \"InetAddr.hpp\"const static int default_backlog = 6; // TODOclass TcpServer : public nocopy{public:TcpServer(uint16_t port) : _port(port), _isrunning(false){}// 都是固定套路void Init(){// 1. 创建 socket, file fd, 本质是文件_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock  0){buffer[n] = 0;std::cout << \"client say# \" << buffer <<std::endl;std::string echo_string = \"server echo# \";echo_string += buffer;write(sockfd, echo_string.c_str(),echo_string.size());}else if (n == 0) // read 如果返回值是 0,表示读到了文件结尾(对端关闭了连接!){lg.LogMessage(Info, \"client quit...\\n\");break;}else{lg.LogMessage(Error, \"read socket error, errnocode: % d, error string : % s\\n\", errno, strerror(errno));break;}}}void ProcessConnection(int sockfd, struct sockaddr_in& peer){// v2 多进程pid_t id = fork();if (id  0)exit(0);InetAddr addr(peer); // 获取 client 地址信息lg.LogMessage(Info, \"process connection: %s:%d\\n\",addr.Ip().c_str(), addr.Port());// 孙子进程,孤儿进程,被系统领养,正常处理Service(sockfd);close(sockfd);exit(0);}else{close(sockfd);pid_t rid = waitpid(id, nullptr, 0);if (rid == id){// do nothing}}}void Start(){_isrunning = true;while (_isrunning){// 4. 获取连接struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = accept(_listensock, CONV(&peer), &len);if (sockfd < 0){lg.LogMessage(Warning, \"accept socket error, errnocode: % d, error string : % s\\n\", errno, strerror(errno));continue;}lg.LogMessage(Debug, \"accept success, get n newsockfd: % d\\n\", sockfd);ProcessConnection(sockfd, peer);}}~TcpServer(){}private:uint16_t _port;int _listensock; // TODObool _isrunning;};

引入InetAddr.hpp,方便后面打印消息。

2 -> V3 -Echo Server多线程版本

Thread.hpp

#pragma once#include #include #include #include #include #include #include #include #include #include #include #include \"Log.hpp\"#include \"nocopy.hpp\"#include \"Comm.hpp\"#include \"InetAddr.hpp\"const static int default_backlog = 6; // TODOclass TcpServer : public nocopy{public:TcpServer(uint16_t port) : _port(port), _isrunning(false){}// 都是固定套路void Init(){// 1. 创建 socket, file fd, 本质是文件_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock  0){buffer[n] = 0;std::cout << \"client say# \" << buffer <<std::endl;std::string echo_string = \"server echo# \";echo_string += buffer;write(td._sockfd, echo_string.c_str(),echo_string.size());}else if (n == 0) // read 如果返回值是 0,表示读到了文件结尾(对端关闭了连接!){lg.LogMessage(Info, \"client[%s:%d] quit...\\n\",td._addr.Ip().c_str(), td._addr.Port());break;}else{lg.LogMessage(Error, \"read socket error, errnocode: % d, error string : % s\\n\", errno, strerror(errno));break;}}}static void* threadExcute(void* args){pthread_detach(pthread_self());ThreadData* td = static_cast(args);TcpServer::Service(*td);close(td->_sockfd);delete td;return nullptr;}void ProcessConnection(int sockfd, struct sockaddr_in& peer){// v3 多线程InetAddr addr(peer);pthread_t tid;ThreadData* td = new ThreadData(sockfd, peer);pthread_create(&tid, nullptr, threadExcute, (void*)td);}void Start(){_isrunning = true;while (_isrunning){// 4. 获取连接struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = accept(_listensock, CONV(&peer), &len);if (sockfd < 0){lg.LogMessage(Warning, \"accept socket error, errnocode: % d, error string : % s\\n\", errno, strerror(errno));continue;}lg.LogMessage(Debug, \"accept success, get n newsockfd: % d\\n\", sockfd);ProcessConnection(sockfd, peer);}}~TcpServer(){}private:uint16_t _port;int _listensock; // TODObool _isrunning;};

使用最原始的接口,使用内部ThreadData类。

3 -> V3-1 -多线程远程命令执行

Command.hpp

  • 命令类,用来执行命令,并获取结果。
  • 这里暂停,做一个多线程的小业务。
#pragma once#include #include #include #include class Command{private:public:Command() {}Command(int sockfd) : _sockfd(sockfd){// 限定一下命令的个数和范围_safe_command.insert(\"ls\");_safe_command.insert(\"pwd\");_safe_command.insert(\"ls -l\");_safe_command.insert(\"ll\");_safe_command.insert(\"touch\");_safe_command.insert(\"who\");_safe_command.insert(\"whoami\");}bool IsSafe(const std::string& command){auto iter = _safe_command.find(command);if (iter == _safe_command.end()) return false; // 要执行的命令不 set 中,不安全else return true;}std::string Execute(const std::string& command){if (!IsSafe(command)) return \"unsafe\";FILE* fp = popen(command.c_str(), \"r\");if (fp == nullptr)return std::string();char buffer[1024];std::string result;while (fgets(buffer, sizeof(buffer), fp)){result += buffer;}pclose(fp);return result;}std::string RecvCommand(){char line[1024];ssize_t n = recv(_sockfd, line, sizeof(line) - 1, 0); if (n > 0){line[n] = 0;return line;}else{return std::string();}}void SendCommand(std::string result){if (result.empty()) result = \"done\"; // 主要是有些命令没有结果,比如 touchsend(_sockfd, result.c_str(), result.size(), 0);}~Command(){}private:std::set _safe_command;int _sockfd;std::string _command;};

TcpSever.hpp

#pragma once#include #include #include #include #include #include #include #include #include #include #include #include \"Log.hpp\"#include \"nocopy.hpp\"#include \"Comm.hpp\"#include \"InetAddr.hpp\"#include \"Command.hpp\" // 引入命令执行const static int default_backlog = 6; // TODOclass TcpServer : public nocopy{public:TcpServer(uint16_t port) : _port(port), _isrunning(false){}// 都是固定套路void Init(){// 1. 创建 socket, file fd, 本质是文件_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){lg.LogMessage(Fatal, \"create socket error, errnocode: % d, error string : % s\\n\", errno, strerror(errno));exit(Fatal);}int opt = 1;setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR |SO_REUSEPORT, &opt, sizeof(opt));lg.LogMessage(Debug, \"create socket success,sockfd: % d\\n\", _listensock);// 2. 填充本地网络信息并 bindstruct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = htonl(INADDR_ANY);// 2.1 bindif (bind(_listensock, CONV(&local), sizeof(local)) != 0){lg.LogMessage(Fatal, \"bind socket error, errnocode: % d, error string : % s\\n\", errno, strerror(errno));exit(Bind_Err);}lg.LogMessage(Debug, \"bind socket success, sockfd: %d\\n\",_listensock);// 3. 设置 socket 为监听状态,tcp 特有的if (listen(_listensock, default_backlog) != 0){lg.LogMessage(Fatal, \"listen socket error, errnocode: % d, error string : % s\\n\", errno, strerror(errno));exit(Listen_Err);}lg.LogMessage(Debug, \"listen socket success,sockfd: % d\\n\", _listensock);}class ThreadData{public:ThreadData(int sockfd, struct sockaddr_in addr): _sockfd(sockfd), _addr(addr){}~ThreadData(){}public:int _sockfd;InetAddr _addr;};// Tcp 连接全双工通信的.// 新增 staticstatic void Service(ThreadData& td){char buffer[1024];// 一直进行 IOwhile (true){Command command(td._sockfd);std::string commandstr = command.RecvCommand();if (commandstr.empty())return;std::string result = command.Execute(commandstr);command.SendCommand(result);}}static void* threadExcute(void* args){pthread_detach(pthread_self());ThreadData* td = static_cast(args);TcpServer::Service(*td);close(td->_sockfd);delete td;return nullptr;}void ProcessConnection(int sockfd, struct sockaddr_in& peer){// v3 多线程InetAddr addr(peer);pthread_t tid;ThreadData* td = new ThreadData(sockfd, peer);pthread_create(&tid, nullptr, threadExcute, (void*)td);}void Start(){_isrunning = true;while (_isrunning){// 4. 获取连接struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = accept(_listensock, CONV(&peer), &len);if (sockfd < 0){lg.LogMessage(Warning, \"accept socket error, errnocode: % d, error string : % s\\n\", errno, strerror(errno));continue;}lg.LogMessage(Debug, \"accept success, get n newsockfd: % d\\n\", sockfd);ProcessConnection(sockfd, peer);}}~TcpServer(){}private:uint16_t _port;int _listensock; // TODObool _isrunning;};

4 -> V4 -Echo Server线程池版本

引入系统部分的线程池,进行简单的业务处理。

TcpServer.hpp

#pragma once#include #include #include #include #include #include #include #include #include #include #include #include #include \"Log.hpp\"#include \"nocopy.hpp\"#include \"Comm.hpp\"#include \"InetAddr.hpp\"#include \"ThreadPool.hpp\"const static int default_backlog = 6; // TODOclass TcpServer : public nocopy{public:TcpServer(uint16_t port) : _port(port), _isrunning(false){}// 都是固定套路void Init(){// 1. 创建 socket, file fd, 本质是文件_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock  0){buffer[n] = 0;std::cout << \"client say# \" << buffer <<std::endl;std::string echo_string = \"server echo# \";echo_string += buffer;write(sockfd, echo_string.c_str(),echo_string.size());}else if (n == 0) // read 如果返回值是 0,表示读到了文件结尾(对端关闭了连接!){lg.LogMessage(Info, \"client[%s:%d] quit...\\n\",addr.Ip().c_str(), addr.Port());break;}else{lg.LogMessage(Error, \"read socket error, errnocode: % d, error string : % s\\n\", errno, strerror(errno));break;}}}void ProcessConnection(int sockfd, struct sockaddr_in& peer){using func_t = std::function;InetAddr addr(peer);func_t func = std::bind(&TcpServer::Service, this, sockfd,addr); // 这里不能 autoThreadPool::GetInstance()->Push(func);}void Start(){_isrunning = true;while (_isrunning){// 4. 获取连接struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = accept(_listensock, CONV(&peer), &len);if (sockfd < 0){lg.LogMessage(Warning, \"accept socket error, errnocode: % d, error string : % s\\n\", errno, strerror(errno));continue;}lg.LogMessage(Debug, \"accept success, get n newsockfd: % d\\n\", sockfd);ProcessConnection(sockfd, peer);}_isrunning = false;}~TcpServer(){}private:uint16_t _port;int _listensock; // TODObool _isrunning;};

感谢各位大佬支持!!!

互三啦!!!