day38 tcp 并发 ,linux下的IO模型----IO多路复用

TCP 并发

由于tcp协议只能实现一对一的通信模式。为了实现一对多,有以下的的处理方式

1. 多进程
开销大
效率低

2. 多线程
创建线程需要耗时
3. 线程池
多线程模型创建线程耗时问题,提前创建
4. IO多路复用
在不创建进程和线程的前提下,对多个文件描述符进行同时监测
IO:fd读写, 共用一个进程

1.多进程(服务端)

#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>int init_tpc_ser(const char *ip , unsigned short port)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){perror("fail socket");return -1;}struct sockaddr_in ser;ser.sin_family = AF_INET;ser.sin_port = htons(port);ser.sin_addr.s_addr = inet_addr(ip);int ret = bind(sockfd,(struct sockaddr *)&ser,sizeof(ser));if(ret < 0){perror("fail bind ");return -1;}return sockfd;}void handler(int signo)
{wait(NULL);
}int main(int agrc, char *agrv[])
{signal (17,handler);int sockfd = init_tpc_ser("192.168.0.179",50000);int ret = listen (sockfd,100);char buff[1024] = {0};if(ret < 0){perror("fail listen");return -1;}while(1){int connfd = accept(sockfd,NULL,NULL);if(connfd < 0){perror("fail accept");return -1;}pid_t pid = fork();if(pid > 0){}else if(pid == 0){while(1){memset(buff, 0 ,sizeof(buff));int ret = recv(connfd,buff ,sizeof(buff),0);printf("%s\n",buff);if(ret <= 0){return -1;}strcat(buff,"-----ok");ret = send(connfd, buff,sizeof(buff),0);if(ret < 0){return -1;}   }return 0;}else{printf("fork fail");return -1;}}close(sockfd);return-1;}

2.多线程(服务端)

#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>int init_tpc_ser(const char *ip , unsigned short port)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){perror("fail socket");return -1;}struct sockaddr_in ser;ser.sin_family = AF_INET;ser.sin_port = htons(port);ser.sin_addr.s_addr = inet_addr(ip);int ret = bind(sockfd,(struct sockaddr *)&ser,sizeof(ser));if(ret < 0){perror("fail bind ");return -1;}return sockfd;}char buff[1024] = {0};
void* task(void *reg)
{int connfd = *(int *)reg;//传过来保存就不会收到其他线程影响while(1){memset(buff, 0 ,sizeof(buff));int ret = recv(connfd,buff ,sizeof(buff),0);printf("%s\n",buff);if(ret == 0){break;}strcat(buff,"-----ok");ret = send(connfd, buff,sizeof(buff),0);if(ret = 0){break;}   }close(connfd);pthread_detach(pthread_self());//设置线程分离属性。主线程无暇回收次线程return 0;
}int main(int agrc, char *agrv[])
{int sockfd = init_tpc_ser("192.168.0.179",50000);int ret = listen (sockfd,100);int connfd = 0;pthread_t tid = 0;if(ret < 0){perror("fail listen");return -1;}while(1){connfd = accept(sockfd,NULL,NULL);if(connfd < 0){perror("fail accept");return -1;}pthread_create(&tid,NULL,task,&connfd); }close(sockfd);return-1;}

3.io多路复用(具体见下)

2.IO模型

Linux下常用的IO模型:

1. 阻塞IO

fgets
read
getchar
fread
fgetc
recv
recvfrom
1. 让多个IO事件以同步方式处理(顺序处理),
2. 多个IO事件之间互相影响
3. CPU占有率低

2. 非阻塞IO

   将IO对应的文件描述符设置成非阻塞方式。O_NONBLOCKfcntl1.非阻塞一般搭配轮询方式同时监测多个IO2.cpu占有率高
#include "head.h"int main(int argc, const char *argv[])
{mkfifo("./fifo", 0664);	char buff[1024] = {0};int fifofd = open("./fifo", O_RDONLY);if (fifofd < 0){perror("fail open fifo");return -1;}//1, 获取文件描述符(终端)原flags特性int flags = fcntl(0, F_GETFL);//2. 给原flags增加非阻塞特性flags = flags | O_NONBLOCK;//3. 给文件描述符设置新flags特性fcntl(0, F_SETFL, flags);while (1){memset(buff, 0, sizeof(buff));fgets(buff, sizeof(buff), stdin);//不会堵塞在这里,内核检测到键盘输入自动运行printf("STDIN : %s\n", buff);memset(buff, 0, sizeof(buff));read(fifofd, buff, sizeof(buff));printf("FIFO : %s\n", buff);}close(fifofd);return 0;
}

3. 信号驱动IO

  SIGIO1.实现异步IO操作,节省CPU开销2.只能针对比较少的IO事件(信号很少)
