linux笔记归纳14:Socket编程TCP Socket编程TCP目录Socket编程TCP一、接口介绍1.1.socket函数1.2.bind函数1.3.listen函数1.4.telnet命令1.5.accept函数1.6.read函数1.7.write函数1.8.connect函数二、V1单进程回显服务器2.1.TcpServer.hpp2.2.TcpServer.cc2.3.TcpClient.cc2.4.Common.hpp2.5.InetAddr.hpp三、V2多进程回显服务器3.1.TcpServer.hpp3.2.Common.hpp四、V3多线程回显服务器4.1.TcpServer.hpp五、V4线程池回显服务器5.1.TcpServer.hpp六、V5多线程字典服务器6.1.TcpServer.hpp6.2.TcpServer.cc七、V6多线程远程命令服务器7.1.popen函数7.2.Tcpserver.hpp7.3.TcpServer.cc7.4.Command.hpp一、接口介绍1.1.socket函数作用创建一个通信的一端参数1域本地通信AF_UNIX网络通信AF_INET参数2套接字类型UDPSOCK_DGRAM面向数据报TCPSOCK_STREAM面向字节流参数3设置为0返回值返回成功文件描述符返回失败-11.2.bind函数作用给一个套接字网络文件绑定一个名字参数1文件描述符参数2sockaddr结构体填充端口号和IP地址参数3结构体大小1.3.listen函数作用监听一个套接字的链接参数1文件描述符参数2全连接队列1.4.telnet命令作用访问TCP协议的目标服务器只要TCP服务器处于listen状态就已经可以被连接TCP是全双工客户端和服务器在一台机器所以会有两条链接1.5.accept函数作用获取一个套接字的链接参数1文件描述符参数2获取客户端的addr结构输出型参数参数3获取客户端的addr结构大小输出型参数返回值获取成功文件描述符获取失败-1链接从内核中直接获取建立连接的过程与accept无关1.6.read函数作用读文件1.7.write函数作用写文件1.8.connect函数作用与服务器进行连接参数1客户端文件描述符参数2目标服务器的addr结构参数3目标服务器的addr结构大小返回值连接成功0连接失败-1二、V1单进程回显服务器2.1.TcpServer.hpp#pragma once #include Common.hpp #include Log.hpp #include InetAddr.hpp using namespace LogModule; const static int defaultsockfd -1; const static int backlog 8; class TcpServer : public NoCopy { public: TcpServer(uint16_t port) : _port(port), _listensockfd(defaultsockfd), _isrunning(false) { } // 服务器初始化 void Init() { // 创建套接字 _listensockfd socket(AF_INET, SOCK_STREAM, 0); if (_listensockfd 0) { LOG(LogLevel::FATAL) socket error; exit(SOCKET_ERR); } LOG(LogLevel::INFO) socket success: _listensockfd; // fd 3 // 绑定IP地址和端口号 InetAddr local(_port); int n bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen()); if (n 0) { LOG(LogLevel::FATAL) bind error; exit(BIND_ERR); } LOG(LogLevel::INFO) bind success: _listensockfd; // fd 3 // 设置监听状态 n listen(_listensockfd, backlog); if (n 0) { LOG(LogLevel::FATAL) listen error; exit(LISTEN_ERR); } LOG(LogLevel::INFO) listen success: _listensockfd; // fd 3 } // 单线程程序 void Service(int sockfd, InetAddr peer) { char buffer[1024]; while (true) { // 读取数据 // n 0: 读取成功 // n 0: 读取失败 // n 0: 客户端关闭链接, 服务器读到文件结尾 ssize_t n read(sockfd, buffer, sizeof(buffer) - 1); if(n 0) { // 设置为C风格的字符串 // n(实际读取) ≤ sizeof(buffer) - 1 buffer[n] 0; LOG(LogLevel::DEBUG) peer.StringAddr() say# buffer; } else if(n 0) { LOG(LogLevel::DEBUG) peer.StringAddr() 退出了...; close(sockfd); break; } else { LOG(LogLevel::DEBUG) peer.StringAddr() 异常...; close(sockfd); break; } // 写回数据 std::string echo_string echo# ; echo_string buffer; write(sockfd, echo_string.c_str(), echo_string.size()); } } void Run() { _isrunning true; while (_isrunning) { // 获取链接 struct sockaddr_in peer; socklen_t len sizeof(sockaddr_in); // 没有链接时, 会发送阻塞 int sockfd accept(_listensockfd, CONV(peer), len); if (sockfd 0) { LOG(LogLevel::WARNING) accept error; continue; } InetAddr addr(peer); LOG(LogLevel::INFO) accept success, peer addr: addr.StringAddr(); // version 0 Service(sockfd, addr); } _isrunning false; } ~TcpServer() { } private: uint16_t _port; // 服务器端口号 int _listensockfd; // 监听文件描述符 bool _isrunning; // 服务器运行状态 };2.2.TcpServer.cc#include TcpServer.hpp void Usage(std::string proc) { std::cerr Usage: proc port std::endl; } int main(int argc, char *argv[]) { if (argc ! 2) { Usage(argv[0]); exit(USAGE_ERR); } uint16_t port std::stoi(argv[1]); Enable_Console_Log_Strategy(); std::unique_ptrTcpServer tsvr std::make_uniqueTcpServer(port); tsvr-Init(); tsvr-Run(); return 0; }2.3.TcpClient.cc#include iostream #include Common.hpp #include InetAddr.hpp void Usage(std::string proc) { std::cerr Usage: proc server_ip server_port std::endl; } int main(int argc, char* argv[]) { if(argc ! 3) { Usage(argv[0]); exit(USAGE_ERR); } std::string serverip argv[1]; uint16_t serverport std::stoi(argv[2]); // 创建套接字 int sockfd socket(AF_INET, SOCK_STREAM, 0); if(sockfd 0) { std::cerr socket error std::endl; exit(SOCKET_ERR); } // 非显示绑定 // 向目标服务器发起建立链接的请求 InetAddr serveraddr(serverip, serverport); int n connect(sockfd, serveraddr.NetAddrPtr(), serveraddr.NetAddrLen()); if(n 0) { std::cerr connect error std::endl; exit(CONNECT_ERR); } while(true) { // 写入数据 std::string line; std::cout Please Enter ; std::getline(std::cin, line); write(sockfd, line.c_str(), line.size()); // 读取数据 char buffer[1024]; ssize_t size read(sockfd, buffer, sizeof(buffer) - 1); if(size 0) { buffer[size] 0; std::cout server echo# buffer std::endl; } } close(sockfd); return 0; }2.4.Common.hpp#pragma once #include iostream #include unistd.h #include string #include cstring #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h enum ExitCode { OK 0, USAGE_ERR, SOCKET_ERR, BIND_ERR, LISTEN_ERR, CONNECT_ERR, }; // 禁止拷贝 class NoCopy { public: NoCopy() { } ~NoCopy() { } NoCopy(const NoCopy ) delete; const NoCopy operator(const NoCopy ) delete; }; #define CONV(addr) ((struct sockaddr *)addr)2.5.InetAddr.hpp#pragma once #include iostream #include string #include cstring #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include Common.hpp // 网络地址 主机地址 class InetAddr { public: InetAddr(struct sockaddr_in addr) : _addr(addr) { // 网络序列 → 主机序列端口号 _port ntohs(_addr.sin_port); // 网络序列 → 点分十进制IP地址 char ipbuffer[64]; inet_ntop(AF_INET, _addr.sin_addr, ipbuffer, sizeof(_addr)); _ip ipbuffer; } InetAddr(const std::string ip, uint16_t port) : _ip(ip), _port(port) { memset(_addr, 0, sizeof(_addr)); _addr.sin_family AF_INET; // 主机序列 → 网络序列端口号 _addr.sin_port htons(_port); // 点分十进制 → 网络序列IP地址 inet_pton(AF_INET, _ip.c_str(), _addr.sin_addr); } InetAddr(uint16_t port) : _port(port), _ip(0) { memset(_addr, 0, sizeof(_addr)); _addr.sin_family AF_INET; _addr.sin_port htons(_port); _addr.sin_addr.s_addr INADDR_ANY; } uint16_t Port() { return _port; } std::string Ip() { return _ip; } const struct sockaddr_in NetAddr() { return _addr; } const struct sockaddr *NetAddrPtr() { return CONV(_addr); } socklen_t NetAddrLen() { return sizeof(_addr); } bool operator(const InetAddr addr) { return addr._ip _ip addr._port _port; } std::string StringAddr() { return _ip : std::to_string(_port); } ~InetAddr() { } private: struct sockaddr_in _addr; std::string _ip; uint16_t _port; };三、V2多进程回显服务器3.1.TcpServer.hpp#pragma once #include sys/wait.h #include signal.h #include Common.hpp #include Log.hpp #include InetAddr.hpp using namespace LogModule; const static int defaultsockfd -1; const static int backlog 8; class TcpServer : public NoCopy { public: TcpServer(uint16_t port) : _port(port), _listensockfd(defaultsockfd), _isrunning(false) { } // 服务器初始化 void Init() { // 忽略SIG_IGN信号 // OS自动回收子进程, 但无法获取子进程信息 // 推荐写法: signal(SIGCHLD, SIG_IGN); // 创建套接字 _listensockfd socket(AF_INET, SOCK_STREAM, 0); if (_listensockfd 0) { LOG(LogLevel::FATAL) socket error; exit(SOCKET_ERR); } LOG(LogLevel::INFO) socket success: _listensockfd; // fd 3 // 绑定IP地址和端口号 InetAddr local(_port); int n bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen()); if (n 0) { LOG(LogLevel::FATAL) bind error; exit(BIND_ERR); } LOG(LogLevel::INFO) bind success: _listensockfd; // fd 3 // 设置监听状态 n listen(_listensockfd, backlog); if (n 0) { LOG(LogLevel::FATAL) listen error; exit(LISTEN_ERR); } LOG(LogLevel::INFO) listen success: _listensockfd; // fd 3 } void Service(int sockfd, InetAddr peer) { char buffer[1024]; while (true) { // 读取数据 // n 0: 读取成功 // n 0: 读取失败 // n 0: 客户端关闭链接, 服务器读到文件结尾 ssize_t n read(sockfd, buffer, sizeof(buffer) - 1); if (n 0) { // 设置为C风格的字符串 // n(实际读取) ≤ sizeof(buffer) - 1 buffer[n] 0; LOG(LogLevel::DEBUG) peer.StringAddr() say# buffer; } else if (n 0) { LOG(LogLevel::DEBUG) peer.StringAddr() 退出了...; close(sockfd); break; } else { LOG(LogLevel::DEBUG) peer.StringAddr() 异常...; close(sockfd); break; } // 写回数据 std::string echo_string echo# ; echo_string buffer; write(sockfd, echo_string.c_str(), echo_string.size()); } } void Run() { _isrunning true; while (_isrunning) { // 获取链接 struct sockaddr_in peer; socklen_t len sizeof(sockaddr_in); // 没有链接时, 会发送阻塞 int sockfd accept(_listensockfd, CONV(peer), len); if (sockfd 0) { LOG(LogLevel::WARNING) accept error; continue; } InetAddr addr(peer); LOG(LogLevel::INFO) accept success, peer addr: addr.StringAddr(); // version 1 pid_t id fork(); if (id 0) { LOG(LogLevel::FATAL) fork error; exit(FORK_ERR); } else if (id 0) { // 子进程 close(_listensockfd); // 子进程不能访问listensockfd if(fork() 0) // 子进程fork { // 子进程退出 exit(OK); } // 孙子进程孤儿进程: 被1号进程领养, 由系统回收 Service(sockfd, addr); exit(OK); } else { // 父进程 close(sockfd); // 父进程不需要sockfd pid_t rid waitpid(id, nullptr, 0); // 父进程不会阻塞, 立即回收 (void)rid; } } _isrunning false; } ~TcpServer() { } private: uint16_t _port; // 服务器端口号 int _listensockfd; // 监听文件描述符 bool _isrunning; // 服务器运行状态 };3.2.Common.hpp#pragma once #include iostream #include unistd.h #include string #include cstring #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h enum ExitCode { OK 0, USAGE_ERR, SOCKET_ERR, BIND_ERR, LISTEN_ERR, CONNECT_ERR, FORK_ERR, }; // 禁止拷贝 class NoCopy { public: NoCopy() { } ~NoCopy() { } NoCopy(const NoCopy ) delete; const NoCopy operator(const NoCopy ) delete; }; #define CONV(addr) ((struct sockaddr *)addr)四、V3多线程回显服务器4.1.TcpServer.hpp#pragma once #include sys/wait.h #include signal.h #include Common.hpp #include Log.hpp #include InetAddr.hpp #include pthread.h using namespace LogModule; const static int defaultsockfd -1; const static int backlog 8; class TcpServer : public NoCopy { public: TcpServer(uint16_t port) : _port(port), _listensockfd(defaultsockfd), _isrunning(false) { } // 服务器初始化 void Init() { // 创建套接字 _listensockfd socket(AF_INET, SOCK_STREAM, 0); if (_listensockfd 0) { LOG(LogLevel::FATAL) socket error; exit(SOCKET_ERR); } LOG(LogLevel::INFO) socket success: _listensockfd; // fd 3 // 绑定IP地址和端口号 InetAddr local(_port); int n bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen()); if (n 0) { LOG(LogLevel::FATAL) bind error; exit(BIND_ERR); } LOG(LogLevel::INFO) bind success: _listensockfd; // fd 3 // 设置监听状态 n listen(_listensockfd, backlog); if (n 0) { LOG(LogLevel::FATAL) listen error; exit(LISTEN_ERR); } LOG(LogLevel::INFO) listen success: _listensockfd; // fd 3 } void Service(int sockfd, InetAddr peer) { char buffer[1024]; while (true) { // 读取数据 // n 0: 读取成功 // n 0: 读取失败 // n 0: 客户端关闭链接, 服务器读到文件结尾 ssize_t n read(sockfd, buffer, sizeof(buffer) - 1); if (n 0) { // 设置为C风格的字符串 // n(实际读取) ≤ sizeof(buffer) - 1 buffer[n] 0; LOG(LogLevel::DEBUG) peer.StringAddr() say# buffer; } else if (n 0) { LOG(LogLevel::DEBUG) peer.StringAddr() 退出了...; close(sockfd); break; } else { LOG(LogLevel::DEBUG) peer.StringAddr() 异常...; close(sockfd); break; } // 写回数据 std::string echo_string echo# ; echo_string buffer; write(sockfd, echo_string.c_str(), echo_string.size()); } } class ThreadData { public: ThreadData(int fd, InetAddr ar, TcpServer *s) : sockfd(fd), addr(ar), tsvr(s) { } public: int sockfd; InetAddr addr; TcpServer *tsvr; }; static void *Routine(void *args) { pthread_detach(pthread_self()); ThreadData *td static_castThreadData *(args); td-tsvr-Service(td-sockfd, td-addr); delete td; return nullptr; } void Run() { _isrunning true; while (_isrunning) { // 获取链接 struct sockaddr_in peer; socklen_t len sizeof(sockaddr_in); // 没有链接时, 会发生阻塞 int sockfd accept(_listensockfd, CONV(peer), len); if (sockfd 0) { LOG(LogLevel::WARNING) accept error; continue; } InetAddr addr(peer); LOG(LogLevel::INFO) accept success, peer addr: addr.StringAddr(); // version 2 ThreadData *td new ThreadData(sockfd, addr, this); pthread_t tid; pthread_create(tid, nullptr, Routine, td); } _isrunning false; } ~TcpServer() { } private: uint16_t _port; // 服务器端口号 int _listensockfd; // 监听文件描述符 bool _isrunning; // 服务器运行状态 };五、V4线程池回显服务器5.1.TcpServer.hpp#pragma once #include sys/wait.h #include signal.h #include Common.hpp #include Log.hpp #include InetAddr.hpp #include ThreadPool.hpp #include functional using namespace LogModule; using namespace ThreadPoolModule; using task_t std::functionvoid(); const static int defaultsockfd -1; const static int backlog 8; class TcpServer : public NoCopy { public: TcpServer(uint16_t port) : _port(port), _listensockfd(defaultsockfd), _isrunning(false) { } // 服务器初始化 void Init() { // 创建套接字 _listensockfd socket(AF_INET, SOCK_STREAM, 0); if (_listensockfd 0) { LOG(LogLevel::FATAL) socket error; exit(SOCKET_ERR); } LOG(LogLevel::INFO) socket success: _listensockfd; // fd 3 // 绑定IP地址和端口号 InetAddr local(_port); int n bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen()); if (n 0) { LOG(LogLevel::FATAL) bind error; exit(BIND_ERR); } LOG(LogLevel::INFO) bind success: _listensockfd; // fd 3 // 设置监听状态 n listen(_listensockfd, backlog); if (n 0) { LOG(LogLevel::FATAL) listen error; exit(LISTEN_ERR); } LOG(LogLevel::INFO) listen success: _listensockfd; // fd 3 } void Service(int sockfd, InetAddr peer) { char buffer[1024]; while (true) { // 读取数据 // n 0: 读取成功 // n 0: 读取失败 // n 0: 客户端关闭链接, 服务器读到文件结尾 ssize_t n read(sockfd, buffer, sizeof(buffer) - 1); if (n 0) { // 设置为C风格的字符串 // n(实际读取) ≤ sizeof(buffer) - 1 buffer[n] 0; LOG(LogLevel::DEBUG) peer.StringAddr() say# buffer; } else if (n 0) { LOG(LogLevel::DEBUG) peer.StringAddr() 退出了...; close(sockfd); break; } else { LOG(LogLevel::DEBUG) peer.StringAddr() 异常...; close(sockfd); break; } // 写回数据 std::string echo_string echo# ; echo_string buffer; write(sockfd, echo_string.c_str(), echo_string.size()); } } void Run() { _isrunning true; while (_isrunning) { // 获取链接 struct sockaddr_in peer; socklen_t len sizeof(sockaddr_in); // 没有链接时, 会发生阻塞 int sockfd accept(_listensockfd, CONV(peer), len); if (sockfd 0) { LOG(LogLevel::WARNING) accept error; continue; } InetAddr addr(peer); LOG(LogLevel::INFO) accept success, peer addr: addr.StringAddr(); // version 3 // 长服务: 多进程多线程 // 短服务: 线程池 // 将新链接和客户端构建一个新的任务交给线程池 ThreadPooltask_t::GetInstance()-Enqueue( [this, sockfd, addr]() { this-Service(sockfd, addr); }); } _isrunning false; } ~TcpServer() { } private: uint16_t _port; // 服务器端口号 int _listensockfd; // 监听文件描述符 bool _isrunning; // 服务器运行状态 };六、V5多线程字典服务器6.1.TcpServer.hpp#pragma once #include Common.hpp #include Log.hpp #include InetAddr.hpp #include pthread.h #include functional using namespace LogModule; using func_t std::functionstd::string(const std::string , InetAddr ); const static int defaultsockfd -1; const static int backlog 8; class TcpServer : public NoCopy { public: TcpServer(uint16_t port, func_t func) : _port(port), _listensockfd(defaultsockfd), _isrunning(false), _func(func) { } // 服务器初始化 void Init() { // 创建套接字 _listensockfd socket(AF_INET, SOCK_STREAM, 0); if (_listensockfd 0) { LOG(LogLevel::FATAL) socket error; exit(SOCKET_ERR); } LOG(LogLevel::INFO) socket success: _listensockfd; // fd 3 // 绑定IP地址和端口号 InetAddr local(_port); int n bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen()); if (n 0) { LOG(LogLevel::FATAL) bind error; exit(BIND_ERR); } LOG(LogLevel::INFO) bind success: _listensockfd; // fd 3 // 设置监听状态 n listen(_listensockfd, backlog); if (n 0) { LOG(LogLevel::FATAL) listen error; exit(LISTEN_ERR); } LOG(LogLevel::INFO) listen success: _listensockfd; // fd 3 } void Service(int sockfd, InetAddr peer) { char buffer[1024]; while (true) { // 读取数据 // n 0: 读取成功 // n 0: 读取失败 // n 0: 客户端关闭链接, 服务器读到文件结尾 ssize_t n read(sockfd, buffer, sizeof(buffer) - 1); if (n 0) { // 设置为C风格的字符串 // n(实际读取) ≤ sizeof(buffer) - 1 buffer[n] 0; LOG(LogLevel::DEBUG) peer.StringAddr() # buffer; std::string echo_string _func(buffer, peer); write(sockfd, echo_string.c_str(), echo_string.size()); } else if (n 0) { LOG(LogLevel::DEBUG) peer.StringAddr() 退出了...; close(sockfd); break; } else { LOG(LogLevel::DEBUG) peer.StringAddr() 异常...; close(sockfd); break; } } } class ThreadData { public: ThreadData(int fd, InetAddr ar, TcpServer *s) : sockfd(fd), addr(ar), tsvr(s) { } public: int sockfd; InetAddr addr; TcpServer *tsvr; }; static void *Routine(void *args) { pthread_detach(pthread_self()); ThreadData *td static_castThreadData *(args); td-tsvr-Service(td-sockfd, td-addr); delete td; return nullptr; } void Run() { _isrunning true; while (_isrunning) { // 获取链接 struct sockaddr_in peer; socklen_t len sizeof(sockaddr_in); // 没有链接时, 会发生阻塞 int sockfd accept(_listensockfd, CONV(peer), len); if (sockfd 0) { LOG(LogLevel::WARNING) accept error; continue; } InetAddr addr(peer); LOG(LogLevel::INFO) accept success, peer addr: addr.StringAddr(); ThreadData *td new ThreadData(sockfd, addr, this); pthread_t tid; pthread_create(tid, nullptr, Routine, td); } _isrunning false; } ~TcpServer() { } private: uint16_t _port; // 服务器端口号 int _listensockfd; // 监听文件描述符 bool _isrunning; // 服务器运行状态 func_t _func; // 服务器回调处理 };6.2.TcpServer.cc#include TcpServer.hpp #include Dict.hpp std::string defaulthandler(const std::string word, InetAddr addr) { LOG(LogLevel::DEBUG) 发生回调; std::string s haha, ; s word; return s; } void Usage(std::string proc) { std::cerr Usage: proc port std::endl; } int main(int argc, char *argv[]) { if (argc ! 2) { Usage(argv[0]); exit(USAGE_ERR); } uint16_t port std::stoi(argv[1]); Enable_Console_Log_Strategy(); // 翻译模块 Dict d; d.LoadDict(); std::unique_ptrTcpServer tsvr std::make_uniqueTcpServer(port, [d](const std::string word, InetAddr addr) { return d.Translate(word, addr); }); tsvr-Init(); tsvr-Run(); return 0; }七、V6多线程远程命令服务器7.1.popen函数作用创建管道和子进程在子进程中执行命令将子进程的标准输出重定向到管道写端返回值父进程接收管道的读端7.2.Tcpserver.hpp#pragma once #include Common.hpp #include Log.hpp #include InetAddr.hpp #include pthread.h #include functional using namespace LogModule; using func_t std::functionstd::string(const std::string , InetAddr ); const static int defaultsockfd -1; const static int backlog 8; class TcpServer : public NoCopy { public: TcpServer(uint16_t port, func_t func) : _port(port), _listensockfd(defaultsockfd), _isrunning(false), _func(func) { } // 服务器初始化 void Init() { // 创建套接字 _listensockfd socket(AF_INET, SOCK_STREAM, 0); if (_listensockfd 0) { LOG(LogLevel::FATAL) socket error; exit(SOCKET_ERR); } LOG(LogLevel::INFO) socket success: _listensockfd; // fd 3 // 绑定IP地址和端口号 InetAddr local(_port); int n bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen()); if (n 0) { LOG(LogLevel::FATAL) bind error; exit(BIND_ERR); } LOG(LogLevel::INFO) bind success: _listensockfd; // fd 3 // 设置监听状态 n listen(_listensockfd, backlog); if (n 0) { LOG(LogLevel::FATAL) listen error; exit(LISTEN_ERR); } LOG(LogLevel::INFO) listen success: _listensockfd; // fd 3 } void Service(int sockfd, InetAddr peer) { char buffer[1024]; while (true) { // 读取数据 // n 0: 读取成功 // n 0: 读取失败 // n 0: 客户端关闭链接, 服务器读到文件结尾 ssize_t n read(sockfd, buffer, sizeof(buffer) - 1); if (n 0) { // 设置为C风格的字符串 // n(实际读取) ≤ sizeof(buffer) - 1 buffer[n] 0; LOG(LogLevel::DEBUG) peer.StringAddr() # buffer; std::string echo_string _func(buffer, peer); write(sockfd, echo_string.c_str(), echo_string.size()); } else if (n 0) { LOG(LogLevel::DEBUG) peer.StringAddr() 退出了...; close(sockfd); break; } else { LOG(LogLevel::DEBUG) peer.StringAddr() 异常...; close(sockfd); break; } } } class ThreadData { public: ThreadData(int fd, InetAddr ar, TcpServer *s) : sockfd(fd), addr(ar), tsvr(s) { } public: int sockfd; InetAddr addr; TcpServer *tsvr; }; static void *Routine(void *args) { pthread_detach(pthread_self()); ThreadData *td static_castThreadData *(args); td-tsvr-Service(td-sockfd, td-addr); delete td; return nullptr; } void Run() { _isrunning true; while (_isrunning) { // 获取链接 struct sockaddr_in peer; socklen_t len sizeof(sockaddr_in); // 没有链接时, 会发生阻塞 int sockfd accept(_listensockfd, CONV(peer), len); if (sockfd 0) { LOG(LogLevel::WARNING) accept error; continue; } InetAddr addr(peer); LOG(LogLevel::INFO) accept success, peer addr: addr.StringAddr(); ThreadData *td new ThreadData(sockfd, addr, this); pthread_t tid; pthread_create(tid, nullptr, Routine, td); } _isrunning false; } ~TcpServer() { } private: uint16_t _port; // 服务器端口号 int _listensockfd; // 监听文件描述符 bool _isrunning; // 服务器运行状态 func_t _func; // 服务器回调处理 };7.3.TcpServer.cc#include TcpServer.hpp #include Command.hpp std::string defaulthandler(const std::string word, InetAddr addr) { LOG(LogLevel::DEBUG) 发生回调; std::string s haha, ; s word; return s; } void Usage(std::string proc) { std::cerr Usage: proc port std::endl; } int main(int argc, char *argv[]) { if (argc ! 2) { Usage(argv[0]); exit(USAGE_ERR); } uint16_t port std::stoi(argv[1]); Enable_Console_Log_Strategy(); // 命令执行 Command cmd; func_t f std::bind(Command::Execute, cmd, std::placeholders::_1, std::placeholders::_2); std::unique_ptrTcpServer tsvr std::make_uniqueTcpServer(port, f); // 等价于: // std::unique_ptrTcpServer tsvr std::make_uniqueTcpServer(port, // [cmd](const std::string cpmmand, InetAddr addr) // { // return cmd.Execute(command, addr); // }); tsvr-Init(); tsvr-Run(); return 0; }7.4.Command.hpp#pragma once #include iostream #include string #include set #include Common.hpp #include InetAddr.hpp #include Log.hpp using namespace LogModule; class Command { public: Command() { // 严格匹配 _WhiteListCommands.insert(ls); _WhiteListCommands.insert(pwd); _WhiteListCommands.insert(ls -l); _WhiteListCommands.insert(touch haha.txt); _WhiteListCommands.insert(who); _WhiteListCommands.insert(whoami); } bool IsSafeCommand(const std::string cmd) { auto iter _WhiteListCommands.find(cmd); return iter ! _WhiteListCommands.end(); } std::string Execute(const std::string cmd, InetAddr addr) { // 检查命令 if (!IsSafeCommand(cmd)) { return std::string(你要执行的命令不安全); } std::string who addr.StringAddr(); // 执行命令 FILE *fp popen(cmd.c_str(), r); if (nullptr fp) { return std::string(你要执行的命令不存在: ) cmd; } std::string res; char line[1024]; while (fgets(line, sizeof(line), fp)) { res line; } pclose(fp); std::string result who execute done, result is: \n res; LOG(LogLevel::DEBUG) result; return result; } ~Command() { } private: // 受限制的远程执行 std::setstd::string _WhiteListCommands; };