【linux 多进程并发】0203 网络资源的多进程处理,子进程完全继承网络套接字,避免“惊群”问题

news2024/11/5 16:28:45

0203 网络资源的多进程处理

专栏内容

  • postgresql使用入门基础
  • 手写数据库toadb
  • 并发编程

个人主页:我的主页
管理社区:开源数据库
座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.

一、概述


上一章节介绍了堆栈资源,文件在父子进程中的表现,在应用程序中还有一类经常用到同样非常重要的资源,它就是网络套接字。

二、资源创建场景


当我们需要搭建一个网络服务器时,很多时候是多任务协作处理高并发的客户端请求,可能是如下几种模型。

接待-工作者模式

  • 一个任务负责监听来自客户端的连接请求;
  • 当有客户端连接请求时,建立连接,将新的客户端连接分发给工作者任务;
  • 工作者任务负责接收客户端的消息,处理,并响应客户端;
  • 一般只有一个负责接待的任务,同时会有多个工作者任务,并且根据并发多少不断增减工作者任务的数量;

在这里插入图片描述

监听-接待工作者模式

  • 一般由主任务初始化服务端并启动监听;
  • 然后再由主任务启动多个工作者任务,数量一般是CPU核的数倍;
  • 每个工作者任务都继承了这个正在监听的套接字;
  • 当有客户端连接请求时,在工作者任务中进行accept,建立与客户端的连接;
  • 每个工作者任务可以对应多个客户端,无阻塞方式处理来自多个客户端的消息;

在这里插入图片描述

大家可以从两个网络架构中看出,第一种模式应适于独占的式的应用;而第二种模式更适和与互联网应用。

三、套接字的继承


在父子进程的多任务架构中,父进程创建的网络套接字,fork出来子进程后,子进程是完全进行了复制。

3.1 验证代码

下面我们就以第二种网络模型为例进行验证。

代码如下:

/*
 * ex020302_netprocess.c
 */
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <arpa/inet.h>  
#include <sys/epoll.h>  
#include <fcntl.h>  
#include <errno.h>  
  
#define MAX_EVENTS 10  
#define BUFFER_SIZE 1024  
#define PORT 5809

void initializeServerNet();
void closeServerFd();
void dispatchLoop();

void subprocess();


int listen_fd;

int main(int argc ,char *argv[])
{
	initializeServerNet();
    
    subprocess();
    subprocess();

    dispatchLoop();
    
    closeServerFd();
	return 0;
}

void subprocess()
{
    int pid = -1;
	pid = fork();
	if(pid < 0)
	{
		printf("fork error[%s]\n",strerror(errno));
		exit(-1);
	}
	else if(pid > 0)
	{
		// parent.
		return;
	}
	else 
	{
		// child 
        dispatchLoop();
		exit(0);
	}
}

void initializeServerNet()
{
    struct sockaddr_in server_addr;  
  
    // 创建监听socket  
    listen_fd = socket(AF_INET, SOCK_STREAM, 0);  
    if (listen_fd == -1) 
    {  
        perror("socket");  
        exit(EXIT_FAILURE);  
    }  
   
    // 绑定地址和端口  
    server_addr.sin_family = AF_INET;  
    server_addr.sin_addr.s_addr = INADDR_ANY;  
    server_addr.sin_port = htons(PORT);  
    if (bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) 
    {  
        perror("bind");  
        close(listen_fd);  
        exit(EXIT_FAILURE);  
    }

    // 开始监听
    if (listen(listen_fd, SOMAXCONN) == -1)
    {
        perror("listen");
        close(listen_fd);
        exit(EXIT_FAILURE);
    }
}

void closeServerFd()
{
    close(listen_fd);  
}

void dispatchLoop()
{
    int conn_fd;  

    // 主循环  
    while (1) 
    {  
        // 新的连接  
        conn_fd = accept(listen_fd, NULL, NULL);  
        if (conn_fd == -1) 
        {  
            printf("[%d] accept wakeup, but failure.\n", getpid());
            sleep(1);
            continue;  
        }

        printf("[%d] accept a new client [%d]. \n",getpid(), conn_fd);

        close(conn_fd);
    }  

    closeServerFd();
}

说明

  • 在主进程中创建网络套接字,绑定地址,并启动监听;
  • 创建多个工作子进程;
  • 工作子进程中继承了监听套接字;
  • 每个工作子进程可以独立与客户端建立连接,并处理消息;