#include "head.h"char buff[1024] = {0};
void handler (int signo)
{memset(buff, 0, sizeof(buff));fgets(buff, sizeof(buff), stdin);printf("STDIN : %s\n", buff);}
int main(int argc, const char *argv[])
{signal(SIGIO,handler);mkfifo("./fifo", 0664);	int fifofd = open("./fifo", O_RDONLY);if (fifofd < 0){perror("fail open fifo");return -1;}//1, 获取文件描述符原flags特性int flags = fcntl(0, F_GETFL);//2. 给原flag异步flags = flags |O_ASYNC;//3. 给文件描述符设置新flags特性fcntl(0, F_SETFL, flags);//关联信号与当前进程fcntl(0,F_SETOWN,getpid());while (1){memset(buff, 0, sizeof(buff));read(fifofd, buff, sizeof(buff));printf("FIFO : %s\n", buff);}close(fifofd);return 0;
}

4. IO多路复用(IO多路转接)

   在不创建新的进程和线程的前提下, 在一个进程中,同时监测多个IO
1. select1. 文件描述符集合以数组(位图)的方式保存,最多监测1024个文件描述符。2. 文件描述符集合创建在应用层,需要应用层和内核层反复拷贝3. 内核层返回整个文件描述符集合,需要用户层遍历查找到达事件的文件描述符4. 只能工作在水平触发模式(低速模式)
2. poll1. 文件描述符集合以链表的方式保存,监测文件描述符可超过1024。2. 文件描述符集合创建在应用层,需要应用层和内核层反复拷贝3. 内核层返回整个文件描述符集合,需要用户层遍历查找到达事件的文件描述符4. 只能工作在水平触发模式(低速模式)
3. epoll 1. 文件描述符集合以树型结构(红黑树)保存,监测文件描述符可超过1024, 提高了数据的查找速度。2. 文件描述符集合创建在内核层;3. 返回的是到达事件的文件描述符集合,无需遍历查找4. 可以工作在水平触发模式(低速模式),也可以工作在边沿触发模式(高速模式)

1.select

  1. 创建文件描述符集合
    2. 添加关注的文件描述符到集合
    3. 传递给内核,内核开始监测IO事件 select
    4. IO事件到达则返回结果–》
    2.哪个fd事件到达执行哪个
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
功能:通知内核监测IO事件并返回事件结果
参数:nfds:最大文件描述符+1readfds: 文件描述符读事件集合writefds:文件描述符写事件集合exceptfds:其他timeout : 超时时间
返回值:成功:返回达到的事件的个数失败:-1设置超时:超时时间到,但没有事件到达:0void FD_CLR(int fd, fd_set *set);int  FD_ISSET(int fd, fd_set *set);void FD_SET(int fd, fd_set *set);void FD_ZERO(fd_set *set);
#include "head.h"int main(int argc, const char *argv[])
{mkfifo("./fifo", 0664);	char buff[1024] = {0};int fifofd = open("./fifo", O_RDONLY);if (fifofd < 0){perror("fail open fifo");return -1;}int maxfd = 0;fd_set rdfds;//创建集合FD_ZERO(&rdfds);//清0//加入集合FD_SET(0,&rdfds);FD_SET(fifofd,&rdfds);maxfd = fifofd >maxfd ?fifofd:maxfd;fd_set rdfdscopy;while(1){rdfdscopy= rdfds;//防止rdfds被修改,//发送位图给内核int cnt = select(maxfd+1,&rdfdscopy,NULL,NULL,NULL);if(cnt < 0){perror("fail select");return -1;}//判断是否事件到达if(FD_ISSET(0,&rdfdscopy)){memset(buff,0,sizeof(buff));fgets(buff,sizeof(buff),stdin);printf("stdin : %s",buff);}if(FD_ISSET(fifofd,&rdfdscopy)){memset(buff,0,sizeof(buff));read(fifofd,buff,sizeof(buff));printf("fifo : %s\n",buff);}}close(fifofd);return 0;
}

select实现并发tcp

