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.hpp
2.2.TcpServer.cc
2.3.TcpClient.cc
2.4.Common.hpp
2.5.InetAddr.hpp
三、V2多进程回显服务器
3.1.TcpServer.hpp
3.2.Common.hpp
四、V3多线程回显服务器
4.1.TcpServer.hpp
五、V4线程池回显服务器
5.1.TcpServer.hpp
六、V5多线程字典服务器
6.1.TcpServer.hpp
6.2.TcpServer.cc
七、V6多线程远程命令服务器
7.1.popen函数
7.2.Tcpserver.hpp
7.3.TcpServer.cc
7.4.Command.hpp
一、接口介绍
1.1.socket函数
作用:创建一个通信的一端
参数1:域
- 本地通信:AF_UNIX
- 网络通信:AF_INET
参数2:套接字类型
- UDP:SOCK_DGRAM(面向数据报)
- TCP:SOCK_STREAM(面向字节流)
参数3:设置为0
返回值:
- 返回成功:文件描述符
- 返回失败:-1
1.2.bind函数
作用:给一个套接字(网络文件)绑定一个名字
参数1:文件描述符
参数2:sockaddr结构体(填充端口号和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_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(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_cast<ThreadData *>(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::function<void()>; 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 // 长服务: 多进程多线程 // 短服务: 线程池 // 将新链接和客户端构建一个新的任务交给线程池 ThreadPool<task_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::function<std::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_cast<ThreadData *>(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_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(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::function<std::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_cast<ThreadData *>(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_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(port, f); // 等价于: // std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(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::set<std::string> _WhiteListCommands; };