这里会创建两个子进程,在父进程和子进程中都会对同一个socket监听连接请求。

3.2 网络客户端

为了方便验证,我们编写一个简单的客户端程序。

/*
 * ex020302_client.c
 */

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <arpa/inet.h>  
  
#define SERVER_IP "127.0.0.1"  
#define SERVER_PORT 4808  
#define BUFFER_SIZE 1024  


int main(int argc, char *argv[]) 
{  
    int sockfd;  
    struct sockaddr_in server_addr;  
    char buffer[BUFFER_SIZE] = {0};  
    const char *message = "Hello, Server!";  
    int port = SERVER_PORT;

    if(argc > 1)
    {
        port = atoi(argv[1]);
    }

    // 创建套接字  
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
    {  
        perror("socket creation failed");  
        exit(EXIT_FAILURE);  
    }  
  
    // 配置服务器地址信息  
    server_addr.sin_family = AF_INET;  
    server_addr.sin_port = htons(port);  
  
    // 将IP地址从字符串转换为二进制形式  
    if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) 
    {  
        perror("Invalid address/ Address not supported");  
        close(sockfd);  
        exit(EXIT_FAILURE);  
    }  
  
    // 连接到服务器  
    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) 
    {  
        perror("Connection Failed");  
        close(sockfd);  
        exit(EXIT_FAILURE);  
    }  
  
    for(int i = 0; i < 20; i++)
    {
        // 发送消息到服务器  
        send(sockfd, message, strlen(message), 0);  
        printf("Message sent: %s\n", message);  
    
        // 接收服务器的响应  
        int bytes_received = recv(sockfd, buffer, BUFFER_SIZE - 1, 0);  
        if (bytes_received < 0) 
        {  
            perror("Error in receiving");  
        } 
        else if (bytes_received == 0) 
        {  
            printf("Server closed the connection\n");  
        } 
        else 
        {  
            buffer[bytes_received] = '\0'; // 确保字符串以空字符结尾  
            printf("Message received from server: %s\n", buffer);  
        }  

        sleep(1);
    }
  
    // 关闭套接字  
    close(sockfd);  
  
    return 0;  
}

说明

  • 客户端带一个参数,是服务端的端口号;
  • 创建socket,并且与服务端(IP:port)连接;
  • 不断发送和接收消息,重复20次;
  • 实际上服务端只是接收建立连接,不会接收和发送响应,这已经达到了测试的目的;

3.3 结果验证

  • 编译服务端,并且运行

这里没有使用deamon后台服务的方式运行,会停在这里。

[senllang@hatch ex02]$ gcc ex020105_forksocket.c -o test
[senllang@hatch ex02]$ ./test 
  • 编译客户端,并运行

在另外一个终端编译和运行网络客户端程序;

服务端默认的端口号是5809,作为参数输入。

[senllang@hatch ex02]$ gcc ex020302_client.c -o client_opt
[senllang@hatch ex02]$ ./client_opt 5809
Message sent: Hello, Server!
Error in receiving: Connection reset by peer
[senllang@hatch ex02]$ ./client_opt 5809
Message sent: Hello, Server!
Error in receiving: Connection reset by peer
[senllang@hatch ex02]$ ./client_opt 5809
Message sent: Hello, Server!
Error in receiving: Connection reset by peer
[senllang@hatch ex02]$ ./client_opt 5809
Message sent: Hello, Server!
Error in receiving: Connection reset by peer
[senllang@hatch ex02]$ ./client_opt 5809
Message sent: Hello, Server!
Error in receiving: Connection reset by peer
[senllang@hatch ex02]$ ./client_opt 5809
Message sent: Hello, Server!
Error in receiving: Connection reset by peer

为了模拟多客户端的场景,我们将客户端启动多次;

可以看到客户端启动后,打印了发送的消息,之后就退出了,

因为服务端与客户端建立连接成功后,随即就关闭了连接。

  • 运行结果

可以看到服务端打印的信息。

[1205954] accept a new client [4]. 
[1205955] accept a new client [4]. 
[1205956] accept a new client [4]. 
[1205954] accept a new client [4]. 
[1205955] accept a new client [4]. 
[1205956] accept a new client [4]. 

