多路复用IO、TCP并发模型

news2024/9/21 10:47:51

时分复用

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 是标准输入的文件描述符 stdin
	fcntl(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.coloradmin.cn/o/1945310.html

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

相关文章

群辉NAS利用AList搭建混合云盘①套件安装及百度云盘挂载

目录 一、群辉NAS准备 二、远程访问 三、安装套件 四、挂载公有云盘 1、挂载百度网盘 ……(未完待续) 公有云盘是由云服务提供商运营,向广大用户提供数据存储和文件共享服务的一种在线存储解决方案。 其优点包括: 1. 方便易用:用户可以通过互联网随时随地访问自己存…

Java企业微信服务商代开发获取AccessToken示例

这里主要针对的是企业微信服务商代开发模式 文档地址 可以看到里面大致有三种token&#xff0c;一个是服务商的token&#xff0c;一个是企业授权token&#xff0c;还有一个是应用的token 这里面主要有下面几个参数 首先是服务商的 corpid 和 provider_secret &#xff0c;这个可…

等保定级指南(PPT原件)

新版网络安全等级保护定级指南网络安全等级保护工作的作用对象&#xff0c;主要包括基础信息网络、工业控制系统、云计算平台、物联网、使用移动互联技术的网络和大数据等。 软件全套精华资料包清单部分文件列表&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xf…

前端自动化测试(一):揭秘自动化测试秘诀

目录 [TOC](目录)前言自动化测试 VS 手动测试测试分类何为单元测试单元测试的优缺点优点缺点 测试案例测试代码 测试函数的封装实现 expect 方法实现 test 函数结语 正文开始 &#xff0c; 如果觉得文章对您有帮助&#xff0c;请帮我三连订阅&#xff0c;谢谢&#x1f496;&…

7月投稿警惕!6本On Hold期刊被数据库剔除!

本周投稿推荐 SCI&EI • 1区计算机类&#xff0c;3.5-4.0&#xff08;1个月录用&#xff09; • CCF推荐&#xff0c;1区-Top&#xff08;3天初审&#xff09; EI • 各领域沾边均可&#xff08;2天录用&#xff09; 知网&#xff08;CNKI&#xff09;、谷歌学术 •…

nginx的配置和使用

一、nginx支持win和linux版本的下载&#xff0c;选择合适的版本进行安装 二、配置文件注解 重点的几个参数进行注释&#xff1a; 1、listen 要监听的服务的端口&#xff0c;符合这个端口的才会被监听 server_name要监听的服务地址&#xff0c;可能是ip,也可能是域名&#xf…

流量书单,互联网营销必读

《流量池》杨飞 《增长黑客》肖恩埃利斯(Sean Ellis)、摩根布朗(Morgan Brown) 《增长五线》王赛 《参与感》黎万强 《场景革命》吴声 《网络营销实战密码》昝辉 《网络营销推广实战宝典》江礼坤 《超级IP&#xff0c;互联网新物种方法论》吴声 《周鸿祎自述&#xff0…

Golden Software Surfer v25 解锁版下载与安装教程 (三维绘图软件)

前言 Golden Software Surfer 是一款三维绘图软件&#xff0c;具备强大的插值功能和绘制图件能力&#xff0c;可用来处理XYZ数据&#xff0c;轻松绘制专业三维图。该软件有着很直观的用户界面&#xff0c;尽管不支持中文&#xff0c;但是很用户在熟悉流程以后依然能够轻松学会…

《计算机网络》(学习笔记)

目录 一、计算机网络体系结构 1.1 计算机网络概述 1.1.1 计算机网络的概念 1.1.2 计算机网络的组成 1.1.3 计算机网络的功能 1.1.4 电流交换、报文交换和分组交换 1.1.5 计算机网络的分类 1.1.6 计算机网络的性能指标 1.2 计算机网络体系结构与参考模型 1.2.1 计算机…

SpringCloud Nacos的配置与使用

Spring Cloud Nacos的配置与使用 文章目录 Spring Cloud Nacos的配置与使用1. 简单介绍2. 环境搭建3. 服务注册/服务发现4. Nacos 负载均衡4.1 服务下线4.2 权重配置4.3 同集群优先访问 5. Nacos 健康检查5.1 两种健康检查机制5.2 服务实例类型 6.Nacos 环境隔离6.1 创建namesp…

QT开发笔记:常用控件

常用控件&#xff1a; 站在巨人的肩膀上&#xff0c;Qt中已经提供了大量的内置控件&#xff08;按钮、文本框、单选按钮、复选按钮、下拉框&#xff09; 可直接学习其特性使用方法使用。 控件 Widget 界面上的各种元素&#xff0c;各种部分的统称。 HTML 包含很多标签&#…

解决 Shiro 重复调用 doGetAuthenticationInfo 导致异常处理错误的问题

遇到一个 Shiro 中反复调用 doGetAuthenticationInfo 导致异常没有被成功处理的问题&#xff0c;经过一些源码调试&#xff0c;发现了问题的所在&#xff0c;只需在继承 BasicHttpAuthenticationFilter 的类中重写 onAccessDenied 方法即可。 文章目录 1.问题环境2.问题描述3.问…

【数学 分类讨论】2029. 石子游戏 IX

本文涉及知识 质数、最大公约数、菲蜀定理 LeetCode 2029. 石子游戏 IX Alice 和 Bob 再次设计了一款新的石子游戏。现有一行 n 个石子&#xff0c;每个石子都有一个关联的数字表示它的价值。给你一个整数数组 stones &#xff0c;其中 stones[i] 是第 i 个石子的价值。 Ali…

在 Kubernetes 中设置 Pod 优先级及其调度策略详解

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

Gson的基本使用:解析Json格式数据 序列化与反序列化

目录 一&#xff0c;Gson和Json 1&#xff0c;Gson 2&#xff0c;Json 3&#xff0c;Gson处理对象的几个重要点 4&#xff0c;序列化和反序列化 二&#xff0c;Gson的使用 1&#xff0c;Gson的创建 2&#xff0c;简单对象序列化 3&#xff0c;对象序列化&#xff0c;格…

vue学习笔记(十一)——开发心得(axios的封装、promise细节、vue-router开发中的使用)

1. axios的网络请求的封装 1.1 为什么要封装api? 代码分层&#xff0c;便于以后的修改&#xff0c;无需触碰逻辑页面 目标&#xff1a; 网络请求&#xff0c;不散落在各个逻辑页面里&#xff0c;封装起来方便以后修改 1.2 封装api步骤 ① 在项目 src 下新建目录 utlis &am…

海外发稿:打造希腊媒体宣发新局面

随着全球经济一体化的不断深入&#xff0c;企业对于海外市场的拓展需求日益迫切。在这个过程中&#xff0c;媒体宣发作为一种有效的市场推广手段&#xff0c;已经成为企业出海的重要策略之一。希腊&#xff0c;作为欧洲的重要经济体&#xff0c;拥有丰富的文化底蕴和众多的历史…

如何使用Python实现语音转文字/字幕

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 语音转文字/字幕 📒📝 Python实现📝 便捷封装⚓️ 相关链接 ⚓️📖 介绍 📖 想象一下,在观看一部无字幕的电影或者需要快速整理会议录音时,如果有一个魔法工具能瞬间将音频转化为清晰的文字,那该是多么便捷!今天,…

MPU6050三轴传感器

1.背景&#xff1a; MPU6050 是由 InvenSense&#xff08;现为 TDK 旗下公司&#xff09;生产的一款集成了三轴加速度计和三轴陀螺仪的微机电系统&#xff08;MEMS&#xff09;传感器。它可以测量物体在三个轴上的加速度和旋转角速度&#xff0c;被广泛应用于消费电子、工业控制…

微前端--single-spa

微前端 使用微前端的挑战&#xff1a; 子应用切换&#xff0c;应用相互隔离&#xff0c;互补干扰&#xff0c;子应用之前的通信&#xff0c;多个子应用并存&#xff0c;用户状态的存储&#xff0c;免登。 常用技术方案 路由分发式微前端 通过http服务的反向代理 http {serv…