网络编程第二课

预备知识

1、操作系统的用户态和内核态:

用户态指的是用户自己定义工作空间,自己申请变量、定义函数的操作。

内核态指把一些工作交给操作系统去玩成,用户本身看不到执行过程,只能获取操作系统最后执行完成的结果。其中,在网络编程中,accept recv send等函数均在内核态实现的。

计算机之间的通信,就是内核态在操作系统里实现的,用户本身是感觉不到的,除非使用ping等函数,能查到和其他计算机进程通信的结果。

所以,当有计算机远程连接或传送数据时,操作系统内核首先建立连接,并且开辟一段缓冲区把对方的信息放入缓冲区。如果需要用户去读取,则需要通过特定函数(listen,accept)将首先得到该连接在用户态层面的地址,再使用(recv)函数将存在于内核去的数据读取出来。

阻塞状态:如何内核函数没有相应的结果,会阻塞在这个状态,直到有新的结果出现才会返回。

2、实现tcp server

IO复用

select函数

select函数 也是一个内核态的函数,调用该函数可以使操作系统内核检查其中建立的连接,并把结果进行返回。

//需要首先初始化两个数组
fd_set fdset, rset;
FD_ZERO(&fdset);
//后续需要对fdset数组进行监听,所以需要提前将该数组初始化,即把bind的文件描述符放入其中
FD_SET(servfd, &fdset);int nready = select(maxfd + 1, &rset, NULL, NULL, NULL);

当调用了select函数后,内核就会遍历这个数组,挨个判断这个数组里的文件描述符是否达到可读状态,如果达到可读状态,就把对应文件描述符放入rset数组中,随后用户就可以对rset里的文件进行相应的操作。

注:什么是可读状态?

对于bind端口的文件描述符而言,内核监听到有新的连接建立,就是可读了;

对于普通文件描述符,内核监听到缓冲区有数据了,就是可读状态。

因此在调用select函数后,需要写两段逻辑,分别处理这两种可读状态:

int mready = select(maxfd + 1, &rset, NULL, NULL, NULL);//第一段逻辑,处理是否有新的连接建立,如果有新的建立,就拿到文件描述符//加入到fd_set数组中,参与下一轮的监听if(FD_ISSET(servfd, &rset)){    //acceptint clientfd = accept(servfd, (struct sockaddr*)&clientaddr, &client_len);printf("%d had connected!\n", clientfd);FD_SET(clientfd, &fdset);if(clientfd > maxfd){maxfd = clientfd;}}//第二段逻辑,处理是否有数据发送过来。//如果有,就把数据缓冲区的内容读取出来for(int i = servfd + 1; i <= maxfd; i++){if(FD_ISSET(i, &rset)){char buffer[1024] = {0};int count = recv(i, buffer, 1024, 0);if(count == 0){printf("%d disconnected!\n", i);close(i);FD_CLR(i, &fdset);continue;}printf("receive: %s \n", buffer);send(i, buffer, count, 0);}   }

poll函数

select的弊端:fd_set这个数组的大小是固定的,对于高并发而言效率过低。

select参数过多,监听可读需要一个数组,监听可写需要一个数组,监听错误也需要一个数组,拷 贝到内核态的东西太多了。

poll基于select,建立了新的数组,其实是新的结构体数组,结构体(数组)如下:

相较于select的数组而言,该结构体不只是包含了文件描述符,还还包含了想要监听的事件状态(events)所以可以同时做到监听可读、可写、错误等,这样就避免了select使用过多的数组作为参数传给内核。

//初始化fd数组,准备把该数组传给poll
struct pollfd fds[1024] = {0};
fds[servfd].fd = servfd;
fds[servfd].events = POLLIN;int nready = poll(fds, maxfd + 1, -1);

调用poll函数后,操作系统内核同样会遍历这个数组,根据数组中events中所想要监听的状态判断是否就绪,把结果写入revents中返回,用户态中用户看到基于revents中的结果进行相应操作。

int mready = poll(fds, maxfd + 1, -1);
//对于可读状态,同样要写两端逻辑
//第一段逻辑,判断是否是bind端口的文件描述符,如果是,表明有新的连接,需要加入fd数组
if(fds[servfd].revents & POLLIN){int clientfd = accept(servfd, (struct sockaddr*)&clientaddr, &client_len);printf("%d had connected!\n", clientfd);fds[clientfd].fd = clientfd;fds[clientfd].events = POLLIN;if(clientfd > maxfd){maxfd = clientfd;}
}
//第二段逻辑,判断新的连接是否可读,即缓冲区有无内容。
for(int i = servfd + 1; i <= maxfd; i++){if(fds[i].revents & POLLIN){char buffer[1024] = {0};int count = recv(i, buffer, 1024, 0);if(count == 0){printf("%d disconnected!\n", i);close(i);fds[i].fd = -1;fds[i].events = 0;continue;}printf("receive: %s \n", buffer);send(i, buffer, count, 0);}
}

epoll函数

poll的弊端:内核还是会对传入数组进行遍历,判断每个fd是否是所需要的状态,还是会有拷贝和循环的过程。

