《后台开发:核心技术与应用实践》第七章网络IO模型

2024-06-08 07:38

本文主要是介绍《后台开发:核心技术与应用实践》第七章网络IO模型,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、基础知识
    • 1.IO的两种操作:同步IO和异步IO
    • 2.网络中IO的操作有4种情况:
    • 3.当一个网络IO(eg,read操作),会涉及哪两个系统对象和经历哪两个阶段?
  • 二、四种网络IO模型
    • 1.阻塞IO模型
      • (1)阻塞和非阻塞在于用户进程调用内核IO操作方式下的区别
      • (2)特点
      • (3)几乎所有的IO接口(包括socket接口)都是阻塞型的
      • (4)改进方案:多线程(多进程),线程池,连接池
      • (5)为何一个 socket 可以 accept 多次?
      • (6)总结
    • 2.非阻塞IO模型
    • 3.多路IO复用模型,即事件驱动IO
      • (1)特点
      • (2)多路IO复用和阻塞IO的区别
      • (3)使用select()的效果与非阻塞IO类似
      • (4)select()原型:探测多个文件句柄的状态变化
      • (5)事件驱动模型
    • 4.异步IO模型
      • (1)特点
      • (2)非阻塞IO和异步IO的区别
    • 5.各个 IO 模型的比较
  • 二、select——完成非阻塞方式工作的程序,监视需要被监视的文件描述符的变化情况:读、写或异常
    • 1.select函数原型
    • 2.使用select函数循环读取键盘输入
    • 3.观察 select 超时
    • 3.使用 select 函数提高服务器的处理能力

一、基础知识

1.IO的两种操作:同步IO和异步IO

  • 同步IO:必须等待IO操作完成后,控制权才返回给用户进程
  • 异步IO:无需等待IO操作完成,就将控制权返回给用户进程

2.网络中IO的操作有4种情况:

  • 输入:等待数据到达套接字接收缓冲区
  • 输出:等待套接字发送缓冲区有足够的空间容纳将要发送的数据
  • 服务器接收连接请求:等待新的客户端连接请求的到来
  • 客户端发送连接请求:等待服务器回送客户的发起的SYN所对应的ACK

3.当一个网络IO(eg,read操作),会涉及哪两个系统对象和经历哪两个阶段?

  • 两个系统对象为:
    (1)调用这个IO的进程
    (2)系统内核
  • 两个阶段是:
    (1)等待数据准备
    (2)将数据从内核拷贝到进程(实际是拷贝到内存中)

二、四种网络IO模型

1.阻塞IO模型

(1)阻塞和非阻塞在于用户进程调用内核IO操作方式下的区别

在这里插入图片描述

(2)特点

在这里插入图片描述
在这里插入图片描述

(3)几乎所有的IO接口(包括socket接口)都是阻塞型的

在这里插入图片描述

(4)改进方案:多线程(多进程),线程池,连接池

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(5)为何一个 socket 可以 accept 多次?

在这里插入图片描述

(6)总结

多线程模型可以方便高效的解决小规模的服务请求,但面对大规模的服务请求,多线程模型也会遇到瓶颈,可以用非阻塞模型来尝试解决这个问题。

2.非阻塞IO模型

  • 特点
    在这里插入图片描述
  • recv()函数的不同含义
    在这里插入图片描述
  • 如下的函数可以将某句柄归设为非阻塞状态 :fcntl( fd , F_SETFL , O_NONBLOCK );

3.多路IO复用模型,即事件驱动IO

(1)特点

  • 它的基本原理就是有个函数(eg:select)会不断地轮询所负责的所有socket,当某个socket 有数据到达了,就通知用户进程
  • 多路 IO 复用模型的流程如图 7-3 所示
    在这里插入图片描述

(2)多路IO复用和阻塞IO的区别

在这里插入图片描述

(3)使用select()的效果与非阻塞IO类似

在这里插入图片描述

(4)select()原型:探测多个文件句柄的状态变化

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(5)事件驱动模型

在这里插入图片描述

  • 这种模型的特征在于每一个执行周期都会探测一次或一组事件,一个特定的事件会触发某个特定的响应,这里可以将这种模型归类为“事件驱动模型” 。
  • select()事件驱动模型的优点
    在这里插入图片描述
  • select()事件驱动模型的缺点
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

4.异步IO模型

(1)特点

  • 用户进程发起 read 操作之后,立刻就可以开始去做其他的事;而另一方面,从内核的角度,当它收到一个异步的 read 请求操作之后,首先会立刻返回,所以不会对用户进程产生任何阻塞。
  • 然后,内核会等待数据准备完成,然后将数据拷贝到用户内存中,当这一切都完成之后,内核会给用户进程发送一个信号,返回 read 操作已完成的信息。
    在这里插入图片描述