#include "head.h"int init_tcp_ser(const char *ip, unsigned short port)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("fail socket");return -1;}struct sockaddr_in ser;ser.sin_family = AF_INET;ser.sin_port = htons(port);ser.sin_addr.s_addr = inet_addr(ip);int ret = bind(sockfd, (struct sockaddr *)&ser, sizeof(ser));if (ret < 0){perror("fail bind");return -1;}ret = listen(sockfd, 100);if (ret < 0){perror("fail listen");return -1;}return sockfd;
}	int main(int argc, const char *argv[])
{char buff[1024] = {0};int sockfd = init_tcp_ser("192.168.0.166", 50000);	if (sockfd < 0){return -1;}struct sockaddr_in cli;socklen_t clilen = sizeof(cli);int maxfd = 0;//1、 创建文件描述符集合fd_set rdfds, rdfdstmp;FD_ZERO(&rdfds);//2. 将关注的监听套接字加入到集合FD_SET(sockfd, &rdfds);maxfd = sockfd;while (1){//3. 内核开始检测套接字事件rdfdstmp = rdfds;int cnt = select(maxfd+1, &rdfdstmp, NULL, NULL, NULL);if (cnt < 0){perror("fail select");return -1;}// 是否有监听套接字事件到达if (FD_ISSET(sockfd, &rdfdstmp)){int connfd = accept(sockfd, (struct sockaddr *)&cli, &clilen);if (connfd < 0){perror("fail accept");return -1;}printf("[%s : %d] get online!\n", inet_ntoa(cli.sin_addr), ntohs(cli.sin_port));FD_SET(connfd, &rdfds);maxfd = maxfd > connfd ? maxfd : connfd;}//循环遍历剩余套接字,是否有通讯套接字事件到达for (int i = sockfd+1; i <= maxfd; i++){if (FD_ISSET(i, &rdfdstmp)){memset(buff, 0, sizeof(buff));ssize_t size = recv(i, buff, sizeof(buff), 0);if (size < 0){perror("fail recv");FD_CLR(i, &rdfds);close(i);continue;}else if (0 == size){FD_CLR(i, &rdfds);close(i);continue;}printf("%s\n", buff);strcat(buff, "-----ok!");size  = send(i, buff, strlen(buff), 0);if (size < 0){perror("fail recv");FD_CLR(i, &rdfds);close(i);continue;				}}}}close(sockfd);return 0;
}

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

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

相关文章

Kotlin | Android Provider 的实现案例

目标 使用 Android Room 实现持久化库。 代码 Kotlin 代码编写 DemoDatabase&#xff0c;在build生成 DemoDatabase_Impl 疑问 Provider的数据会存在设备吗&#xff1f; 内部存储: 当使用 Room 创建数据库&#xff08;如 DemoDatabase&#xff09;&#xff0c;数据库文件通常…

NFS服务

nfs文件系统 NFS:NetworkFileSystem网络文件系统&#xff0c;基于内核的文件系统。 服务安装 不固定端口启动&#xff0c;会注册到rpcbind&#xff08;固定端口&#xff09;服务上&#xff0c; 局域网适用[rootvm ~]# yum -y install nfs-utils # 依赖安装rpcbind [ro…

<C++学习>C++ 标准库(STD)的输入与输出(I/O)操作教程

C 的输入与输出主要通过标准库中的流类实现&#xff0c;包括标准输入流&#xff08;std::cin&#xff09;、标准输出流&#xff08;std::cout&#xff09;、标准错误流&#xff08;std::cerr&#xff09;和文件流&#xff08;std::ifstream 和 std::ofstream&#xff09;。以下…

maven多模块项目编译一直报Failure to find com.xxx.xxx:xxx-xxx-xxx:pom:1.0-SNAPSHOT in问题

工作中项目上因为多版本迭代&#xff0c;需要对不同迭代版本升级版本号&#xff0c;且因为项目工程本身是多模块结构&#xff0c;且依然多个其他模块工程。 在将工程中子模块的pom.xml中版本号使用变量引用父模块中定义的版本号时&#xff0c;一直报Failure to find com.xxx.x…

来自通义万相的创意加速器:AI 绘画创作

来自通义万相的创意加速器&#xff1a;AI 绘画创作 通义万相动手搭建“通义万相”部署方案资源准备对象存储OSS&#xff08;手动部署&#xff09;DashScope 模型服务灵积云服务器ECS&#xff08;手动部署&#xff09;一键部署ROS Web文生图艺术与设计创作广告与营销物料生成教育…

【Uniapp-Vue3】组合式API中的组件的生命周期函数(钩子函数)

在Uniapp中生命周期函数用得较多的是onMounted和onUnmounted。 一、onMounted函数 如果我们想要获得DOM元素&#xff0c;就需要给DOM标签上添加ref属性&#xff0c;并定义一个相同属性名的变量。 但是我们输出这个DOM元素为NULL 如果我们使用onMounted就能获得到DOM元素&…

Web 品质样式表

《Web 品质样式表》是一个重要的指南&#xff0c;旨在帮助开发者提升网站的整体质量和用户体验。以下是一些关键点&#xff1a; 避免使用 <font> 标签&#xff1a;应使用 CSS 来设置显示网页上的字体尺寸。使用 <font> 标签会增加文档的规模&#xff0c;且使每次改…

uniapp使用chooseLocation安卓篇

