多路复用IO、TCP并发模型

时分复用

CPU单核在同一时刻只能做一件事情,一种解决办法是对CPU进行时分复用(多个事件流将CPU切割成多个时间片,不同事件流的时间片交替进行)。在计算机系统中,我们用线程或者进程来表示一条执行流,通过不同的线程或进程在操作系统内部的调度,来做到对CPU处理的时分复用。这样多个事件流就可以并发进行,不需要一个等待另一个太久,在用户看起来他们似乎就是并行在做一样。

PC寄存器  程序计数器 代码执行到哪里了  程序下一条要执行什么。

有没有一种可以在单线程/进程中处理多个事件流的方法呢?一种答案就是IO多路复用。

因此IO多路复用解决的本质问题是在用更少的资源完成更多的事。

IO模型

  •     1、阻塞IO  
  •     2、非阻塞IO  EAGAIN  忙等待 errno
  •     3、信号驱动IO  SIGIO 用的相对少(了解)
  •     4、并行模型 进程,线程
  •     5, IO多路复用  select、poll、epoll

1、阻塞IO ===》最常用 默认设置

以管道读写为例子:
 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>int main(int argc, const char *argv[])
{int ret = mkfifo("fifo",0666);if(-1 == ret){if(EEXIST == errno){}else{perror("mkfifo error");exit(1);}}int fd = open("fifo",O_RDONLY);if(-1 == fd){perror("open error");exit(1);}while(1){char buf[100] = {0};read(fd,buf,sizeof(buf));printf("fifo:%s\n",buf);bzero(buf,sizeof(buf));fgets(buf,sizeof(buf),stdin);printf("terminal:%s",buf);fflush(stdout);}close(fd);return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
int main(int argc, const char *argv[])
{int ret = mkfifo("fifo",0666);if(-1 == ret){if(EEXIST == errno){}else{perror("mkfifo failed");exit(1);}}int fd = open("fifo", O_WRONLY);if(-1 == fd){perror("open failed");exit(1);}while(1){char buf[100] = "hello,this is fifo test";write(fd,buf,strlen(buf));sleep(3);}close(fd);return 0;
}

2、非阻塞IO ===》在阻塞IO的基础上调整其为不再阻塞等待。


     在程序执行阶段调整文件的执行方式为非阻塞:
            ===》fcntl() ===>动态调整文件的阻塞属性

fcntl()

#include <unistd.h>
    #include <fcntl.h>
 

   int fcntl(int fd, int cmd, ... /* arg */ );


    功能:修改指定文件的属性信息。
    参数:fd 要调整的文件描述符
          cmd 要调整的文件属性宏名称
          ... 可变长的属性值参数。
    返回值:成功  不一定,看cmd
                  失败  -1;

int flag = fcntl(fd,F_GETFL);

fd 是要修改的文件描述符,可能是打开的文件、套接字或其他I/O设备。F_GETFL 是一个预定义的常量,告诉 fcntl 函数获取与文件描述符关联的标志。

fcntl(fd,F_SETFL ,flag | O_NONBLOCK );

 F_SETFL 常量用于设置文件描述符的标志。flag | O_NONBLOCK 是将获取到的当前标志与 O_NONBLOCK 常量进行按位或操作,目的是在现有标志的基础上添加非阻塞标志。

//noblock_fifo_r.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
int main(int argc, const char *argv[])
{int ret = mkfifo("fifo",0666);if(-1 == ret){if(EEXIST == errno){}else{perror("mkfifo error");exit(1);}}int fd = open("fifo",O_RDONLY);if(-1 == fd){perror("open error");exit(1);}int flag = fcntl(fd,F_GETFL);//获取当前文件描述符的标志:fcntl(fd,F_SETFL,flag|O_NONBLOCK);//设置文件描述符为非阻塞模式:flag = fcntl(0,F_GETFL);// 0 是标准输入的文件描述符 stdinfcntl(0,F_SETFL,flag|O_NONBLOCK);while(1){char buf[100] = {0};if(read(fd,buf,sizeof(buf))>0){printf("fifo:%s\n",buf);}bzero(buf,sizeof(buf));if(fgets(buf,sizeof(buf),stdin)){	printf("terminal:%s",buf);fflush(stdout);}}close(fd);return 0;
}
//noblock_fifo_w.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
int main(int argc, const char *argv[])
{int ret = mkfifo("fifo",0666);if(-1 == ret){if(EEXIST == errno){}else{perror("mkfifo failed");exit(1);}}int fd = open("fifo", O_WRONLY);if(-1 == fd){perror("open failed");exit(1);}while(1){char buf[100] = "hello,this is fifo test";write(fd,buf,strlen(buf));sleep(3);}close(fd);return 0;
}

TCP服务器和客户端的收发计数。

头文件:

#ifndef __HEAD_H__
#define __HEAD_H__#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#endif

客户端:

#include "head.h"int CreateTcpClient(char *pip,int port)
{int ret = 0;int sockfd = 0;struct sockaddr_in seraddr;sockfd = socket(AF_INET,SOCK_STREAM,0);if(-1 == sockfd){perror("fail to socket");return -1;}seraddr.sin_family = AF_INET;seraddr.sin_port = htons(port);seraddr.sin_addr.s_addr = inet_addr(pip);ret = connect(sockfd,(struct sockaddr * )&seraddr,sizeof(seraddr));if(-1 == ret){perror("fail to connect");return -1;}return sockfd;
}int main(int argc, const char *argv[])
{int sockfd = 0;char tmpbuff[4096] = {0};int cnt = 0;ssize_t nsize = 0;sockfd = CreateTcpClient("192.168.95.131",50000);while(1){memset(tmpbuff,0,sizeof(tmpbuff));sprintf(tmpbuff,"cnt-----%d",cnt);cnt++;nsize = send(sockfd,tmpbuff,strlen(tmpbuff),0);if(-1 == nsize){perror("fail to send");return -1;}memset(tmpbuff,0,sizeof(tmpbuff));nsize = recv(sockfd,tmpbuff,sizeof(tmpbuff),0);if(nsize == 0){printf("be colsed!\n");return-1;}if(-1 == nsize){perror("fail to recv");return -1;}printf("RECV:%s\n",tmpbuff);}close(sockfd);return 0;
}

服务器:

#include"head.h"int CreateListenSocket(char *pip,int port)
{int ret = 0;int sockfd = 0;struct sockaddr_in seraddr;sockfd = socket(AF_INET,SOCK_STREAM,0);if(-1 == sockfd){perror("fail to socket");return -1;}seraddr.sin_family = AF_INET;seraddr.sin_port = htons(port);seraddr.sin_addr.s_addr = inet_addr(pip);ret = bind(sockfd,(struct sockaddr*)&seraddr,sizeof(seraddr));if(-1 == ret){perror("fail to bind");return -1;}ret = listen(sockfd,10);if(-1 == ret){perror("fail to listen");return -1;}return sockfd;
}int HandleTcpClient(int confd)
{char tmpbuff[4096] = {0};ssize_t nsize = 0;while(1){memset(tmpbuff,0,sizeof(tmpbuff));nsize = recv(confd,tmpbuff,sizeof(tmpbuff),0);if(-1 == nsize){perror("fail to recv");return -1;}else if(0 == nsize){return 0;}printf("%s\n",tmpbuff);sprintf(tmpbuff,"%s---echo",tmpbuff);nsize = send(confd,tmpbuff,strlen(tmpbuff),0);if(-1 == nsize){perror("faill to send");return -1;}}return nsize;
}int main(int argc, const char *argv[])
{int sockfd = 0;int confd = 0;sockfd = CreateListenSocket("192.168.95.131",50000);while(1){confd = accept(sockfd,NULL,NULL);if(-1 == confd){perror("fial to accept");return -1;}HandleTcpClient(confd);}close(confd);close(sockfd);return 0;
}

TCP并发模型  

        在服务器端如何高效地处理多个TCP连接,以实现高并发性能。

为什么 TCP需要并发模型而UDP不需要?

tcp是面向连接的,其中的accept()和recv()如果未收到数据都是会阻塞。

TCP:

1. 面向连接:TCP是面向连接的协议,意味着在数据传输开始前,需要先建立连接,这通常涉及到三次握手的过程。每个TCP连接都是独立的,需要单独管理。

2. 可靠性:TCP提供了可靠的数据传输,它确保数据按顺序到达,且不会丢失。这通过序列号、确认应答、重传机制和流量控制等机制实现。

3. 拥塞控制:TCP具有拥塞控制机制,以防止网络拥塞。当网络拥堵时,TCP会减慢数据发送速率。

UDP:由于UDP的无连接和不可靠特性,通常不需要像TCP那样精细的并发模型。当一个UDP服务器需要处理多个客户端时,它可以简单地接收数据报,处理后回应,而不需要维护每个连接的状态。因此,即使在处理大量并发请求时,UDP服务器的实现通常也较为简单,可能只需要一个线程或进程来处理所有数据报。

1.TCP多进程、多线程模型:

        多进程模型(Multi-process Model): 在这种模型中,每当一个新的连接请求到来时,服务器就会创建一个新的进程来处理这个连接。每个进程负责一个或多个连接,处理完后退出。这种方法的优点是每个连接都有独立的地址空间,错误不会互相影响,但是创建进程的开销较大,而且每个进程都需要消耗一定的系统资源,如内存和文件描述符。

     多线程模型(Multi-threaded Model): 与多进程模型类似,但使用线程代替进程。当新的连接请求到达时,服务器会在现有的线程池中选择一个线程来处理这个连接。线程的切换和上下文切换开销比进程要小,因此可以支持更高的并发数。但是线程共享相同的地址空间,所以需要小心处理线程安全问题。

    缺点:多进程和多线程的创建会带来资源开销,能够实现的并发量比较有限 。

 逻辑控制流在时间上的重叠叫做 并发。

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

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

相关文章

通过POST请求往Elastic批量插入数据

文章目录 引言I 请求文档请求参数请求例子引言 调试工具:Apifox 需求: 向Elasticsearch中的’test_index’索引批量插入文档 情况认证: Basic Auth 在 Header 添加参数 Authorization,其值为在 Basic 之后拼接空格,以及经过 Base64 编码的 {{Username}}:{{Password}} 示…

H3CNE(STP)

8.1 二层环路与STP的介绍 8.1.1 二层环路 8.1.2 冲突域 8.1.3 二层环路带来的问题 8.1.4 STP的基本概念&#xff1a;桥ID 8.1.5 STP的基本概念&#xff1a;根桥 8.1.6 STP的基本概念&#xff1a;Cost 8.1.7 STP的基本概念&#xff1a;Port ID 8.1.8 STP的基本概念&#xff1a;…

Ubuntu22.04下 MySQL8创建并使用存储过程

在Ubuntu下的MySQL 8中创建并使用存储过程&#xff1a; 使用mysql命令登录到MySQL服务器&#xff0c;例如&#xff1a; mysql -u root -p输入root用户的密码。 选择你想要创建存储过程的数据库&#xff0c;例如&#xff1a; CREATE DATABASE mydb;USE mydb;CREATE TABLE us…

pikachu Fileinclusion(local)

随便选择一个都试试 发现url上数字会变 发现文件名确实是file1.php~file5.php 那么会不会还有别的burp抓包选中数字 设置6-100的爆破 strat attack 678异常还有个100也是 先改一下试试看 其他的会报错 但是通过这我们可以得到路径 先写一个 下一步 读取系统文件 windows系统肯定…

【unity 新手教程 001/100】安装与窗口布局介绍

欢迎关注 、订阅专栏 【unity 新手教程】谢谢你的支持&#xff01;&#x1f49c;&#x1f49c; Unity下载与安装 &#x1f449;点击跳转详细图文步骤&#xff1a;Unity Hub Unity 编辑器 窗口布局&#xff1a; Hierarchy: 层级窗口 | 默认 Sample Scene (main camera、direc…

矩阵分析——线性积分方程组的矩阵解法研究

矩阵分析——线性积分方程组的矩阵解法研究 前言线性积分方程组的矩阵解法研究 前言 “矩阵分析”是一门选修课&#xff0c;当时选这门课程的原因是想着图像处理就涉及到很多矩阵运算。但没想到的是这门课程吧虽然是选修&#xff0c;最后的结课要求是让我们写一篇“论文”&…

.dat 文件如何查看内容

第一种通过hexdump -C hexdump -C potk-00199D-0435-20000201.dat 命令查看 第二种 vscode &#xff0c;将文件拖到vscode 工作区,选中要显示的乱码&#xff0c;点击右上角HEX图标&#xff0c;如下面两张图示

UM980高精度RTK定位模块支持哪些通信接口?

注 产品参数信息请以和芯星通最新版官方手册为准。

python-NLP:1中文分词

文章目录 规则分词正向最大匹配法逆向最大匹配法双向最大匹配法 统计分词语言模型HMM模型 jieba分词分词关键词提取词性标注 规则分词 基于规则的分词是一种机械分词方法&#xff0c;主要是通过维护词典&#xff0c;在切分语句时&#xff0c;将语句的每个字符串与词表中的词进行…

Java代码基础算法练习-字符串分类统计-2024.07.24

任务描述&#xff1a; 输入一行字符&#xff08;字符串长度不超过255&#xff09;&#xff0c;分别统计出其中英文字母、数字、空格和其他 字符的个数。&#xff08;提示&#xff0c;空格ASCALL码值为32&#xff09; 解决思路&#xff1a; 输入一字符串&#xff0c;先判断是否…

对递归的一些理解。力扣206题:翻转链表

今天在刷力扣的时候&#xff0c;在写一道翻转链表的题目的过程中&#xff0c;在尝试使用递归解决该问题的时候&#xff0c;第一版代码却每次都返回的是null&#xff0c;这个错误让我尝试去debug了一下&#xff0c;最终找出了问题&#xff0c;并且让我对递归有了一些更深的理解&…

顶级电子合同平台推荐:2024年精选

本文将介绍以下10款工具&#xff1a;e签宝、法大大、上上签、金格签约、契约锁、DocuSign、Concord、PandaDoc、Agiloft、Evisort。 在当今数字化时代&#xff0c;选择一个合适的电子合同平台可能会让人感到困惑&#xff0c;如何判断哪个平台最适合企业的需求&#xff1f;电子合…

Java之数组应用-选择排序-插入排序

已经完全掌握了冒泡排序和二分查找的同学&#xff0c;可以自己尝试学习选择、插入排序。不要求今天全部掌握&#xff0c;最近2-3天掌握即可&#xff01; 1 选择排序 选择排序(Selection Sort)的原理有点类似插入排序&#xff0c;也分已排序区间和未排序区间。但是选择排序每次…

超低功耗ARM Cortex-M33 TZ MCU STM32WBA54、STM32WBA55:通过提升无线性能实现更出色的用户体验

摘要 STM32WBA54、STM32WBA55产品系列同时支持多种无线标准&#xff0c;包括Bluetooth低功耗 5.4&#xff08;已认证&#xff09;、Zigbee、Thread以及可用作Thread边界路由器的Matter。 该产品系列具有出色的灵活性和更强的安全性&#xff0c;可帮助开发人员应对不断变化的无…

24暑假算法刷题 | Day21 | LeetCode 669. 修剪二叉搜索树,108. 将有序数组转换为二叉搜索树,538. 把二叉搜索树转换为累加树

目录 669. 修剪二叉搜索树题目描述题解 108. 将有序数组转换为二叉搜索树题目描述题解 538. 把二叉搜索树转换为累加树题目描述题解 669. 修剪二叉搜索树 点此跳转题目链接 题目描述 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪…

vite环境下使用bootstrap

环境 nodejs 18 pnpm 初始化 pnpm init pnpm add -D vite --registry http://registry.npm.taobao.org pnpm add bootstrap popperjs/core --registry http://registry.npm.taobao.org pnpm add -D sass --registry http://registry.npm.taobao.org新建vite.config.js cons…

用三行代码“偷袭白嫖党”,保护自己的代码(多语言实现)

计算机科学专业的同学们最近遇到了一件烦心事。教授布置了一系列具有挑战性的编程作业&#xff0c;需要大家运用所学知识来完成。然而&#xff0c;一些同学却动起了歪脑筋&#xff0c;想要通过抄袭他人的代码来轻松完成任务。 小李、小王和小张是同宿舍的好友&#xff0c;他们…

四、GD32 MCU 常见外设介绍(1)RCU 时钟介绍

系统架构 1.RCU 时钟介绍 众所周知&#xff0c;时钟是MCU能正常运行的基本条件&#xff0c;就好比心跳或脉搏&#xff0c;为所有的工作单元提供时间 基数。时钟控制单元提供了一系列频率的时钟功能&#xff0c;包括多个内部RC振荡器时钟(IRC)、一个外部 高速晶体振荡器时钟(H…

如何提升短视频的曝光量和获客效能?云微客来解决

在流量至上的当下&#xff0c;短视频凭借其优势&#xff0c;迅速成为了众多企业获客引流的核心营销手段。进入短视频赛道后&#xff0c;如何提升短视频的曝光量和获客效能&#xff0c;就成为了众多企业亟待解决的焦点。 如果你不想投入大量的广告预算&#xff0c;还想在短视频平…

【Linux】进程间通信及管道详细介绍(上)

前言 本节我们开始学习进程间通信相关的知识&#xff0c;并详细探讨一下管道&#xff0c;学习匿名管道和命名管道的原理和代码实现等相关操作… 目录 1. 进程间通信背景1.1 进程通信的目的&#xff1a; 2 管道的引入&#xff1a;2.1 匿名管道&#xff1a;2.1.1 匿名管道的原理&…