socket网络编程——多进程、多线程处理并发

news2024/10/6 6:46:49
  如下图所示, 当一个客户端与服务器建立连接以后,服务器端 accept()返回,进而准备循环接收客户端发过来的数据。
  如果客户端暂时没发数据,服务端会在 recv()阻塞。此时,其他客户端向服务器发起连接后,由于服务器阻塞了,无法执行 accept()接受连接,也就是其他客户端发送的数据,服务器无法读取。服务器也就无法并发同时处理多个客户端。

  这个问题可以通过引入多线程和多进程来解决。 服务端接受一个客户端的连接后,创建一个线程或者进程,然后在新创建的线程或进程中循环处理数据。主线程(父进程)只负责监听客户端的连接,并使用 accept()接受连接,不进行数据的处理。如下图所示:

 

多线程处理并发的服务器端示例代码 MultiThread.c 如下: 

#include <stdio.h>      // 引入标准输入输出头文件
#include <stdlib.h>     // 引入标准库头文件
#include <unistd.h>     // 引入Unix标准函数定义头文件
#include <string.h>     // 引入字符串处理头文件
#include <assert.h>     // 引入断言头文件
#include <sys/socket.h> // 引入套接字接口头文件
#include <netinet/in.h> // 引入互联网地址族头文件
#include <arpa/inet.h>  // 引入互联网定义头文件
#include <pthread.h>    // 引入POSIX线程头文件

void* fun(void * arg) // 线程函数
{
    int c = (int)arg; // 客户端套接字

    while( 1 ) // 持续处理客户端消息
    {
        char buff[128] = {0}; // 消息缓冲区
        if ( recv(c,buff,127,0) <= 0 ) // 接收消息
        {
            break; // 接收失败或连接关闭则退出循环
        }

        printf("recv(%d)=%s\n",c,buff); // 打印接收到的消息
        send(c,"ok",2,0); // 向客户端发送响应
    }

    printf("one client over(%d)\n",c); // 打印客户端结束信息
    close(c); // 关闭客户端套接字
}

int main() // 主函数
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0); // 创建套接字
    assert( sockfd != -1 ); // 断言套接字创建成功

    struct sockaddr_in saddr,caddr; // 服务器和客户端地址结构
    memset(&saddr,0,sizeof(saddr)); // 初始化服务器地址结构
    saddr.sin_family = AF_INET; // 设置地址族为IPv4
    saddr.sin_port = htons(6000); // 设置端口号
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 设置IP地址

    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr)); // 绑定地址
    assert( res != -1 ); // 断言绑定成功

    listen(sockfd, 5); // 监听套接字

    while( 1 ) // 持续接受客户端连接
    {
        int len = sizeof(caddr); // 客户端地址长度
        int c = accept(sockfd,(struct sockaddr*)&caddr,&len); // 接受客户端连接
        if ( c < 0 )
        {
            continue; // 接受失败则继续
        }

        printf("accept c = %d\n",c); // 打印接受的客户端套接字描述符

        pthread_t id; // 线程ID
        pthread_create(&id,NULL,fun,(void*)c); // 创建线程处理客户端
    }

    close(sockfd); // 关闭服务器套接字
    exit(0); // 退出程序
}
执行结果如下所示:

多进程处理并发的服务器端示例代码 MultiProcess.c 如下:  

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>

void DealClientLink(int c, struct sockaddr_in caddr)
{
    while(1)
    {
        char buff[128] = {0};
        int n = recv(c, buff, 127, 0); // 从客户端接收数据
        if(n <= 0)
        {
            break; // 如果接收失败或客户端断开连接,则退出循环
        }

        printf("%s:%d %s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), buff); // 打印客户端IP、端口和消息

        send(c, "OK", 2, 0); // 向客户端发送响应消息
    }

    printf("one client unlink\n"); // 打印客户端断开连接的消息
    close(c); // 关闭与客户端的连接
}

void sigfun(int sign)
{
    wait(NULL); // 处理僵尸进程
}

int main()
{
    signal(SIGCHLD, sigfun); // 设置信号处理函数,处理子进程结束信号,防止僵尸进程

    int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建套接字
    assert(-1 != sockfd); // 确保套接字创建成功

    struct sockaddr_in saddr;
    memset(&saddr, 0, sizeof(saddr)); // 初始化地址结构
    saddr.sin_family = AF_INET; // 设置地址类型为IPv4
    saddr.sin_port = htons(6000); // 设置端口号为6000
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 设置IP地址为本地地址

    int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)); // 绑定套接字
    assert(-1 != res); // 确保绑定成功

    listen(sockfd, 5); // 监听套接字,最大连接数为5

    while(1)
    {
        struct sockaddr_in caddr;
        int len = sizeof(caddr);

        int c = accept(sockfd, (struct sockaddr*)&caddr, &len); // 接受客户端连接
        assert(-1 != c); // 确保接受连接成功

        printf("%s:%d Link Success\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port)); // 打印客户端IP和端口

        pid_t pid = fork(); // 创建子进程
        assert(-1 != pid); // 确保子进程创建成功
        if(0 == pid)
        {
            DealClientLink(c, caddr); // 子进程处理客户端连接
            exit(0); // 子进程结束
        }
        else
        {
            close(c); // 父进程关闭客户端套接字
        }
    }

    close(sockfd); // 关闭服务器套接字
    exit(0); // 退出程序
}