本文章全部以高德地图为例 代码 <view class"bottom"><button click"choose">定位</button> </view> choose() {uni.chooseLocation({success: function(res) {console.log(位置名称&#xff1a; res.name);console.log(详细地…

js:根据后端返回数据的最大值进行计算然后设置这个最大值为百分之百,其他的值除这个最大值

问&#xff1a; 现在tabData.value 接收到了后端返回的数据&#xff0c; [{text:人力,percentage&#xff1a;‘90’}&#xff0c;{text:物品,percentage&#xff1a;‘20’}&#xff0c;{text:物理,percentage&#xff1a;‘50’}&#xff0c;{text:服务,percentage&#xff…

设计一个利用事务特性可以阻塞线程的排他锁,并且通过注解和 AOP 来实现

设计思路&#xff1a; 利用数据库表记录锁标识&#xff1a;通过唯一标识符&#xff08;如方法名 参数&#xff09;&#xff0c;我们可以在数据库中插入一条记录&#xff0c;表示当前方法正在执行。这条记录需要记录插入时间。 注解&#xff1a;通过注解标识哪些方法需要加锁&a…

标定 3

标定场景与对应的方式 标定板标定主要应用场景: (1)无法获取到执行机构物理坐标值,比如相机固定,执行机构为传送带等 (2)相机存在畸变等非线性标定情况,需要进行畸变校正 (3)标定单像素精度 (4)获取两个相机之间的坐标系关系 标定板操作步骤: (1)确定好拍…

了解模2除法:原理与应用

模2除法&#xff0c;也被称为二进制除法或XOR除法&#xff0c;是一种在二进制数制下进行的特殊除法运算。与常规的十进制或其他进制的除法不同&#xff0c;模2除法使用异或&#xff08;XOR&#xff09;运算代替减法&#xff0c;并且不涉及进位或借位。这种除法运算在数字通信、…

基于 SSH 的任务调度系统

文末附有完整项目代码 在当今科技飞速发展的时代&#xff0c;任务调度系统的重要性日益凸显。本文将详细介绍一个基于 SSH&#xff08;SpringStruts2Hibernate&#xff09;的任务调度系统的设计与实现。 一、系统概述 本系统旨在改变传统人工任务调度方式&#xff0c;通过计算…

天童美语:如何培养孩子的音乐细胞

在孩子的成长旅程中&#xff0c;音乐不仅是美妙的旋律&#xff0c;更是情感表达和智力发展的重要媒介。培养孩子的音乐细胞不仅能增强他们的艺术修养&#xff0c;还能促进大脑发育&#xff0c;提高创造力和协调性。济南天童美语提供一些有效的方法&#xff0c;帮助家长激发孩子…

RFC 793

读 TCP 协议 RFC-793_rfc 793-CSDN博客TCP灌包中RTT时延与RTO超时关系 - konglingbin - 博客园 TCP的RTT算法 从前面的TCP重传机制我们知道Timeout的设置对于重传非常重要。 设长了&#xff0c;重发就慢&#xff0c;丢了老半天才重发&#xff0c;没有效率&#xff0c;性能差&…

Transformer:深度学习的变革力量

深度学习领域的发展日新月异&#xff0c;在自然语言处理&#xff08;NLP&#xff09;、计算机视觉等领域取得了巨大突破。然而&#xff0c;早期的循环神经网络&#xff08;RNN&#xff09;在处理长序列时面临着梯度消失、并行计算能力不足等瓶颈。而 Transformer 的横空出世&am…

计算机网络 笔记 数据链路层 2

1,信道划分&#xff1a; (1)时分复用TDM 将时间等分为“TDM帧”&#xff0c;每个TDM帧内部等分为m个时隙&#xff0c;m个用户对应m个时隙 缺点&#xff1a;每个节点只分到了总带宽的1/m,如果有部分的1节点不发出数据&#xff0c;那么就会在这个时间信道被闲置&#xff0c;利用…

vue el-table 数据变化后,高度渲染问题

场景&#xff1a;el-table设置了height属性&#xff0c;但是切换查询条件后再次点击查询重新获取data时&#xff0c;el-table渲染的高度会有问题&#xff0c;滚动区域变矮了。 解决办法&#xff1a;使用doLayout方法‌&#xff0c;在表格数据渲染后调用doLayout方法可以重新布局…

深度学习|表示学习|一个神经元可以干什么|02

如是我闻&#xff1a; 如果我们只有一个神经元&#xff08;即一个单一的线性或非线性函数&#xff09;&#xff0c;仍然可以完成一些简单的任务。以下是一个神经元可以实现的功能和应用&#xff1a; 1. 实现简单的线性分类 输入&#xff1a;一组特征向量 x x x 输出&#xff…

开源生成式物理引擎Genesis,可模拟世界万物

这是生成大模型时代 —— 它们能生成文本、图像、音频、视频、3D 对象…… 而如果将所有这些组合到一起&#xff0c;我们可能会得到一个世界&#xff01; 现在&#xff0c;不管是 LeCun 正在探索的世界模型&#xff0c;还是李飞飞想要攻克的空间智能&#xff0c;又或是其他研究…