8/21作业

一、 非阻塞型IO

让我们的read函数不再阻塞,无论是否读取到消息,立刻返回

1.1 fcntl函数

原型:int fcntl(int fd, int cmd, ... /* arg */ );
调用:int flag = fcntl(描述符,F_GETFL)
     fcntl(描述符,F_SETFL,flag)
功能描述:设置或者获取文件的各项属性,到底如何操作由cmd决定,一般我们都会用来设置阻塞或者非阻塞IO
参数解析:
    参数 fd:准备设置属性的文件的描述符
    参数 cmd:文件到底设置什么属性又cmd决定
    参数 ...:
        F_SETFL:设置文件的flag属性
        F_GETFL:获取当前文件的flag属性

二、 多路文件IO

能够实现效果:先发生输入事件,再去调用阻塞型的读取函数

例如:

正常情况下,都是先调用scanf函数,阻塞并等待键盘输入事件

如果使用了多路文件IO的话,就能实现:先发生键盘输入事件,事件发生之后,立刻调用scanf函数,直线了高效率且非阻塞

2.1 多路文件IO的工作原理

内核会监视目标套接字的缓存区变化:如果

① 缓存区发生了改变:边缘触发

② 缓存区存在数据:水平触发

内核就会通知监视者,有描述符是可读的

已下3个模型,都是上述工作方式

2.2 select 模型

原型:int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);

调用:select(FD_SETSIZE,描述符集合,0,0,0)
FD_SETSIZE:值为1024

功能描述:以阻塞的形式监视 readfds,writefds,exceptfds 这3个描述符集合中,所有描述符,如果有任何描述符激活,则select解除阻塞

参数解析:
    参数 nfds:readfds,writefds,exceptfds 这3个集合中的最大值
    参数 readfds:监视描述符集合中任意的描述符是否可读,一般我们只用这个
    参数 writefds:监视描述符集合中任意的描述符是否可写,一般写NULL
    参数 exceptfds:监视描述符集合中任意的描述符是否发生意外,一般写NULL
        注意:只要select监视到了有描述符激活,就会将激活的描述符,以覆盖的形式写入到上述3个fds里面去
    
    参数 timeout:是一个结构体,结构如下
        struct timeval {
            long    tv_sec;         /* seconds */
            long    tv_usec;        /* microseconds */
        };
        表示select函数只阻塞传入的时间长度的秒数,超过这个时间自动解除阻塞
        传NULL表示:一直阻塞,不受时间影响
返回值:返回激活的描述符的数量

如何操作描述符集合

void FD_CLR(int fd, fd_set *set);
    功能描述: 从 set 中删除描述符 fd
int  FD_ISSET(int fd, fd_set *set);
    功能描述:判断 set 中是否存在描述符 fd
    返回值:如果存在返回1,不存在返回0
void FD_SET(int fd, fd_set *set);
    功能描述:将描述符 fd 添加到 set 里面去
void FD_ZERO(fd_set *set);
    功能描述:清空 set 所有描述符,相当于初始化的功能

select代码模型

fd_set readfds;
FD_ZERO(readfds)
FD_SET(想要监视的描述符,readfds)
while(1){
    select()
    判断/循环判断哪个描述符激活了{
        调用对用的阻塞函数
        例如:accept 或者 read    
    }
}

2.3 poll模型

工作方式和select是一样的,都是用来监视描述符是否激活,只不过操作过程不一样

因为select的不足,才会有poll模型的

① select中 fd_set 类型大小固定,1024个,如果要监视的描述符数量超过1024个,就会有问题

② select监视到激活的描述符成功后,会将激活的把readfds覆盖

poll 解决了上述2个问题

原型:int poll(struct pollfd *fds, nfds_t nfds, int timeout);
调用:poll(准备监视的struct pollfd 数组,数组的容量/实际长度,-1)
功能描述:监视 fds所指向的描述符数组中所有描述符的情况,最多监视nfds个,一般就是数组的容量
参数解析:
    参数 fds:结构体数组,数组中的每一个结构体元素都是一个描述符搭配一些其他数据,结构如下
        struct pollfd {
            int   fd;         /* file descriptor */要监视的描述符
            short events;     /* requested events */可读、可写、意外
                因为可读的原因激活:POLLIN,一般我们都写这个
                因为可写的原因激活:POLLOUT
            short revents;    /* returned events */
                fd描述符,一旦激活,用来表示具体因为什么原因激活的                                   
        };
    参数 nfds:想要监视的描述符的数量,一般就是fds这个数组的实际长度
    参数 timeout:poll函数阻塞时长,单位为毫秒
        传 0 表示不阻塞
        传 -1 表示一直阻塞,直到有描述符激活
        
