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服务器实现多进程版,多线程版,线程池版的详细介绍,可以通过这篇文章发现之前在系统中学习的知识在网络中全部结合起来了,今天的介绍就到这里了,感谢大家的阅读,我们下次再见!

相关推荐
❀搜不到44 分钟前
ubuntu 更新cmake
linux·运维·ubuntu
Mr_pyx44 分钟前
TypeScript 完全入门指南:从基础到项目配置
linux·运维·ubuntu
志栋智能1 小时前
安全超自动化如何支持业务快速安全地创新?
运维·安全·自动化
TechWayfarer1 小时前
别让“能用”的IP拖垮业务——共享IP易封禁的原因与IP风险等级评估实战
网络·python·tcp/ip·安全
console.log('npc')1 小时前
git发版上线的时候,打tag标签方便jenkins部署
运维·git·jenkins
Frank_refuel1 小时前
Linux网络之网络编程套接字
linux·运维·网络
minji...1 小时前
Linux 高级IO(一)理解IO及其本质,理解五种IO模型,非阻塞IO,fcntl
服务器·网络·多路转接·高级io·非阻塞io·五种io模型·阻塞io
lisanmengmeng1 小时前
gitlab 配置的jenkins 链接错误
运维·gitlab·jenkins
RD_daoyi1 小时前
Google 官方调整抓取工具 IP 文件路径:SEO 与服务器安全策略要变了?
服务器·人工智能·学习·tcp/ip·搜索引擎·chatgpt
treesforest1 小时前
如何查IP归属地?IP地址归属地查询的三种方式与选型指南
网络·数据库·网络协议·tcp/ip