客户端代码 TcpClient.c 如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建套接字
    assert(sockfd != -1); // 确保套接字创建成功

    struct sockaddr_in saddr;
    memset(&saddr, 0, sizeof(saddr)); // 初始化地址结构
    saddr.sin_family = AF_INET; // 设置地址类型为IPv4
    saddr.sin_port = htons(6000); // 设置端口号为6000
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 设置IP地址为本地地址

    int res = connect(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)); // 连接服务器
    assert(res != -1); // 确保连接成功

    while(1)
    {
        char buff[128] = {0};
        printf("input:\n"); // 提示用户输入

        fgets(buff, 128, stdin); // 获取用户输入
        if(strncmp(buff, "end", 3) == 0) // 如果输入为"end",则退出循环
        {
            break;
        }

        send(sockfd, buff, strlen(buff), 0); // 发送数据到服务器

        memset(buff, 0, 128); // 清空缓冲区
        recv(sockfd, buff, 127, 0); // 接收服务器响应
        printf("buff=%s\n", buff); // 打印服务器响应
    }

    close(sockfd); // 关闭套接字
}

执行结果如下所示:

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

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

相关文章

DP:子序列模型

子数组vs子数列 1、子数组&#xff08;n^2&#xff09; 子序列(2^n) 2、子数组是子序列的一个子集 3、子数组必须连续&#xff0c;子序列可以不连续 一、最长递增子序列 . - 力扣&#xff08;LeetCode&#xff09; 算法原理&#xff1a; 1、状态表示&#xff…

智能电销机器人的作用和原理是什么?

要问世界上更火爆的创新技术&#xff0c;人工智能必然要算其一&#xff0c;人工智能正不断的改变着我们的生活&#xff0c;比如智能手机、智能家居、智能门锁等产品已经不断的渗透在了我们的生活之中&#xff0c;而近几年兴起的人工智能语音识别机器人&#xff0c;也迅速俘获了…

Centos7安装ElasticSearch

Centos7安装ElasticSearch 准备工作 下载elasticsearch https://www.elastic.co/cn/elasticsearch 将下载好的包上传到/usr/local/elasticsearch/ 路径下 安装 安装elasticsearch解压缩即可&#xff01; tar -zxvf elasticsearch-8.12.2-linux-x86_64.tar.gz进入/usr/loca…

使用Django Channels和WebSocket构建聊天应用

一、引言 WebSocket提供了一种在客户端和服务器之间进行实时双向通信的方式。结合Django Channels&#xff0c;我们可以轻松地在Django项目中实现WebSocket功能。本文将通过一个简单的聊天应用示例&#xff0c;展示如何使用Django Channels和WebSocket。 二、环境搭建 项目的…

Spring Boot 整合开源 Tess4J库 实现OCR图片文字识别

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

Linux基础 (十二):Linux 线程的创建与同步

本篇博客详细介绍与线程有关的内容&#xff0c;这部分也是笔试面试的重点&#xff0c;需要我们对线程有深刻的理解&#xff0c;尤其是线程的并发运行以及线程同步的控制&#xff01;接下来&#xff0c;让我们走进线程的世界&#xff0c;去理解线程&#xff0c;使用线程&#xf…

修改west扩展命令的路径

west命令是zephyr工程中非常重要的工具。使用west命令&#xff0c;可以高效的创建工程&#xff0c;管理代码&#xff0c;此外&#xff0c;通过扩展命令&#xff0c;还可以支持编译&#xff0c;烧录等功能。 从下图中可以看出&#xff0c;extension commands from project mani…

线性代数|机器学习-P8矩阵低秩近似eckart-young

文章目录 1. SVD奇异值分解2. Eckart-Young2.1 范数 3. Q A Q U Σ V T QAQU\Sigma V^T QAQUΣVT4. 主成分分析图像表示 1. SVD奇异值分解 我们知道&#xff0c;对于任意矩阵A来说&#xff0c;我们可以将其通过SVD奇异值分解得到 A U Σ V T AU\Sigma V^T AUΣVT&#xff0…

[ue5]建模场景学习笔记(4)——必修内容可交互的地形,交互沙(1)