epoll和select和poll相比,省略了传入新建fd数组并拷贝的环节,而是直接在内核中构造了该数组。

//在内核中构造了一个数组,用来存放监听到的文件描述符,可供用户态直接使用
int epollfd = epoll_create(1);//创建好了之后需要初始化该数组,即把bind端口的fd放入该数组中
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = servfd;
epoll_ctl(epollfd, EPOLL_CTL_ADD, servfd, &ev);

之后会调用epoll_wait函数,内核会把已经就绪的文件描述符返回,用户只需要新建一个数组用来接收就可以了

//在用户态建立数组用于接收返回的就绪的文件描述符
struct epoll_event events[1024];
int mready = epoll_wait(epollfd, events, 1024, -1);
//直接对该数组进行遍历
for(int i = 0; i < mready; i++){//同样是两段代码逻辑,一个是判段是否是有新的连接,一个是判断是否有新数据到达if(events[i].data.fd == servfd){int clientfd = accept(servfd, (struct sockaddr*)&clientaddr, &client_len);printf("%d had connected!\n", clientfd);ev.events = EPOLLIN;ev.data.fd = clientfd;epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &ev);}else if(events[i].events & EPOLLIN){char buffer[1024] = {0};int count = recv(events[i].data.fd, buffer, 1024, 0);if(count == 0){printf("%d disconnected!\n", events[i].data.fd);close(events[i].data.fd);epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, NULL);continue;}printf("receive: %s \n", buffer);send(events[i].data.fd, buffer, count, 0);}
}

课程地址:https://github.com/0voice

总结

io多路复用是面试中常被问到的,之前没有接触也只是笼统的被八股,至于具体内容是什么,原理怎么样并不了解,所以深入问的话是容易露馅的。这次的学习,对io复用有了较深的理解,起码不会像之前那样纯背八股那样停留表面了,起码在操作系统、网络这两个层面有了更进一步的认知。同时不得不感叹,终于有一种把之前学习到的知识串联起来的感觉。之前os是os,计网是计网,考试就是学习对应的知识点应付。这一节通过具体的代码,相当于把这两个进行了连接,有了更立体的认识。如果能有今天这样的认识再回到两个月前参加秋招,别的不提,起码io复用这一块再背问道也会有很大的信心了。还清晰的记得,参加第一场面试被问到io多路复用有了解吗?我竟然回答不是很了解,那么现在我也是直到为啥给我挂了!(哭笑)不过好在什么时候开始都不算晚,知道了哪里不足哪里有欠缺也是一种提升。

果然还是要理论+实践,空背八股会被淘汰,只有代码也很难拿到高薪呀!继续努力吧!

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

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

相关文章

Python酷库之旅-第三方库Pandas(252)

目录 一、用法精讲 1191、pandas.tseries.offsets.BusinessMonthBegin.n属性 1191-1、语法 1191-2、参数 1191-3、功能 1191-4、返回值 1191-5、说明 1191-6、用法 1191-6-1、数据准备 1191-6-2、代码示例 1191-6-3、结果输出 1192、pandas.tseries.offsets.Busine…

IO流之文件

1. 文件流 2. 常用文件操作 2.1 文件创建 方式1 new File(String pathname) // 根据路径创建一个File对象 方式2 new File(File parent,String child) //根据父目录文件子路径构建 方式3 new File(String parent,String child) //根据父目录子路径构建 package com.hspedu.fil…

【从零开始的LeetCode-算法】35. 搜索插入位置

给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2示例 2: 输入: …

C++(4个类型转换)

1. C语言中的类型转换 1. 隐式 类型转换&#xff1a; 具有相近的类型才能进行互相转换&#xff0c;如&#xff1a;int,char,double都表示数值。 2. 强制类型转换&#xff1a;能隐式类型转换就能强制类型转换&#xff0c;隐式类型之间的转换类型强相关&#xff0c;强制类型转换…

深度学习7 梯度下降优化、过拟合、手机价格预测

三、BP算法 3、梯度下降 w w - lr * grad&#xff1a; w 表示权重&#xff0c;lr表示学习率&#xff0c;grad表示梯度 传统下降方式分三类&#xff1a;&#xff08;BGD&#xff09;批量梯度下降、&#xff08;MBGD&#xff09;小批量梯度下降、&#xff08;SGD&#xff09;随…

计算机的错误计算(一百七十二)

摘要 探讨 MATLAB 对于算式 的计算误差。 例1. 在 MATLAB 中计算 的值。 直接贴图吧&#xff1a; 这样&#xff0c;MATLAB 的输出中只有3位正确数字&#xff0c;有效数字的错误率为 (16-3)/16 81.25% . 因为16位的正确输出为 0.2971242332737277e-18&#xff08;ISReals…

手撸了一个文件传输工具

在日常的开发与运维中&#xff0c;文件传输工具是不可或缺的利器。无论是跨服务器传递配置文件&#xff0c;还是快速从一台机器下载日志文件&#xff0c;一个高效、可靠且简单的文件传输工具能够显著提高工作效率。今天&#xff0c;我想分享我自己手撸一个文件传输工具的全过程…

