news 2026/8/8 7:36:18

linux笔记归纳14:Socket编程TCP

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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.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; };
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/8 7:34:05

高通X105 5G-Advanced模组实战(1): Linux驱动移植与USB枚举的踩坑经验

本文基于实际项目经验&#xff0c;详细记录高通X105&#xff08;SDX75&#xff09;5G-Advanced模组在Linux系统下的驱动移植全流程。不同于X62平台&#xff0c;X105引入了MHI新架构&#xff0c;USB枚举顺序有变化&#xff0c;PID也有更新&#xff0c;笔者在项目中踩了7个坑才彻…

作者头像 李华
网站建设 2026/8/8 7:30:23

华为MateBook 13右侧Type-C接口失灵排查与修复全攻略

1. 问题现象与初步排查最近在工作室处理了好几台华为MateBook 13笔记本&#xff0c;症状出奇地一致&#xff1a;右侧那个Type-C接口突然“罢工”了。具体表现就是&#xff0c;插上U盘、移动硬盘或者扩展坞&#xff0c;设备毫无反应&#xff0c;指示灯不亮&#xff0c;系统也弹不…

作者头像 李华
网站建设 2026/8/8 7:28:51

2026年AI营销赛道观察

随着豆包、DeepSeek、通义千问等生成式AI平台加速渗透用户的日常信息获取与消费决策&#xff0c;品牌营销的流量入口正从传统搜索引擎向"AI问答"场景迁移。行业数据显示&#xff0c;AI营销已从概念验证阶段进入规模化落地期&#xff0c;市场规模、技术范式与服务商格…

作者头像 李华
网站建设 2026/8/8 7:26:13

SpringBoot+Vue高校就业招聘系统开发实践

1. 项目概述&#xff1a;高校就业招聘系统的技术架构与核心价值这个基于SpringBootVue的高校就业招聘管理系统&#xff0c;本质上是一个连接高校、学生与用人单位的B/S架构平台。我在实际开发中发现&#xff0c;这类系统最核心的价值在于解决了传统校园招聘中的三大痛点&#x…

作者头像 李华
网站建设 2026/8/8 7:25:53

Unity游戏翻译革命:XUnity.AutoTranslator深度配置与实战指南

Unity游戏翻译革命&#xff1a;XUnity.AutoTranslator深度配置与实战指南 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 还在为日系RPG的剧情对话感到困惑&#xff1f;或是面对欧美大作的英文界面一筹莫…

作者头像 李华