1.需求分析&#xff1a; 现在的沙漠场景仅仅只是一张贴图&#xff0c;人物走过不会留下任何痕迹&#xff0c;很不真实&#xff0c;尝试优化一下&#xff0c;做出可交互的沙漠效果。 2.操作实现&#xff1a; 1.思路&#xff1a;这是一个相对复杂的工程&#xff0c;要考虑玩家踩…

深入理解C++三五零法则

三五零法则就是三法则&#xff08;The Rule of Three&#xff09;、五法则&#xff08;The Rule of Five&#xff09;、零法则&#xff08;The Rule of Zero&#xff09;。三五零法则是和C的特殊成员函数有关&#xff0c;特别是那些涉及对象如何被创建、复制、移动和销毁的函数…

ESD防护SP3232E真+3.0V至+5.5V RS-232收发器

特征 采用3.0V至5.5V电源&#xff0c;符合真正的EIA/TIA-232-F标准 满载时最低 120Kbps 数据速率 1μA 低功耗关断&#xff0c;接收器处于活动状态 &#xff08;SP3222E&#xff09; 可与低至 2.7V 电源的 RS-232 互操作 增强的ESD规格&#xff1a; 15kV人体模型 15kV IEC1000…

Java Web学习笔记17——Vue快速入门

什么是Vue&#xff1f; Vue是一套前端框架&#xff0c;免除原生JavaScript中的DOM操作&#xff0c;简化书写。 基于MVVM&#xff08;Model-View-ViewModel&#xff09;思想&#xff0c;实现数据的双向绑定&#xff0c;将编程的关注点放在数据上。 官网&#xff1a;https://v…

概率分析和随机算法

目录 雇佣问题 概率分析 随机算法 生日悖论 随机算法 概率分析 球与箱子 总结 雇佣问题 有n个候选人面试&#xff0c;如果面试者比目前雇佣者的分数高&#xff0c;评价更好&#xff0c;那么就辞掉当前雇佣者&#xff0c;而去聘用面试者&#xff0c;否则继续面试新的候…

区块链简要介绍及运用的技术

一、区块链的由来 区块链概念最早是从比特币衍生出来的。 比特币&#xff08;Bitcoin&#xff09;诞生于2008年&#xff0c;是由一个名叫中本聪&#xff08;Satoshi Nakamoto&#xff09;的人首次提出&#xff0c;这个人非常神秘&#xff0c;至今没有他的任何准确信息。在提出…

三、【源码】Mapper XML的解析和注册使用

源码地址&#xff1a;https://github.com/mybatis/mybatis-3/ 仓库地址&#xff1a;https://gitcode.net/qq_42665745/mybatis/-/tree/03-parse-mapperXML Mapper XML的解析和注册使用 流程&#xff1a; 1.Resources加载MyBatis配置文件生成Reader字符流 2.SqlSessionFact…

Activity->Activity中动态添加Fragment->add和replace方式添加的区别

XML文件 Activity布局文件R.layout.activity_main <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:id"id/root_ll"android:orientation"v…

一个简单好用的 C# Animation Easing 缓动动画类库

文章目录 1. 类库说明2.使用步骤2.1 创建一个Windows Form 项目2.2 安装类库2.3 编码效果3.代码下载1. 类库说明 App.Animations 类库是一个很精炼、好用的 csharp easing 动画库 基于 net-standard 2.0提供 Fluent API,写代码非常舒服。支持多个参数同时参与动画。自带了十几…

Flutter基础 -- Flutter常用组件

目录 1. 文本组件 Text 1.1 基础用法 1.2 Text 定义 1.3 Text 示例 1.4 Text.rich、RichText 、TextSpan 1.5 RichText 示例 2. 导入资源 2.1 加入资源 2.2 加入图片 3. 图片组件 image 3.1 colorBlendMode 混合参数 3.2 fit 图片大小适配 3.3 ImageProvider 图片…

【Python报错】已解决NameError: name ‘xxx‘ is not defined

解决Python报错&#xff1a;NameError: name ‘xxx’ is not defined 在Python编程中&#xff0c;NameError是一个非常常见的错误类型&#xff0c;它发生在你尝试访问一个未被定义的变量时。本文将介绍这种错误的原因&#xff0c;以及如何通过具体的代码示例来解决这个问题。 …

深度学习笔记: 最详尽LinkedIn Feed 排名系统设计

欢迎收藏Star我的Machine Learning Blog:https://github.com/purepisces/Wenqing-Machine_Learning_Blog。如果收藏star, 有问题可以随时与我交流, 谢谢大家&#xff01; LinkedIn Feed 排名 1. 问题陈述 设计一个个性化的LinkedIn Feed&#xff0c;以最大化用户的长期参与度…