TCP服务器实现—多进程版,多线程版,线程池版

目录

前言

1.存在的问题

2.多进程版

3.多线程版

4.线程池版

总结


前言

在上一篇文章中使用TCP协议实现了一个简单的服务器,可以用来服务端和客户端通信,但是之前的服务器存在一个问题,就是当有多个客户端连接服务器的时候,服务器只能和一个客户端通信,其它的客户端是无法通信的,这是为什么呢?有该如何解决呢?将在这篇文章中为大家介绍

1.存在的问题

如图所示:

为什么会存在这样的问题呢?

如上图所示,之前我们实现的服务器是单进程版的,所以当第一次和客户端建立连接完成之后,就死循环处理读取信息的逻辑了,所以就无法再和新的客户端建立连接了 。找到问题之后,很明显解决方式就是将建立连接和通信分开执行,此时我们就可以使用多进程和多线程来解决了,下面我们就具体来实现以下如何使用多进程和多线程。

2.多进程版

实现思路:与客户端建立连接完成,fork创建子进程,让父进程继续建立连接,让子进程实现后续的数据通信。

这样实现存在的问题:当父进程创建完子进程之后,需要使用waitpid回收子进程的资源,否则子进程就会变为僵尸进程,导致资源泄漏,但是使用waitpid回收子进程资源,程序变为串行化执行了,就无法实现之前的需求,让父进程负责建立连接,子进程负责数据通信了。

解决方式有两种:

1.fork创建子进程,在子进程内部再fork创建子进程,然后让父进程直接退出,此时之前的子进程作为父进程退出,新创建的子进程就变为孤儿进程被操作系统领养,并不会造成资源泄漏,并且让该子进程负责通信

2.因为子进程退出之后操作系统会发送一个SIGCHLD信号,所以可以使用signal函数捕捉SIGCHLD信号,将默认处理动作设置为SIG_IGN,在这个默认动作里会回收子进程的资源,并不会造成资源泄漏,并且让该子进程负责通信

思路1代码:

cpp 复制代码
pid_t id = fork();
if(id == 0)//child
{
    close(_sock);
    if(fork() > 0)
        exit(0);
    serviceIO(sock);
    close(sock);
    exit(0);
}
close(sock);
waitpid(id,nullptr,0);

思路2代码:

cpp 复制代码
signal(SIGCHLD,SIG_IGN);
if(id == 0)//child
{
    // 子进程会继承父进程的文件描述符表,当子进程不需要时进行关闭,
    // 防止子进程文件描述符资源泄露
    close(_sock);
    serviceIO(sock);
    close(sock);
    exit(0);
}

运行截图:

myl@VM-8-12-centos tcp\]$ ./tcpServer 8080 create socket success bind socket success listen socket success accept a new link success sock: 4 accept a new link success sock: 4 recvice message: 你好,我是客户端1 recvice message: 你好,我是客户端2 此时就实现了一个客户端可以被多个服务端连接并且实现通信。 # 3.多线程版 说明:相比于多进程,多线程的创建和销毁对操作系统是更轻量的,消耗的资源也是更少的,所以实现数据通信可以采用多线程的方式,让主线程负责建立,让从线程负责数据通信 代码实现: ```cpp class TcpServerData { public: TcpServerData(TcpServer* self,int sock) :_self(self),_sock(sock) {} public: TcpServer* _self; int _sock; }; cout << "我是主线程" << endl; pthread_t tid; TcpServerData* tsd = new TcpServerData(this,sock); pthread_create(&tid,nullptr,start_routine,tsd); //因为是类内成员函数,必须包含this指针,但是start_routine作为参数是没有this指针的 //所以start_routine函数必须加上static,静态成员方法是不能访问类内成员的,所以参数传递this //调用serviceIO,但是serviceIO函数需要传递参数sock,所以可以封装一个结构体,在结构体中包含成员 //属性sock和this static void* start_routine(void* args) { //设置与主线程分离,此时主线程不需要等待从线程退出了,而是继续建立连接 cout << "我是从线程" << endl; pthread_detach(pthread_self()); TcpServerData* t = static_cast(args); t->_self->serviceIO(t->_sock); close(t->_sock); delete t; return nullptr; ``` 运行截图: \[myl@VM-8-12-centos tcp\]$ ./tcpServer 8080 create socket success bind socket success listen socket success accept a new link success sock: 4 我是主线程 我是从线程 recvice message: 你好,我是客户端1 accept a new link success sock: 5 我是主线程 我是从线程 recvice message: 你好,我是客户端2 # 4.线程池版 说明:线程池版的实现思路是基于多线程,虽然多线程创建和销毁的消耗比多进程的低,但是为了更进一步提升效率,可以预先创建好一批线程,主线程负责建立连接获取任务,然后将任务加入到队列中,让预先创建好的线程从队列中获取任务,然后处理获取到的任务。 代码实现: ```cpp void start() { //线程池初始化:预先创建好一批线程: ThreadPool::getInstance()->run(); for (;;) { // 建立连接: struct sockaddr_in peer; socklen_t len = sizeof(peer); int sock = accept(_sock, (struct sockaddr *)&peer, &len); if (sock < 0) { logMessage(ERROR, "accept error, next"); continue; } logMessage(NORMAL, "accept a new link success"); std::cout << "sock: " << sock << std::endl; //未来通信全部用sock,面向字节流的,后续全部都是文件操作: ThreadPool::getInstance()->push(Task(sock,serviceIO)); } } ``` 运行截图: \[myl@VM-8-12-centos tcp\]$ ./tcpServer 8080 create socket success bind socket success listen socket success thread-1 start ... thread-2 start ... thread-3 start ... thread-4 start ... thread-5 start ... thread-6 start ... thread-7 start ... thread-8 start ... thread-9 start ... thread-10 start ... accept a new link success sock: 4 accept a new link success sock: 5 recv message: 你好,我是客户端1 recv message: 你好,我是客户端2 注:关于线程池详细的设计与实现可以观看[线程池](https://blog.csdn.net/qq_65307907/article/details/129474658?spm=1001.2014.3001.5501 "线程池")这篇文章,里面有相信的代码实现 # 总结 以上就是关于TCP服务器实现多进程版,多线程版,线程池版的详细介绍,可以通过这篇文章发现之前在系统中学习的知识在网络中全部结合起来了,今天的介绍就到这里了,感谢大家的阅读,我们下次再见!

相关推荐
只是橘色仍温柔4 分钟前
xshell可以ssh连接,但vscode不行
运维·vscode·ssh
小吃饱了6 分钟前
TCP可靠性传输
网络·网络协议·tcp/ip
IT里的交易员9 分钟前
【系统】换硬盘不换系统,使用WIN PE Ghost镜像给电脑无损扩容换硬盘
运维·电脑
共享家952720 分钟前
深入剖析Linux常用命令,助力高效操作
linux·运维·服务器
大刘讲IT33 分钟前
制造业数字化转型:流程改造先行还是系统固化数据?基于以MTO和MTS的投资回报分析
运维·经验分享·生活·产品经理·数据可视化
吃旺旺雪饼的小男孩1 小时前
Ubuntu 22.04 安装和运行 EDK2 超详细教程
linux·运维·ubuntu
IT小馋猫1 小时前
Linux 企业项目服务器组建(附脚本)
linux·服务器·网络
阿政一号1 小时前
Linux进程间通信:【目的】【管道】【匿名管道】【命名管道】【System V 共享内存】
linux·运维·服务器·进程间通信
方渐鸿1 小时前
【2025】快速部署安装docker以及项目搭建所需要的基础环境(mysql、redis、nginx、nacos)
java·运维·docker·持续部署·dockercompse
小哈里2 小时前
【运维】云计算的发展历程,云原生时代的运维理念&工具技术栈,高可用系统的云运维 —— 以K8S集群调度算法与命令为例
运维·云原生·kubernetes·云计算·架构设计