返回值:成功返回激活的描述符的数量                        
注意:poll函数监视的直接是一个结构体数组,这个数组我们是可以直接操作的,不需要额外的函数去操作
注意:poll的激活方式为水平触发
    水平触发:只要监视的所有套接字中,有任何套接字缓存区存在数据,则poll就会激活   

poll的代码模型

struct pollfd fds[n] = {0};
fds[0].fd = 想要监视的第一个描述符
fds[0].events = POLLIN

while(1){
    poll(fds,n,-1)
    for(遍历 fds){
        // 判断 fds中哪个描述符激活了
        if(fds[i].revents == POLLIN){
            调用对应的阻塞函数
            例如 accept 或者 read 或者 scanf等        
        }                        
    }
}

作业:

serzy.c:

#include <myhead.h>
#define SER_PORT 6666
#define SER_IP "192.168.0.205"void insert_client(int *clinent_arr, int *len, int client)
{clinent_arr[*len] = client;(*len)++;
}int find_client(int *client_arr, int len, int client)
{for (int i = 0; i < len; i++){if (client_arr[i] == client){return i;}}return -1;
}void remove_client(int *client_arr, int *len, int client)
{int tar = find_client(client_arr, *len, client);if (tar == -1){return;}int i = -1;for (i = tar; i < *len; i++){client_arr[i] = client_arr[i + 1];}(*len)--;
}
int main(int argc, char const *argv[])
{int sfd = socket(AF_INET, SOCK_STREAM, 0);if (sfd == -1){perror("socket error");return -1;}fd_set readfd;int client_arr[100];int client_count = 0;FD_ZERO(&readfd);struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_addr.s_addr = inet_addr(SER_IP);sin.sin_port = htons(SER_PORT);if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) == -1){perror("bind error");return -1;}printf("bind success\n");if (listen(sfd, 128) == -1){perror("listen error");return -1;}FD_SET(sfd, &readfd);FD_SET(0, &readfd);// struct sockaddr_in cin;// socklen_t addrlen = sizeof(cin);while (1){fd_set temp = readfd;select(FD_SETSIZE, &temp, 0, 0, 0);if (FD_ISSET(sfd, &temp)){int client = accept(sfd, 0, 0);printf("有新的客户端连接\n");FD_SET(client, &readfd);insert_client(client_arr, &client_count, client);}else{for (int i = 0; i < client_count; i++){int client = client_arr[i];if (FD_ISSET(client, &temp)){char buf[128] = "";int res = read(client, buf, sizeof(buf));if (res == 0){printf("有客户端断开连接\n");FD_CLR(client, &readfd);remove_client(client_arr, &client_count, client);close(client);break;}printf("客户端发来消息:%s\n", buf);}}}if (FD_ISSET(0, &temp)){char buf[128] = "";fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = 0;for (int i = 0; i < client_count; i++){int client = client_arr[i];write(client, buf, strlen(buf));}}}return 0;
}

clizy.c:

#include <myhead.h>
#define SER_PORT 6666
#define SER_IP "192.168.0.205"
#define CLI_PORT 8888
#define CLI_IP "192.168.0.205"
int main(int argc, char const *argv[])
{close(3);// 1.创建用于通信的套接文字描述符int cfd = socket(AF_INET, SOCK_STREAM, 0);if (cfd == -1){perror("socket error");return -1;}fd_set readfd;FD_ZERO(&readfd);// 2.绑定IP地址和端口号//  填充地址信息struct sockaddr_in cin;cin.sin_family = AF_INET;cin.sin_port = htons(CLI_PORT);cin.sin_addr.s_addr = inet_addr(CLI_IP);// 绑定工作if (bind(cfd, (struct sockaddr *)&cin, sizeof(cin)) == -1){perror("bind error");return -1;}printf("bind success\n");// 3.连接到服务器// 3.1填充服务器地址信息结构体struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(SER_PORT);sin.sin_addr.s_addr = inet_addr(SER_IP);// 3.2连接服务器FD_SET(cfd, &readfd);FD_SET(0, &readfd);if (connect(cfd, (struct sockaddr *)&sin, sizeof(sin)) == -1){perror("connect error");return -1;}printf("连接服务器成功\n");// 4.数据收发char buf[128] = "";while (1){fd_set temp = readfd;select(FD_SETSIZE, &temp, 0, 0, 0);if (FD_ISSET(0, &temp) == 1){printf("标准输入流激活\n");char buf[32] = {0};fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = 0;send(cfd, buf, strlen(buf), 0);}if (FD_ISSET(cfd, &temp) == 1){printf("服务器端激活\n");char buf[32] = "";int res= read(cfd, buf, sizeof(buf));if (res==0){printf("服务器已下线\n");break;}printf("服务器读取到的数据为:%s\n", buf);}}// 5.关闭套接字close(cfd);return 0;
}

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

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

相关文章

洛谷p10892题解

题目背景 AzureHair 在 NOIP 2022 中被喵了个喵创死&#xff0c;于是患上了不治之症——T2 恐惧症&#xff0c;于是他在 NOIP 2023 中果断跳过了 T2 并杠 T3 两小时无果&#xff0c;遗憾离场&#xff0c;他的同学决定帮他治疗这种不治之症。 在他的同学给他治愈了 T2 恐惧症后…

机器学习-KNN 算法

一.K-近邻(KNN) K-近邻&#xff08;K-Nearest Neighbors, 简称 KNN&#xff09;是一种基于实例的学习算法&#xff0c;主要用于分类和回归问题。KNN 的工作原理直观且简单&#xff0c;它基于相似性进行预测&#xff0c;也就是说给定一个新的数据点&#xff0c;KNN 算法会查找距…

JUC知识点总结

JUC应用场景 1. 网页服务器处理并发请求 当一个网页服务器需要处理大量并发请求时&#xff0c;可以使用多线程来提高处理效率。 import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; ​ public class WebServer {public static void main(S…

8月21日笔记

Frp Frp(Fast e Reverse ) Proxy) 是一款简单&#xff0c;好用&#xff0c;稳定的隧道工具。Frp 使用 Go语言开发&#xff0c;支持跨平台&#xff0c;仅需下载对应平台的二进制文件即可执行&#xff0c;没有额外依赖。它是一款高性能的反向代理应用&#xff0c;可以轻松地进行…

Spring DI 数据类型—— set 方法注入

首先新建项目&#xff0c;可参考 初识IDEA、模拟三层--控制层、业务层和数据访问层 一、spring 环境搭建 &#xff08;一&#xff09;pom.xml 导相关坐标 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.or…

http连接未释放导致生产故障

凌晨4点运维老大收到报警&#xff08;公司官网页面超时&#xff0c;上次故障因为运维修改nginx导致官网域名下某些接口不可用后&#xff0c;运维在2台nginx服务器上放了检测程序&#xff0c;检测官网页面&#xff09;&#xff0c;运维自己先看了看服务器相关配置&#xff0c;后…

Java实现STL中的全排列函数next_permutation()

目录 一、引言 二、全排列函数next_permutation() 三、next_permutation()的使用 四、Java实现next_permutation() 五、使用next_permutation()实现全排列 一、引言 相信很多小伙伴们都做过全排列的算法题&#xff0c;输入一个n&#xff0c;输出1~n的全排列。对于这个问题…

k8s相关命令

一、Kubectl是什么 控制K8S通信的命令工具。 格式&#xff1a; kubectl [command] [TYPE] [NAME] [FLAGS] 二、Kubectl命令 1.查看容器命令 kubectl get pod kubectl get pod -n default #查看当前的命名空间的pod kubectl get pod -A #查看所有命名空间的pod kubectl de…

.Net插件开发开源框架

在.NET开发中&#xff0c;有许多开源框架可以用于插件开发&#xff0c;以下是一些最常见的框架&#xff1a; MEF&#xff08;Managed Extensibility Framework&#xff09; MEF是一个用于创建可插拔软件应用程序的库&#xff0c;它可以在不修改原始应用程序的情况下扩展应用程…

