Linux--->网络编程(TCP并发服务器构建:[ 多进程、多线程、select ])

TCP并发服务器构建

一、服务器

    单循环服务器:服务端同一时刻只能处理一个客户端的任务(TCP)
并发服务器:服务端同一时刻可以处理多个客户端的任务(UDP)

二、TCP服务端并发模型

1、多进程

     进程资源开销大,安全性高。

     以下是一个实现多进程TCP服务端代码:

#define SER_PORT 62300
#define SER_IP "192.168.0.165"int init_tcp_ser()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){perror("socket error");return 0;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(SER_PORT);seraddr.sin_addr.s_addr = inet_addr(SER_IP);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if(ret < 0){printf("bind error");return -1;}int cnt = listen(sockfd, 100);if(cnt < 0){perror("listen error");return -1;}return sockfd;
}void wait_handler(int signo)
{wait(NULL);
}int main(int argc, char *argv[])
{signal(SIGCHLD, wait_handler);struct sockaddr_in cliaddr;socklen_t clilen = sizeof(cliaddr);int sockfd = init_tcp_ser();if(sockfd < 0){perror("init_tcp_ser error");return -1;}while(1){int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen); if(connfd < 0){perror("accept error");return -1;}pid_t pid = fork();if(pid > 0){}else if(pid == 0){char buff[1024] = {0};while(1){memset(buff, 0, sizeof(buff));ssize_t ret = recv(connfd, buff, sizeof(buff), 0);if(ret < 0){perror("send error");return -1;}else if(ret == 0){printf("[%s : %d]: offline!\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));break;}printf("[%s : %d]: %s\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), buff);strcat(buff, "----OK");ssize_t cnt = send(connfd, buff, strlen(buff), 0);if(cnt < 0){perror("send error");break;}}   close(connfd);}}close(sockfd);return 0;
}

2、多线程

     线程相对与进程资源开销小,相同资源环境下,并发量比进程大。

     以下是一个实现多线程TCP服务端代码:

#include "head.h"#define SER_PORT 62300
#define SER_IP "192.168.0.165"int init_tcp_ser()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){perror("socket error");return 0;}//允许绑定处于TIME_WAIT状态的地址,避免端口占用问题:int optval = 1;setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(SER_PORT);seraddr.sin_addr.s_addr = inet_addr(SER_IP);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if(ret < 0){printf("bind error");return -1;}int cnt = listen(sockfd, 100);if(cnt < 0){perror("listen error");return -1;}return sockfd;
}void *task(void *sug)
{int connfd = *(int *)sug;char buff[1024] = {0};while(1){memset(buff, 0, sizeof(buff));ssize_t ret = recv(connfd, buff, sizeof(buff), 0);if(ret < 0){perror("send error");break;}else if(ret == 0){printf("[%s : %d]: offline!\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));break;}printf("[%s : %d]: %s\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), buff);strcat(buff, "----OK");ssize_t cnt = send(connfd, buff, strlen(buff), 0);if(cnt < 0){perror("send error");break;}}   close(connfd);
}int main(int argc, char *argv[])
{struct sockaddr_in cliaddr;socklen_t clilen = sizeof(cliaddr);int sockfd = init_tcp_ser();if(sockfd < 0){perror("init_tcp_ser error");return -1;}while(1){int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen); if(connfd < 0){perror("accept error");return -1;}pthread_t tid;int fd = pthread_create(&tid, NULL, task, &connfd);if(fd < 0){perror("thread create error");return -1;}  pthread_detach(tid);}close(sockfd);return 0;
}

3、线程池

     为了解决多线程或者多进程模型,在服务器运行过程,频繁创建和销毁线程(进程)带来的时间消耗问题。
基于生产者和消费者编程模型,以及任务队列等,实现的一套多线程框架。

4.  IO多路复用

    1)概念

     对多个文件描述符的读写可以复用一个进程。
在不创建新的进程和线程的前提下,使用一个进程实现对多个文件读写的同时监测。
阻塞IO模式:

    2)方式

        实现方式有 select、poll、epoll 三种。

5、select 实现IO多路复用

    1)实现步骤

    (1)创建文件描述符集合     fd_set
