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

目录

前言

1.存在的问题

2.多进程版

3.多线程版

4.线程池版

总结


前言

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

1.存在的问题

如图所示:

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

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

2.多进程版

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

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

解决方式有两种:

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

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

思路1代码:

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代码:

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.多线程版

说明:相比于多进程,多线程的创建和销毁对操作系统是更轻量的,消耗的资源也是更少的,所以实现数据通信可以采用多线程的方式,让主线程负责建立,让从线程负责数据通信

代码实现:

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<TcpServerData*>(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.线程池版

说明:线程池版的实现思路是基于多线程,虽然多线程创建和销毁的消耗比多进程的低,但是为了更进一步提升效率,可以预先创建好一批线程,主线程负责建立连接获取任务,然后将任务加入到队列中,让预先创建好的线程从队列中获取任务,然后处理获取到的任务。

代码实现:

void start()
{
    //线程池初始化:预先创建好一批线程:
    ThreadPool<Task>::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<Task>::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
注:关于线程池详细的设计与实现可以观看线程池这篇文章,里面有相信的代码实现

总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/79441.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

微信开发者工具项目简单介绍和使用

主要目录简介&#xff1a; 页面文件的简介&#xff1a; 四个json文件的简介&#xff1a; 1.app.json 2.project.config.json 3.sitemap.json 4.页面中的json 简单操作 1.快速新建小程序页面&#xff0c;在app.json的pages下编写页面的路径&#xff0c;保存后微信开发者工具会自…

SpringjDBCTemplate_spring25

1、首先导入两个包&#xff0c;里面有模板 2、transtion事务 jDbc操作对象&#xff0c;底层默认的是事务&#xff1a; 3、我们java一般对实体类进行操作。 4、第一步写好坐标。 创建一个Account表 数据修改用update 数据进去了

LeetCode235. 二叉搜索树的最近公共祖先

235. 二叉搜索树的最近公共祖先 文章目录 [235. 二叉搜索树的最近公共祖先](https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/)一、题目二、题解方法一&#xff1a;递归方法二&#xff1a;迭代 一、题目 给定一个二叉搜索树, 找到该树中两个指定…

XSS 跨站脚本攻击

XSS(DOM) XSS 又称CSS(Cross Site Scripting)或跨站脚本攻击&#xff0c;攻击者在网页中插入由JavaScript编写的恶意代码&#xff0c;当用户浏览被嵌入恶意代码的网页时&#xff0c;恶意代码将会在用户的浏览器上执行。 XSS攻击可分为三种&#xff1a;分别为反射型(Reflected…

VSCODE[配置ssh免密远程登录]

配置ssh免密远程登录 本文摘录于&#xff1a;https://blog.csdn.net/qq_44571245/article/details/123031276只是做学习备份之用&#xff0c;绝无抄袭之意&#xff0c;有疑惑请联系本人&#xff01; 这里要注意如下几个地方: 1.要进入.ssh目录创建文件: 2.是拷贝带"ssh-…

模糊测试面面观 | 模糊测试工具知多少

自1988年威斯康星大学的Barton Miller首次提出模糊测试这一概念以来&#xff0c;模糊测试领域经历了持续长久发展。模糊测试作为一种软件测试方法&#xff0c;旨在通过向程序输入模糊、随机、异常的数据&#xff0c;探测和发现潜在的漏洞和错误。这种方法备受安全研究人员的青睐…

使用 Ansible Galaxy 安装角色

使用 Ansible Galaxy 安装角色 使用 Ansible Galaxy 和要求文件 /home/curtis/ansible/roles/requirements.yml 。从以下 URL 下载角色并安装到 /home/curtis/ansible/roles &#xff1a; http://rhgls.area12.example.com/materials/haproxy.tar 此角色的名称应当为 balancer …

【无监督】2、MAE | 自监督模型提取的图像特征也很能打!(CVPR2022 Oral)

文章目录 一、背景二、方法三、效果 论文&#xff1a;Masked Autoencoders Are Scalable Vision Learners 代码&#xff1a;https://github.com/facebookresearch/mae 出处&#xff1a;CVPR2022 Oral | 何凯明 | FAIR 一、背景 本文的标题突出了两个词&#xff1a; masked…

微信小程序前后端开发快速入门(完结篇)

这篇是微信小程序前后端快速入门完结篇了&#xff0c;今天利用之前学习过的所有知识做一个新的项目「群登记助手v1.0」小程序。 整体技术架构&#xff1a;小程序原生前端小程序云开发。 经历了前面教程的学习&#xff0c;大家有了一定的基础&#xff0c;所以本次分享重心主要是…

【C++】位图和布隆过滤器

位图和布隆过滤器 前言正式开始位图位图讲解模拟实现位图几道关于位图的题目 布隆过滤器概念实例布隆过滤器模拟实现误判率测试几道题 前言 本来本篇是和前面的两篇连着的&#xff0c;但是没写到一块&#xff0c;位图和布隆过滤器都是基于哈希的思想的&#xff0c;如果对于哈希…

2023年即将推出的CSS特性对你影响大不大?

Google开发者大会每年都会提出有关于 Web UI 和 CSS 方面的新特性&#xff0c;今年又上新了许多新功能&#xff0c;今天就从中找出了影响最大的几个功能给大家介绍一下 :has :has() 可以通过检查父元素是否包含特定子元素或这些子元素是否处于特定状态来改变样式&#xff0c;也…

【JavaEE进阶】MyBatis的创建及使用

文章目录 一. MyBatis简介二. MyBatis 使用1. 数据库和数据表的创建2. 创建Mybatis项目2.1 添加MyBatis框架支持2.2 设置MyBatis配置信息 3. MyBatis开发流程4. MyBatis查询数据库测试 三. MyBatis 流程1. MyBatis 查询数据库流程2. MyBatis 框架交互流程图 一. MyBatis简介 M…

Springboot 实践(8)springboot集成Oauth2.0授权包,对接spring security接口

此文之前&#xff0c;项目已经添加了数据库DAO服务接口、资源访问目录、以及数据访问的html页面&#xff0c;同时项目集成了spring security&#xff0c;并替换了登录授权页面&#xff1b;但是&#xff0c;系统用户存储代码之中&#xff0c;而且只注册了admin和user两个用户。在…

Tomcat 部署优化

Tomcat Tomcat 开放源代码web应用服务器&#xff0c;是由java代码开发的 tomcat就是处理动态请求和基于java代码的页面开发 可以在html当中写入java代码&#xff0c;tomcat可以解析html页面当中的iava&#xff0c;执行动态请求 动态页面机制有问题&#xff1a;不对tomcat进行优…

仿牛客论坛项目day7|Kafka

一、阻塞队列 创建了一个生产者线程和一个消费者线程。生产者线程向队列中放入元素&#xff0c;消费者线程从队列中取出元素。我们可以看到&#xff0c;当队列为空时&#xff0c;消费者线程会被阻塞&#xff0c;直到生产者线程向队列中放入新的元素。 二、Kafka入门 发布、订阅…

mysql数据库迁移

目录 背景迁移数据库 背景 公司有个项目&#xff0c;刚开始数据量不是大的时候&#xff0c;数据库和服务上的所有应用数据都放在一个旧小盘中&#xff0c;随着项目数据的增长&#xff0c;旧的磁盘被占满了&#xff0c;导致系统无法写入数据&#xff0c;我和同事排查了很长时间…

阿里云云主机_ECS云服务器_轻量_GPU_虚拟主机详解

阿里云云主机分为云虚拟主机、云服务器ECS、轻量应用服务器、GPU云服务器、弹性裸金属服务器、专有宿主机、FPGA云服务器、高性能计算E-HPC、无影云电脑等&#xff0c;阿里云百科来详细说下阿里云云主机详解&#xff1a; 目录 阿里云云主机 云服务器ECS 轻量应用服务器 云…

C++ 面向对象三大特性——多态

✅<1>主页&#xff1a;我的代码爱吃辣 &#x1f4c3;<2>知识讲解&#xff1a;C 继承 ☂️<3>开发环境&#xff1a;Visual Studio 2022 &#x1f4ac;<4>前言&#xff1a;面向对象三大特性的&#xff0c;封装&#xff0c;继承&#xff0c;多态&#xff…

红帽8.2版本CSA题库:第十一题查找文件

红帽8.2版本CSA题库&#xff1a;第十一题查找文件 touch /tmp/{gamelan,jacques,libWedgeit.so.1.2.3} chown jacques:jacques /tmp/{gamelan,jacques,libWedgeit.so.1.2.3}mkdir /root/findfiles #创建文件夹 find / -user jacques -exec cp -a {} /root/findfiles \; …

Centos7安装Docker及配置加速器地址

一、安装docker #1.yum 包更新到最新 yum update #2.安装需要的软件包&#xff0c;yum-util 提供yum-config-manager功能&#xff0c;另外两个是devicemapper驱动依赖的 yum install -y yum-utils device-mapper-persistent-data lvm2 #3.设置yum源 yum-config-manager --add…