新手参加2025年CTF大赛——Web题目的基本解题流程

CTF&#xff08;Capture the Flag&#xff09;是网络安全比赛中的一种常见形式&#xff0c;参赛者需要通过破解题目、发现漏洞并获取flag&#xff08;标志&#xff09;来获得分数。 这些问题涉及多个领域&#xff0c;如逆向工程、Web安全、密码学、二进制漏洞、取证分析等。CTF…

1Panel 自建邮局 - Docker Mailserver

本文首发于 Anyeの小站&#xff0c;点击链接 访问体验更佳 前言 首先发一段劝退说辞&#xff1a;我相信点进本文的人自建邮局的目的更多地是为了能用自己的域名邮箱&#xff0c;收发邮件&#xff1f; 仅收不发&#xff0c;推荐使用 https://www.cloudflare.com/zh-cn/develop…

【Maven】功能和核心概念

1. 什么是Maven 1.1 Maven的概念 Maven 是 Apache 软件基金会组织维护的一款自动化构建工具&#xff0c;专注服务于 Java 平台的项目构建和依赖管理。 1.2 为什么要使用Maven&#xff1f; 在项目开发中&#xff0c;我们需要引用各种 jar 包&#xff0c;引用的 jar 包可能有…

企业网站面临的爬虫攻击及安全防护策略

在当今数字化时代&#xff0c;企业网站不仅是展示企业形象的窗口&#xff0c;更是进行商业活动的重要平台。然而&#xff0c;企业网站在日常运营中面临着多种类型的爬虫攻击&#xff0c;这些攻击不仅会对网站的正常访问造成影响&#xff0c;还可能窃取敏感数据&#xff0c;给企…

vue3 + vite + antdv 项目中自定义图标

前言&#xff1a; 去iconfont-阿里巴巴矢量图标库 下载自己需要的icon图标&#xff0c;下载格式为svg&#xff1b;项目中在存放静态资源的文件夹下 assets 创建一个存放svg格式的图片的文件夹。 步骤&#xff1a; 1、安装vite-plugin-svg-icons npm i vite-plugin-svg-icons …

安装SQL Server 2022提示需要Microsoft .NET Framework 4.7.2 或更高版本

安装SQL Server 2022提示需要Microsoft .NET Framework 4.7.2 或更高版本。 原因是&#xff1a;当前操作系统版本为Windows Server 2016 Standard版本&#xff0c;其自带的Microsoft .NET Framework 版本为4.6太低&#xff0c;不满足要求。 根据报错的提示&#xff0c;点击链接…

基于yolov8、yolov5的铝材缺陷检测识别系统(含UI界面、训练好的模型、Python代码、数据集)

摘要&#xff1a;铝材缺陷检测在现代工业生产和质量管理中具有重要意义&#xff0c;不仅能帮助企业实时监控铝材质量&#xff0c;还为智能化生产系统提供了可靠的数据支撑。本文介绍了一款基于YOLOv8、YOLOv5等深度学习框架的铝材缺陷检测模型&#xff0c;该模型使用了大量包含…

Goland或Idea启动报错

Goland或Idea启动不了 报错如图&#xff1a; 原因&#xff1a;破解导致 解决方案 环境变量中有关Goland的全部删除

ceph手动部署

ceph手动部署 一、 节点规划 主机名IP地址角色ceph01.example.com172.18.0.10/24mon、mgr、osd、mds、rgwceph02.example.com172.18.0.20/24mon、mgr、osd、mds、rgwceph03.example.com172.18.0.30/24mon、mgr、osd、mds、rgw 操作系统版本&#xff1a; Rocky Linux release …

PMP–一、二、三模、冲刺–分类–8.质量管理

文章目录 技巧五、质量管理 一模8.质量管理--质量管理计划--质量管理计划包括项目采用的质量标准&#xff0c;到底有没有满足质量需求&#xff0c;看质量标准即可。6、 [单选] 自项目开始以来&#xff0c;作为项目经理同事的职能经理一直公开反对该项目&#xff0c;在讨论项目里…

C语言——库函数

常用的函数 https://cplusplus.com/reference/ 没事儿多看看 1 数学函数 #include <math.h> #include <stdio.h> int main() {printf("%lf\n", sqrt(4));//开平方根——>double类型printf("%lf\n", pow(2, 10));//求几次方的——>do…

纯Go语言开发人脸检测、瞳孔/眼睛定位与面部特征检测插件-助力GoFly快速开发框架

前言​ 开发纯go插件的原因是因为目前 Go 生态系统中几乎所有现有的人脸检测解决方案都是纯粹绑定到一些 C/C 库&#xff0c;如 ​​OpenCV​​ 或 ​​​dlib​​​&#xff0c;但通过 ​​​cgo​​​ 调用 C 程序会引入巨大的延迟&#xff0c;并在性能方面产生显著的权衡。…

27 基于51单片机的方向盘模拟系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STC89C52单片机&#xff0c;采用两个MPX4115压力传感器作为两路压力到位开关电路&#xff0c; 采用滑动变阻器连接数模转换器模拟重力加速度传感器电路&#xff1b; 一个按键控制LED灯的点亮与…