(2)添加关注的文件描述符到集合     FD_SET
(3)使用 select 传递集合表给内核,内核开始监测事件    select()
(4)当内核监测到事件时,应用层select将解除阻塞,并获得相关的事件结果
(5)根据 select 返回的结果做不同的任务处理

    2)select 的宏

        将集合清0:void FD_ZERO(fd_set *set);
向集合中添加文件描述符:void FD_SET(int fd, fd_set *set);
从集合中移除文件描述符:void FD_CLR(int fd, fd_set *set);
检查文件描述符是否在集合中:int  FD_ISSET(int fd, fd_set *set);

    3)select() 函数接口

 int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);

功能:传递文件描述符结合表给内核并等待获取事件结果
参数:
nfds : 关注的最大文件描述符+1
readfds:读事件的文件描述符集合
writefds:写事件的文件描述符集合
exceptfds:其他事件的文件描述符集合
timeout:设置select监测时的超时时间
若为NULL : 不设置超时时间(select一直阻塞等待)

返回值:
成功:返回内核监测的到达事件的个数
失败:-1
0 : 超时时间到达,但没有事件发生,则返回0

    4)应用示例

        (1)通过select函数构建多路复用,实现管道和终端的读

        管道向服务端写的代码:

int main(void)
{mkfifo("./myfifo", 0664);int fd = open("./myfifo", O_WRONLY);if(fd < 0){perror("open error");return -1;}while(1){write(fd, "hello world", 11);sleep(1);}close(fd);return 0;
}

        读的代码:

int main(void)
{char buff[1024] = {0};mkfifo("./myfifo", 0664);int fifofd = open("./myfifo", O_RDONLY);if(fifofd < 0){perror("open error");return -1;}//1、创建文件描述符集合fd_set rdfds;fd_set rdfdstmp;//2、清空文件描述符集合表FD_ZERO(&rdfds);//初始化...集//3、添加关注的文件描述符到集合中FD_SET(0, &rdfds);//添加int maxfd = 0;FD_SET(fifofd, &rdfds);maxfd = maxfd > fifofd ? maxfd : fifofd;while(1){rdfdstmp = rdfds;//4、传递集合表给内核并等待返回到达事件的结果int cnt = select(maxfd+1, &rdfdstmp, NULL, NULL, NULL);if(cnt < 0){perror("select error");return -1;}if(FD_ISSET(0, &rdfdstmp))//检查文件描述符0是否在集合中{fgets(buff, sizeof(buff), stdin);//0printf("STDIN: %s\n", buff);}if(FD_ISSET(fifofd, &rdfdstmp)){memset(buff, 0, sizeof(buff));read(fifofd, buff, sizeof(buff));printf("FIFO: %s\n", buff);}}close(fifofd);return 0;
}

        (2)使用select实现TCP服务端IO多路复用代码

#define SER_PORT  50000
#define SER_IP    "192.168.0.165"int init_tcp_ser()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket error");return -1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(SER_PORT);seraddr.sin_addr.s_addr = inet_addr(SER_IP);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (ret < 0){perror("bind error");return -1;}ret = listen(sockfd, 100);if (ret < 0){perror("listen error");return -1;}return sockfd;
}int main(int argc, const char *argv[])
{char buff[1024] = {0};struct sockaddr_in cliaddr;socklen_t clilen = sizeof(cliaddr);int sockfd = init_tcp_ser();if (sockfd < 0){return -1;}//1. 创建文件描述符集合fd_set rdfds;fd_set rdfdstmp;FD_ZERO(&rdfds);//2. 添加关注的文件描述符到集合FD_SET(sockfd, &rdfds);int maxfd = sockfd;while (1){rdfdstmp = rdfds;//3. 传递集合到内核,并等待返回监测结果int cnt = select(maxfd+1, &rdfdstmp, NULL, NULL, NULL);if (cnt < 0){perror("select error");return -1;}//4. 是否有监听套接字事件到达 ----》三次握手已完成,可以acceptif (FD_ISSET(sockfd, &rdfdstmp)){int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);if (connfd < 0){perror("accept error");return -1;}FD_SET(connfd, &rdfds);maxfd = maxfd > connfd ? maxfd : connfd;}//5. 是否有通讯套接字事件到达for (int i = sockfd+1; i <= maxfd; i++){if (FD_ISSET(i, &rdfdstmp)){memset(buff, 0, sizeof(buff));ssize_t cnt = recv(i, buff, sizeof(buff), 0);if (cnt < 0){perror("recv error");FD_CLR(i, &rdfds);close(i);continue;}else if (0 == cnt){FD_CLR(i, &rdfds);close(i);continue;}printf("%s\n", buff);strcat(buff, "--->ok");cnt = send(i, buff, strlen(buff), 0);if (cnt < 0){perror("send error");FD_CLR(i, &rdfds);close(i);continue;}}}	}close(sockfd);return 0;
}