服务端启动了三个进程,可以看到三个进程的PID分别为1205954,1205955,1205956,它们都可以收到来自客户端的连接请求。

同时出现了一件很有意思的事情,三个进程轮流进行处理连接请求,这里主要避免了惊群的问题。

四、总结


本节主要分享了网络套接字在父子进程中的继承的情况。

可以通过验证发现,父启动监听后,此时创建子进程,在子进程中也继承了监听套接字,它也可以与客户端建立连接;而父进程中的监听套接字,也仍然可以与客户端建立连接。

当然其它网络操作步骤也是一样的,在父子进程中相同的套接字都会收到相同的网络事件,但最终只有一个进行处理,这样就带来一个问题,其它不处理事件的套接字会被频繁唤醒。

结尾


非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的评论,如果觉得值得鼓励,请点赞,收藏,我会更加努力!

作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。

注:未经同意,不得转载!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

江协科技STM32学习- P32 MPU6050

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

程序设计方法与实践-时空权衡

什么是时空权衡&#xff1f; 时空权衡是算法设计中的一个众所周知的问题&#xff0c;也就是对算法的空间和时间效率做出权衡&#xff0c;它大概有分两种形式&#xff1a; 对输入的部分数据或者全部数据作预处理&#xff0c;然后对于获得额外信息储存起来&#xff0c;从而加快…

STM32F1学习——TIM

一、STM32中的定时器 在STM32中分为三种定时器&#xff0c;分别是基本定时器&#xff0c;通用定时器和高级定时器&#xff0c;每种定时器都是向下兼容的。 二、定时器详细介绍 a、基本定时器 基本定时器主要由下面与分频器、计数器 和 自动重装寄存器三个组成的时基单元&#…

W5500-EVB-Pico2评估板介绍

目录 1 概述 2 板载资源 2.1 硬件规格 2.2 硬件规格 2.3 工作条件 3 参考资料 3.1 RP2350 数据手册 3.2 W5500 数据手册 3.3 原理图 原理图 & 物料清单 & Gerber 文件 3.3 尺寸图 (单位 : mm) 3.4 参考例程 认证 CE FCC AWS 资质 Microsoft Azure 认证…

FFmpeg 4.3 音视频-多路H265监控录放C++开发十二:在屏幕上显示多路视频播放,可以有不同的分辨率,格式和帧率。

上图是在安防领域的要求&#xff0c;一般都是一个屏幕上有显示多个摄像头捕捉到的画面&#xff0c;这一节&#xff0c;我们是从文件中读取多个文件&#xff0c;显示在屏幕上。

Oracle视频基础1.4.3练习

15个视频 1.4.3 できない dbca删除数据库 id ls cd cd dbs ls ls -l dbca# delete a database 勾选 # chris 勾选手动删除数据库 ls ls -l ls -l cd /u01/oradata ls cd /u01/admin/ ls cd chris/ ls clear 初始化参数文件&#xff0c;admin&#xff0c;数据文件#新版本了…

一个由Deno和React驱动的静态网站生成器

大家好&#xff0c;今天给大家分享一个由 Deno React 驱动的静态网站生成器Pagic。 项目介绍 Pagic 是一个由 Deno React 驱动的静态网站生成器。它配置简单&#xff0c;支持将 md/tsx 文件渲染成静态页面&#xff0c;而且还有大量的官方或第三方主题和插件可供扩展。 核心…

1分钟解决Excel打开CSV文件出现乱码问题

一、编码问题 1、不同编码格式 CSV 文件有多种编码格式&#xff0c;如 UTF - 8、UTF - 16、ANSI 等。如果 CSV 文件是 UTF - 8 编码&#xff0c;而 Excel 默认使用的是 ANSI 编码打开&#xff0c;就可能出现乱码。例如&#xff0c;许多从网络应用程序或非 Windows 系统生成的 …

发布天工AI高级搜索功能,昆仑万维做最懂科研学术的AI搜索

今天&#xff0c;昆仑万维天工AI正式发布最新版本的AI高级搜索功能。 一年时光&#xff0c;栉风沐雨。昆仑万维致力于通过领先的AI技术&#xff0c;为全球用户提供创新的智能搜索和信息处理解决方案。无论是金融、科技领域的专业搜索还是文档分析&#xff0c;「天工AI高级搜索…

mac找到主目录下的文件夹