(2)非阻塞IO和异步IO的区别

  • 非阻塞 IO 在执行 recvfrom 这个系统调用的时候,如果内核的数据没有准备好,这时候不会阻塞进程 。但是当内核中数据准备好时,recvfrom 会将数据从内核拷贝到用户内存中,这个时候进程则被阻塞。
  • 异步 IO 则不一样,当进程发起 IO 操作之后,就直接返回,直到内核发送一个信号,告诉进程 IO 已完成,则在这整个过程中,进程完全没有被阻塞。
    在这里插入图片描述

5.各个 IO 模型的比较

在这里插入图片描述

二、select——完成非阻塞方式工作的程序,监视需要被监视的文件描述符的变化情况:读、写或异常

1.select函数原型

在这里插入图片描述
(1)结构体1: fd_set
在这里插入图片描述
(2)结构体2: timeval
结构体 timeval 是一个常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数 。

(3)select 的各个参数所表示的含义
在这里插入图片描述
在这里插入图片描述

2.使用select函数循环读取键盘输入

#include <sys/time.h>  
#include <stdio.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <assert.h>  
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/select.h>
int main(){int keyboard;  int ret,i;  char c;  fd_set readfd;  struct timeval timeout;keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK); assert(keyboard>0);  while(1){timeout.tv_sec=1; timeout.tv_usec=0; FD_ZERO(&readfd);  //将readfd清零FD_SET(keyboard,&readfd);//将keyboard加入readfdret=select(keyboard+1,&readfd,NULL,NULL,&timeout); if(FD_ISSET(keyboard,&readfd))//如果keyboard在readfd中,则为真 {i=read(keyboard,&c,1);  if('\n'==c)  continue;  printf("The input is %c\n",c);  if ('q'==c)  break;  }}return 0;
}

(1)执行
在这里插入图片描述
在这里插入图片描述
(2)分析

  • open("/dev/tty",O_RDONLY | O_NONBLOCK)
    在这里插入图片描述
  • assert(keyboard>0)
    在这里插入图片描述
    在这里插入图片描述
  • 函数解释
		timeout.tv_sec=1; timeout.tv_usec=0; FD_ZERO(&readfd);  //将readfd清零FD_SET(keyboard,&readfd);//将keyboard加入readfdret=select(keyboard+1,&readfd,NULL,NULL,&timeout); 

在这里插入图片描述

  • 函数解释
		if(FD_ISSET(keyboard,&readfd))//如果keyboard在readfd中,则为真 {i=read(keyboard,&c,1);  if('\n'==c)  continue;  printf("The input is %c\n",c);  if ('q'==c)  break;  }

在这里插入图片描述

3.观察 select 超时

#include <sys/time.h>  
#include <stdio.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <assert.h>  
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/select.h>
int main(){int keyboard; int ret,i; char c;fd_set readfd;struct timeval timeout;keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK);assert(keyboard>0);while(1) {timeout.tv_sec=5;timeout.tv_usec=0;FD_ZERO(&readfd);FD_SET(keyboard,&readfd);ret=select(keyboard+1,&readfd,NULL,NULL,&timeout);if (ret == -1)perror("select error");else if (ret){if(FD_ISSET(keyboard,&readfd)){i=read(keyboard,&c,1);if('\n'==c)continue;printf("hehethe input is %c\n",c);if ('q'==c)break;}  }else if (ret == 0)printf("time out\n");}return 0;
}  

(1)执行
在这里插入图片描述
在这里插入图片描述

(2)解释

	if (ret == -1)perror("select error");else if (ret){if(FD_ISSET(keyboard,&readfd)){i=read(keyboard,&c,1);if('\n'==c)continue;printf("hehethe input is %c\n",c);if ('q'==c)break;}  }else if (ret == 0)printf("time out\n");}

在这里插入图片描述

3.使用 select 函数提高服务器的处理能力

  • server端代码:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