【END】

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

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

相关文章

深入解析达梦数据库:模式分类、状态管理与实操指南

达梦数据库&#xff08;DM Database&#xff09;作为国产数据库的核心代表&#xff0c;其模式与状态机制是保障数据高可用、实现主备同步的关键基础。无论是日常运维中的数据库配置&#xff0c;还是故障场景下的主备切换&#xff0c;都需要深入理解模式与状态的特性及交互逻辑。…

如何选择适合自己的PHP微服务框架?

在开始选择之前&#xff0c;我们首先要明白&#xff1a;为什么需要微服务框架&#xff1f;传统的单体应用&#xff08;Monolithic Application&#xff09;虽然开发简单&#xff0c;但随着业务复杂度的增加&#xff0c;会变得臃肿且难以维护。而微服务架构通过将应用拆分为一组…

ESP32使用场景及大规模物联网IoT

最近用ESP32搭建了一个网络,想知道搭建的网络拓扑对不对。一、物联网无线通信v.s通讯网络无线通信我第一个好奇的问题就是&#xff0c;物联网用ESP32的话&#xff0c;路由器用什么&#xff1f;物联网也可以组WLAN&#xff0c;通讯网也可以组WLAN。把自己的Tenda AC1200路由器拆…

NSSCTF 4th WP

第一次打比赛AK了&#xff0c;虽然题比较简单没啥好说的&#xff0c;但还是想记录一下 WEB ez_signin 源码&#xff1a; from flask import Flask, request, render_template, jsonify from pymongo import MongoClient import reapp Flask(__name__)client MongoClient…

Paimon——官网阅读:主键表

主键表(Table with PK)PK 是 Primary Key&#xff08;主键&#xff09;的缩写。在数据库中&#xff0c;主键是一个或多个列的组合&#xff0c;其值在表中是唯一的&#xff0c;并且不能为 NULL。主键的作用是确保每一行记录的唯一性&#xff0c;便于数据的查找、管理和维护&…

【配置 PyCharm 连接远程服务器进行开发和调试的完整流程】

前提条件&#xff1a; 1.PyCharm Professional&#xff08;社区版不支持远程解释器&#xff09; 2.代码在本地目录里面&#xff0c;可以同步上传远程服务器 3.宿主机上安装了conda 环境 操作方法&#xff1a; 1、在本地使用PyCharm打开工程代码&#xff1b; 2、然后Add New_in…

在压力测试中如何确定合适的并发用户数?

确定压力测试中的合适并发用户数 在进行压力测试时&#xff0c;确定合适的并发用户数是评估系统性能的关键步骤。并发用户数是指同时向系统发送请求的用户数量&#xff0c;它直接影响系统的负载水平和性能表现。以下是几种常用的方法和考虑因素&#xff0c;用于确定合适的并发…

微算法科技(NASDAQ:MLGO)突破性FPGA仿真算法技术助力Grover搜索,显著提升量子计算仿真效率

在量子计算迅猛发展的今天&#xff0c;量子算法尤其是在搜索和加密领域的应用&#xff0c;正逐步揭开了其颠覆性潜力。然而&#xff0c;量子计算机的实际实现仍是一项复杂且充满挑战的任务&#xff0c;因此&#xff0c;如何在经典计算平台上高效建模和仿真量子算法成为了当前的…

TencentOS Server 4.4 下创建mysql容器无法正常运行的问题

环境 腾讯的 TencentOS Server 4.4 服务器系统 Linux app 6.6.92-34.1.tl4.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Jun 25 14:33:47 CST 2025 x86_64 x86_64 x86_64 GNU/Linux docker使用的是yum安装的版本 [rootapp ~]# docker version Client:Version: 28.0.1-202…

稀土:从“稀有”到“命脉”的科技核心