访达-&#xff08;上方状态栏显示&#xff09;-然后在

安装fpm,解决*.deb=> *.rpm

要从生成 .deb 包转换为 .rpm 包&#xff0c;可以按照以下步骤修改打包脚本 1. 使用 fpm 工具 fpm 是一个强大的跨平台打包工具&#xff0c;可以将 .deb 包重新打包成 .rpm&#xff0c;也可以直接从源文件打包成 .rpm。 安装 fpm sudo apt-get install ruby-dev sudo gem in…

分布式光伏管理办法

随着分布式光伏项目的不断增加&#xff0c;传统的管理方式已经难以满足高效、精准的管理需求。光伏业务管理系统作为一种集信息化、智能化于一体的管理工具&#xff0c;正在逐步成为分布式光伏项目管理的重要支撑。 光伏业务管理系统通过数字化手段实现对光伏业务全流程的精细化…

数据结构:LRUCache

什么是LRUCache 首先我们来看看什么是cache 缓存&#xff08;Cache&#xff09;通常用于两个速度不同的介质之间&#xff0c;以提高数据访问的速度和效率。这里有几个典型的应用场景&#xff1a; 处理器和内存之间&#xff1a; 处理器&#xff08;CPU&#xff09;的运算速度远…

智能提醒助理系列-springboot项目彩虹日志+TraceID

本系列文章记录“智能提醒助理”产品建设历程&#xff0c;记录实践经验、巩固知识点、锻炼总结能力。 本篇介绍如何让springboot启动日志“彩打” 提升日志识别度&#xff0c;同时增加TraceID&#xff0c;便于同一请求&#xff0c;全链路的追踪。 一、需求出发点 提升日志识别度…

窨井监测遥测终端RTU IP68防水强信号穿透力

在窨井的潮湿 黑暗和腐蚀性环境中 常规物联网设备往往难以生存 如何突破层层环境挑战 轻松应对极端条件 确保信号 24h不掉线&#xff0c;不延迟 不仅是对技术的突破 更是对恶劣环境的征服 ↓↓↓ 坚守 ——严苛环境下的工业设备 计讯物联工业级设备&#xff0c;专为恶劣环境设计…

150道MySQL高频面试题,学完吊打面试官--如何实现索引机制

前言 本专栏为150道MySQL大厂高频面试题讲解分析&#xff0c;这些面试题都是通过MySQL8.0官方文档和阿里巴巴官方手册还有一些大厂面试官提供的资料。 MySQL应用广泛&#xff0c;在多个开发语言中都处于重要地位&#xff0c;所以最好都要掌握MySQL的精华面试题&#xff0c;这也…

基于Matlab 模拟停车位管理系统【源码 GUI】

系统对进入停车位的车辆进行车牌识别&#xff0c;将识别出来的车牌号显示出来&#xff1b;然后对车主进行人脸识别&#xff0c;框出车主照片的人脸部分作为车主信息的标记&#xff0c;记录在系统库中。车辆在库期间&#xff0c;系统使用者可以随意查看车辆与车主信息的获取过程…

微信小程序 https://pcapi-xiaotuxian-front-devtest.itheima.net 不在以下 request 合法域名

微信小程序在调用接口的时候出现以上报错&#xff0c;接口没有问题&#xff0c;是因为小程序自动校验了合法域名 打开本地设置&#xff1a; 勾选不校验合法域名&#xff0c;即可 效果如下&#xff1a;

数据治理,数据提取,大数据中心建设,大数据治理总体解决方案书(word,ppt原件)

1. 数据管理的现状 2. 数据治理的概述 1.1数据治理概念 2.2数据治理目标 3. 数据治理体系 4. 数据治理核心领域 1.1 数据模型 1.2 数据生命周期 &#xff08;1&#xff09;数据生成及传输 &#xff08;2&#xff09;数据存储 &#xff08;3&#xff09;数据处理和应用…

电机控制储备知识 二:电磁学理论知识

一&#xff1a;磁场的发现过程和和一些实验现象 古代发现&#xff1a;公元前七世纪&#xff0c;中国和古希腊的学者就已经发现了磁石。 吉尔伯特的研究&#xff1a;1600年&#xff0c;英国女王御臣威廉吉尔伯特&#xff08;William Gilbert&#xff09;发表了《地磁论》&#…