#define DEFAULT_PORT 6666
int main( int argc, char ** argv){int serverfd,acceptfd; /* 监听socket: serverfd,数据传输socket: acceptfd */struct sockaddr_in my_addr; /* 本机地址信息 */struct sockaddr_in their_addr; /* 客户地址信息 */unsigned int sin_size, myport=6666, lisnum=10;if ((serverfd = socket(AF_INET , SOCK_STREAM, 0)) == -1) {perror("socket" );return -1;}printf("socket ok \n");my_addr.sin_family=AF_INET;my_addr.sin_port=htons(DEFAULT_PORT);my_addr.sin_addr.s_addr = INADDR_ANY;bzero(&(my_addr.sin_zero), 0);if (bind(serverfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr )) == -1) {perror("bind" );return -2;}printf("bind ok \n");if (listen(serverfd, lisnum) == -1) {perror("listen" );return -3;}printf("listen ok \n");fd_set client_fdset;	/*监控文件描述符集合*/int maxsock;            /*监控文件描述符中最大的文件号*/struct timeval tv;		/*超时返回时间*/int client_sockfd[5];   /*存放活动的sockfd*/bzero((void*)client_sockfd,sizeof(client_sockfd));int conn_amount = 0;    /*用来记录描述符数量*/maxsock = serverfd;char buffer[1024];int ret=0;while(1){/*初始化文件描述符号到集合*/FD_ZERO(&client_fdset);/*加入服务器描述符*/FD_SET(serverfd,&client_fdset);//把服务器描述符加入到集合中/*设置超时时间*/tv.tv_sec = 30; /*30秒*/tv.tv_usec = 0;/*把活动的句柄加入到文件描述符中*/for(int i = 0; i < 5; ++i){
/*程序中Listen中参数设为5,故i必须小于5*/if(client_sockfd[i] != 0){FD_SET(client_sockfd[i], &client_fdset);}}/*printf("put sockfd in fdset!\n");*//*select函数*/ret = select(maxsock+1, &client_fdset, NULL, NULL, &tv);if(ret < 0){perror("select error!\n");break;}else if(ret == 0){printf("timeout!\n");continue;}/*轮询各个文件描述符*/for(int i = 0; i < conn_amount; ++i){/*FD_ISSET检查client_sockfd是否可读写,>0可读写*/if(FD_ISSET(client_sockfd[i], &client_fdset)){printf("start recv from client[%d]:\n",i);ret = recv(client_sockfd[i], buffer, 1024, 0);if(ret <= 0){printf("client[%d] close\n", i);close(client_sockfd[i]);FD_CLR(client_sockfd[i], &client_fdset);client_sockfd[i] = 0;}else{printf("recv from client[%d] :%s\n", i, buffer);}}}/*检查是否有新的连接,如果有,接收连接,加入到client_sockfd中*/if(FD_ISSET(serverfd, &client_fdset)){/*接受连接*/struct sockaddr_in client_addr;size_t size = sizeof(struct sockaddr_in);int sock_client = accept(serverfd, (struct sockaddr*)(&client_addr), (unsigned int*)(&size));if(sock_client < 0){perror("accept error!\n");continue;}/*把连接加入到文件描述符集合中*/if(conn_amount < 5){client_sockfd[conn_amount++] = sock_client;bzero(buffer,1024);strcpy(buffer, "this is server! welcome!\n");send(sock_client, buffer, 1024, 0);printf("new connection client[%d] %s:%d\n", conn_amount, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));bzero(buffer,sizeof(buffer));ret = recv(sock_client, buffer, 1024, 0);if(ret < 0){perror("recv error!\n");close(serverfd);return -1;}printf("recv : %s\n",buffer);if(sock_client > maxsock){maxsock = sock_client;}else{printf("max connections!!!quit!!\n");break;}}}
}for(int i = 0; i < 5; ++i){if(client_sockfd[i] != 0){close(client_sockfd[i]);}}close(serverfd);return 0;	
}
  • client端代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#define DEFAULT_PORT 6666
int main( int argc, char * argv[]){int connfd = 0;int cLen = 0;struct sockaddr_in client;if(argc < 2){printf(" Uasge: clientent [server IP address]\n");return -1;}	client.sin_family = AF_INET;client.sin_port = htons(DEFAULT_PORT);client.sin_addr.s_addr = inet_addr(argv[1]);connfd = socket(AF_INET, SOCK_STREAM, 0);if(connfd < 0){perror("socket" );return -1;}if(connect(connfd, (struct sockaddr*)&client, sizeof(client)) < 0){perror("connect" );return -1;}char buffer[1024];bzero(buffer,sizeof(buffer));recv(connfd, buffer, 1024, 0);printf("recv : %s\n", buffer);bzero(buffer,sizeof(buffer));strcpy(buffer,"this is client!\n");send(connfd, buffer, 1024, 0);while(1){bzero(buffer,sizeof(buffer));scanf("%s",buffer);int p = strlen(buffer);buffer[p] = '\0';send(connfd, buffer, 1024, 0);printf("i have send buffer\n");}close(connfd);return 0;
}
  • makefile代码
all:server client
server:server.og++ -g -o server server.o
client:client.og++ -g -o client client.o
server.o:server.cppg++ -g -c server.cpp
client.o:client.cppg++ -g -c client.cpp
clean:allrm all

(1)执行
在这里插入图片描述
在这里插入图片描述
(2)分析server代码
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这篇关于《后台开发:核心技术与应用实践》第七章网络IO模型的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1041559

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设