稀土&#xff0c;这个听起来有些陌生的词汇&#xff0c;其实早已悄然渗透进我们生活的方方面面。它并非真的“稀有”&#xff0c;而是指17种金属元素的统称&#xff0c;包括镧、铈、钕、铕等。这些元素在地壳中并不稀少&#xff0c;但因其独特的物理和化学性质&#xff0c;使其…

开发手札:UnrealEngine编辑器开发

以前在unity框架中开发了非常多实用且高频使用的编辑器工具&#xff0c;现在准备把目前用得上工具移植到ue4中。下面说明一下ue4开发编辑器工具的流程。1.创建编辑器工具控件2.在控件中创建一个Button和一个EditableText&#xff0c;用于测试3.新建一个继承UEditorUtilityWidge…

EXCEL开发之路(一)公式解析—仙盟创梦IDE

Excel 数据校验&#xff1a;基于自定义格式的深度解析与开发实现引言在数据处理和管理领域&#xff0c;Excel 是一款广泛应用的工具。确保 Excel 中数据的准确性和完整性至关重要&#xff0c;而数据校验是达成这一目标的关键手段。本文将借助特定的代码示例&#xff0c;深入探讨…

Day14——JavaScript 核心知识全解析:变量、类型与操作符深度探秘

接续上文&#xff1a;《前端小白进阶 Day13&#xff1a;JavaScript 基础语法 交互技巧 知识图谱&#xff0c;零基础也能懂》-CSDN博客 点关注不迷路哟。你的点赞、收藏&#xff0c;一键三连&#xff0c;是我持续更新的动力哟&#xff01;&#xff01;&#xff01; 主页:一位…

anaconda本身有一个python环境(base),想用别的环境就是用anaconda命令行往anaconda里创建虚拟环境

差不多是这个意思&#xff0c;但需要稍微澄清一下&#xff1a;Anaconda 可以管理任意版本的 Python你安装了 Anaconda 后&#xff0c;默认有一个 base 环境自带的 Python。如果你想用其他版本&#xff0c;比如 Python 3.9、3.10&#xff0c;可以用 conda create -n py39 python…

毕业项目推荐:28-基于yolov8/yolov5/yolo11的电塔危险物品检测识别系统(Python+卷积神经网络)

文章目录 项目介绍大全&#xff08;可点击查看&#xff0c;不定时更新中&#xff09;概要一、整体资源介绍技术要点功能展示&#xff1a;功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出&#xff08;xls格式…

字库原理 GB2312-80

这篇文章介绍的是 在嵌入式开发中 常常会遇见的 中文字体点阵字库&#xff08;如汉字库&#xff09; 的核心编码原理&#xff1a;区位码 偏移计算 内存映射。我们将会介绍 GB2312-80 字库的工作机制。 &#x1f4d8;什么是 GB2312-80&#xff1f; GB2312-80 是中国国家标准定…

【Golang】 项目启动方法

Go 项目启动方法 1. 常见启动方式 使用 go run 命令 # 运行主程序文件 go run main.go# 运行多个文件 go run *.go# 运行整个模块&#xff08;Go 1.11&#xff09; go run .# 运行指定包 go run github.com/yourusername/yourproject先构建再执行 # 构建可执行文件 go build# 运…

3459. 最长 V 形对角线段的长度

Problem: 3459. 最长 V 形对角线段的长度 文章目录思路解题过程复杂度Code思路 深度优先搜索 记忆数组 解题过程 主函数和先遍历从每一个1开始搜索&#xff0c;并枚举每一个方向进入dfs&#xff0c;dfs先检查是否遍历过&#xff0c;然后枚举下一个可以走的方向&#xff0c;最后…

Unity 串口通信

可以通过计算机管理->设备管理器&#xff0c;查看端口串口通讯&#xff0c;通常是指的通过计算机或其他设备上的串行端口实现数据传输的过程。 定义与特点&#xff1a;串口通讯是按位&#xff08;bit&#xff09;发送和接收字节的通信方式&#xff0c;它将数据一位一位地顺序…

ArcGIS JSAPI 高级教程 - 创建渐变色材质的自定义几何体

ArcGIS JSAPI 高级教程 - 创建渐变色材质的自定义几何体核心代码完整代码在线示例工作中遇到一个比较复杂的功能&#xff0c;其中用到渐变色&#xff0c;于是研究了一下&#xff0c;发现虽然 JS API 不直接支持渐变色&#xff0c;但是也可以自定义创建渐变色&#xff0c;通过 M…