JVM 有哪些垃圾回收器?

JVM 有哪些垃圾回收器&#xff1f; 图中展示了7种作用于不同分代的收集器&#xff0c;如果两个收集器之间存在连线&#xff0c;则说明它们可以搭配使用。虚拟机所处的区域则表示它是属于新生代还是老年代收集器。 新生代收集器&#xff08;全部的都是复制算法&#xff09;&…

【安全靶场】-DC-7

❤️博客主页&#xff1a; iknow181 &#x1f525;系列专栏&#xff1a; 网络安全、 Python、JavaSE、JavaWeb、CCNP &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐评论✍ 一、收集信息 1.查看主机是否存活 nmap -T4 -sP 192.168.216.149 2.主动扫描 看开放了哪些端口和功能 n…

【网络】UDP和TCP之间的差别和回显服务器

文章目录 UDP 和 TCP 之间的差别有连接/无连接可靠传输/不可靠传输面向字节流/面向数据报全双工/半双工 UDP/TCP API 的使用UDP APIDatagramSocket构造方法方法 DatagramPacket构造方法方法 回显服务器&#xff08;Echo Server&#xff09;1. 接收请求2. 根据请求计算响应3. 将…

JVM分代回收

JVM分代回收 堆中分为两份:新生代和老年代(1:2) 新生代 新生代的内部分为了三个区域 Eden区,From区,To区[8:1:1] 当eden区内存不足时,就会使用可达性分析算法进行标记 标记eden区和from区的存活对象 将这些对象复制到to区,对eden区和from区进行清除 如果eden下次又出现…

黑马头条vue2.0项目实战(十一)——功能优化(组件缓存、响应拦截器、路由跳转与权限管理)

1. 组件缓存 1.1 介绍 先来看一个问题&#xff1f; 从首页切换到我的&#xff0c;再从我的回到首页&#xff0c;我们发现首页重新渲染原来的状态没有了。 首先&#xff0c;这是正常的状态&#xff0c;并非问题&#xff0c;路由在切换的时候会销毁切出去的页面组件&#xff…

Java之HashMap的底层实现

Java之HashMap的底层实现 摘要HashMap的底层原理哈希值转换为数组下标节点初始化put(Object key, Object value)重写toString()get(Object key)增加泛化remove(K key) 摘要 本博客主要讲述了Java的HashMap的底层实现 HashMap的底层原理 底层原理&#xff1a;数组链表 过程…

【ARM 芯片 安全与攻击 5.6 -- 侧信道与隐蔽信道的区别】

文章目录 侧信道与隐蔽信道的区别侧信道攻击(Side-channel Attack)侧信道攻击简介侧信道攻击 使用方法侧信道攻击示例隐蔽信道(Covert Channel)隐蔽信道简介隐蔽信道使用方法隐蔽信道代码示例侧信道与隐蔽信道在芯片及系统安全方面的使用侧信道的应用隐蔽信道的应用Summary…

【C/C++】菱形继承问题

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

python进阶语法---异常处理

在 Python 中&#xff0c;异常处理是一种机制&#xff0c;用于处理程序运行时可能发生的错误&#xff0c;以确保程序能够优雅地处理错误情况&#xff0c;而不是直接崩溃。异常处理机制的核心是使用 try、except、else 和 finally 语句。 一、基础语法 异常处理的基本语法如下…

【JAVA CORE_API】Day18 网络编程、线程、在线聊天室v1.0

C/S&#xff1a;客户端/服务器端&#xff0c;所有网络应用都是基于客户端服务器端进行的&#xff0c;Java写的是服务端&#xff0c;客户端是一个软件&#xff0c;服务端也是一个软件&#xff0c;两个软件之间交互&#xff1b;&#xff08;只能连接对应的服务器&#xff09; B/…

深度学习-k-mean实现聚类

对模拟函数生成的随机数据&#xff0c;分为三个族群。 1.生成数据 设定样本数量、特征数、族群数&#xff0c;以及簇内标准差之后&#xff0c;通过make_blobs生成随机数据。 scikit-learn库中的make_blobs函数来生成模拟数据集的&#xff0c;用于创建多类单标签数据集&#…