网络编程第二课

预备知识

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,一经查实,立即删除!

相关文章

Spring中@Transactional注解与事务传播机制

文章目录 事务传播机制事务失效的场景 事务传播机制 事务的传播特性指的是 当一个事务方法调用另一个事务方法时&#xff0c;事务方法应该如何执行。 事务传播行为类型外部不存在事务外部存在事务使用方式REQUIRED(默认)开启新的事务融合到外部事务中Transactional(propagati…

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;我想分享我自己手撸一个文件传输工具的全过程…

Linux系统编程之进程创建

概述 在Linux系统中&#xff0c;通过创建新的进程&#xff0c;我们可以实现多任务处理、并发执行和资源隔离等功能。创建进程的主要方法为&#xff1a;fork、vfork、clone。下面&#xff0c;我们将分别进行介绍。 fork fork是最常用的创建新进程的方法。当一个进程调用fork时&a…

【人工智能】用Python实现卷积神经网络(CNN)进行图像分类:从零开始的深度学习教程

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 卷积神经网络(CNN)是处理图像分类任务的核心工具,它通过卷积操作和池化机制提取图像的特征并实现分类。本文将手把手教你如何使用 Python 和深度学习框架(PyTorch)从头实现一个 CNN 模型,应用于图像…

深入探讨NIO

目录 传统阻塞IO 非阻塞IO select() epoll 总结 传统阻塞IO 非阻塞IO IO多路复用select() IO多路复用epoll 传统阻塞IO 在传统的阻塞IO模型中&#xff0c;当一个线程执行到IO操作&#xff08;如读取数据&#xff09;时&#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…

QT-thread2种方式选择的优劣对比

1.第一种方式&#xff1a;使用 QObject 的 moveToThread() QObjectQthread class MessageWriter : public QObject {Q_OBJECT public slots:void writeDataToFile(); };threadMsgExchange new QThread();MessageWriter *writer new MessageWriter();writer->moveToThread…

【Maven】功能和核心概念

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

Go运行Grule引擎实现计费规则管理

Go运行Grule引擎实现计费规则管理 github位置: https://github.com/hyperjumptech/grule-rule-engine # 安装grule模块 go get -u github.com/hyperjumptech/grule-rule-engineGrule的示例代码 示例位置: https://github.com/hyperjumptech/grule-rule-engine/tree/master/e…

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

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

Hive on Spark 的Pre-commit 测试

什么是 Pre-Commit 测试&#xff1f; Pre-Commit 测试是一种提交代码到主分支或共享代码库之前运行的一系列自动化测试&#xff0c;用于捕获代码中的潜在问题自动运行的测试流程。其目的是确保新提交的代码不会引入错误&#xff0c;破坏现有功能或降低代码质量。对于大型项目如…

android shader gl_Position是几个分量

在Android的OpenGL ES中&#xff0c;gl_Position是顶点着色器&#xff08;Vertex Shader&#xff09;的一个内置输出变量&#xff0c;它用于指定顶点在裁剪空间&#xff08;Clip Space&#xff09;中的位置。gl_Position是一个四维向量&#xff08;4-component vector&#xff…

【FAQ】HarmonyOS SDK 闭源开放能力 —Push Kit(6)

1.问题描述&#xff1a; 推送通知到手机&#xff0c;怎么配置拉起应用指定的页面&#xff1f; 解决方案&#xff1a; 1、如果点击通知栏打开默认Ability的话&#xff0c; actionType可以设置为0&#xff0c; 同时可以在.clickAction.data中&#xff0c;指定待跳转的page页面…