当前位置: 首页 > news >正文

【在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 <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>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 <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#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){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);}// Tcp 连接全双工通信的.void Service(int sockfd){char buffer[1024];// 一直进行 IOwhile (true){ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 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){close(sockfd);return;}else if (id == 0){// childclose(_listensock);if (fork() > 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 <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>
#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){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){ssize_t n = read(td._sockfd, buffer, sizeof(buffer) -1);if (n > 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<ThreadData*>(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 <iostream>
#include <string>
#include <set>
#include <unistd.h>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<std::string> _safe_command;int _sockfd;std::string _command;
};

TcpSever.hpp

#pragma once#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>
#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<ThreadData*>(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 <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>
#include <functional>
#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){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. 填充本地网络信息并 bind// struct 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);}// Tcp 连接全双工通信的.void Service(int sockfd, InetAddr addr){char buffer[1024];// 一直进行 IOwhile (true){ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 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<void()>;InetAddr addr(peer);func_t func = std::bind(&TcpServer::Service, this, sockfd,addr); // 这里不能 autoThreadPool<func_t>::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;
};

感谢各位大佬支持!!!

互三啦!!!

http://www.lryc.cn/news/475657.html

相关文章:

  • 面试高频问题:C/C++编译时内存五个分区
  • 阅读博士论文《功率IGBT模块健康状态监测方法研究》
  • Spring ApplicationContext接口
  • [perl] 数组与哈希
  • 电机学习-SPWM原理及其MATLAB模型
  • 群控系统服务端开发模式-应用开发-腾讯云上传工厂及七牛云上传工厂开发
  • 【深度学习滑坡制图|论文解读3】基于融合CNN-Transformer网络和深度迁移学习的遥感影像滑坡制图方法
  • 《计算机原理与系统结构》学习系列——处理器(下)
  • JDK新特性(8-21)数据类型-直接内存
  • 003-Kotlin界面开发之声明式编程范式
  • QT pro项目工程的条件编译
  • 深度学习之经典网络-AlexNet详解
  • 部署Prometheus、Grafana、Zipkin、Kiali监控度量Istio
  • 结合 Spring Boot Native 和 Spring Boot 构建高性能服务器架构
  • ArcGIS影像调色(三原色)三原色调整
  • SQLite从入门到精通面试题及参考答案
  • 【C/C++】字符/字符串函数(0)(补充)——由ctype.h提供
  • Git 的特殊配置文件
  • 数据的表现形式(1)
  • 《高频电子线路》—— 调幅
  • ubuntu22.04安装ROS2Humble
  • 软中端,硬中断(学习笔记)
  • scIDST:弱监督学习推断单细胞转录组数据中的疾病进展阶段
  • Linux 下执行定时任务之 Systemd Timers
  • flutter 专题二 Flutter状态管理之Riverpod 0.8.4
  • 【Linux】从零开始使用多路转接IO --- poll
  • Docker配置宿主机目录和网络映射
  • 第十七课 component组件解析
  • 求余和求模是不是一样的,就要看看计算机中的 fix 和 floor 区别
  • 00 递推和递归的核心讲解