多路io复用Select [补档-2023-07-16]
select
2.1 简介
select函数可以用于实现高效的多路复用 I/O,同时处理多个文件描述符的事件,包括监听可读、可写和异常条件,具有阻塞和非阻塞模式,并可以设置超时时间。这使得程序能够高效地处理并发任务,提高性能和响应性。
2.2 select函数
头文件:#include <sys/select>
函数原型:int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);
函数参数:
nfds:监视文件描述符的最大值加一(文件描述符集合中所有的文件描述符的最大值+1)
readfds:指向一个文件描述符集合,用于监视是否有数据可读。
writefds:指向一个文件描述符集合,用于监视是否可以写入数据。
exceptfds:指向一个文件描述符集合,用于监视是否有异常情况。
timeout:指向一个struct timeval结构体,表示超时时间,即select函数最多等待的时间。
结构体struct timeval 的成员:
time_t tv_sec:表示秒数。
suseconds_t tv_usec:表示微妙数。
函数返回值:
成功返回准备就绪的文件描述符总数。
监听超时则返回0。
发生错误返回-1。
2.3 select的几个宏函数
2.3.1将指定的一个文件描述符从集合中清除
函数原型:FD_CLR(int fd, fd_set *set);
函数参数:fd为指定的文件描述符,set为指定的文件描述符集合
2.3.2 将指定的文件描述符集合清空,所有位置都置为0
函数原型:FD_ZERO(fd_set *set);
函数参数:set为指定的文件描述符集合
2.3.3 将指定的文件描述符加入到指定的集合中
函数原型:FD_SET(int fd, fd_set *set);
函数参数:fd为指定的文件描述符,set为指定的文件描述符集合
2.3.4 判断指定的文件描述符是否在某个文件描述符集合中
函数原型:FD_ISSET(int fd, fd_set *set);
函数参数:fd为指定的文件描述符,set为指定的文件描述符集合
2.4 使用select创建一个多路io复用的服务端:
第一步:创建套接字。
第二步:准备一些变量和常量,用于记录最大文件描述符,客户端发来的数据,循环次数,条件判断等待。
第三步:初始化本地(服务器)地址结构体sockaddr_in。
第四步:将套接字与本地地址绑定,并且建议判断一下是否绑定成功。
第五步:切换为监听状态。
第六步:进入一个无限循环然后持续监听。
马上进入循环时初始化文件描述符集合,进入后将刚才监听的文件描述符添加到里面。
在所有已连接的客户端文件描述符集合中寻找最大的文件描述符,然后记录下来。
使用select函数对整个文件描述符集合进行监听,如果监听失败则退出程序,如果有文件描述符可操作, 则进行具体操作。
当select监听有变化则检测监听的套接字是否有新的连接请求,如果有则使用accept接收连接,并且将accept返回的已经和客户端建立连接的描述符加入到已连接的客户端数组中。
处理客户端的请求。遍历已连接的客户端数组,逐个判断每个客户端文件描述符数组中的元素是否中文件描述符集合中,如果在则代表有数据可读。
从客户端接收数据,并且进行处理。如果无法接收数据则关闭客户端文件描述符然后将其从描述符集合中移除。否则就对客户端发来的数据进行相关操作。
第七步:当循环结束后,记得关闭所有已连接的客户端文件描述符和服务器文件描述符。
2.5 使用select创建的多路io的服务端代码示例
点击查看代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#define MAX_CLIENTS 100 //最大客户端描述符数
#define BUF_SIZE 128 //字符串数组的最大长度
int main()
{
int client_fds[MAX_CLIENTS]; //用于存储客户端文件描述符,用来辅助文件描述符集使用
int server_fd; //服务端文件描述符(用于监听)
fd_set readfds; //可读文件描述符集
char data[BUF_SIZE];//用于存储接收来自客户端的信息
int maxfd; //最大文件描述符
int i;//下面可能会用到循环,它用来循环
int retval; //用于记录select的值
//创建套接字,ipv4,tcp
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
printf("创建套接字失败!\n");
exit(-1);
}
//分别用于存储服务端和客户端的地址信息
struct sockaddr_in server_addr, client_addr;
//初始化本地地址信息
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(10066);
server_sddr.sin_addr.s_addr = htonl(INADDR_ANY);
//将套接字与本地服务器地址绑定
int ret;//用于检测绑定或者切换至监听是否成功
ret = bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (ret == -1) {
printf("本地地址与套接字失败!\n");
exit(-1);
}
//切换监听
ret = listen(server_fd, MAX_CLIENTS);
if (ret == -1) {
printf("切换监听态失败!\n");
exit(-1);
}
printf("服务器开始监听,监听端口:10066 监听地址:任意\n");
//因为接下来的select需要接收一个集合的最大描述符,先用一个已有的文件描述符给他赋值
maxfd = server_fd;
//初始化客户端文件描述符集合,-1代表可用
for (i = 0; i < MAX_CLIENTS; i++) {
client_fds[i] = -1;
}
FD_ZERO(&readfds); //初始化可读文件描述符集
while (1) {
FD_SET(server_fd, &readfds); //将当前监听的描述符加入到里面
//将已连接的文件描述符加入到集合中
for (i = 0; i < MAX_CLIENTS; i++) {
int client_fd = client_fds[i];
if (client_fd != -1) {
//不等于-1则代表在被使用中代表已连接
FD_SET(client_fd, &readfds);
if (client_fd > maxfd) {
//如果当前已连接的文件描述符比之前的最大描述符要大,记得交换
//因为select函数接收的是集合中值最大的
maxfd = client_fd;
}
}
}
//使用select函数进程监听整个集合
retval = select(maxfd + 1, &readfds, NULL, NULL, NULL);
if (retval == -1) {
//等于-1代表监听失败
printf("监听失败!!!\n");
exit(-1);
}
else if (retval > 0) {
//大于0代表描述符集readfds有变化
//检测socket_fd是否有新的链接
if (FD_ISSET(server_fd, &readfds)) {
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(server_fd,(struct sockaddr *)&client_addr,&client_len);
if (client_fd == -1) {
printf("接收链接失败!!!\n");
}
else {
//将刚才接收到的新的来自客户端的socket添加到结合中
for (i = 0; i < MAX_CLIENTS; i++) {
if (client_fds[i] == -1) {
//-1则代表当前数组位置可用
client_fds[i] = client_fd;
break;
}
}
}
}
//处理来自客户端的请求
for (i = 0; i < MAX_CLIENTS; i++) {
int client_fd = client_fds[i];
if (client_fd != -1 && FD_ISSET(client_fd, &readfds)) {
//首先数组元素是被使用的状态,并且要判断数组元素i对应的描述符是否中集合中
memset(data, 0, BUF_SIZE);
//接收信息
ssize_t n = recv(client_fd, data, sizeof(data),0);
if (n <= 0) {
printf("未能接收客户端数据!\n");
close(client_fd);//关闭文件描述符
client_fds[i] = -1;//记得将这个坑置为-1,因为咱们操作完了
}
else {
//回应客户端
printf("服务端已经接收到客户端数据:\n%s\n",data);
if (send(client_fd, data, sizeof(data), 0) == -1) {
printf("回送服务端数据失败!\n");
}
}
}
}
}
}
//用完了记得循环关闭链接
for (i = 0; i < MAX_CLIENTS; i++) {
int client_fd = client_fds[i];
if (client_fd != 1) {
close(client_fd);
}
}
close(server_fd);
return 0;
}
2.6 select的优缺点
select优点:
1 一个进程可以支持多个客户端
2 select支持跨平台
select缺点:
1 代码编写困难
2 会涉及到用户区到内核区的来回拷贝,销毁资源大。
3 当客户端多个连接, 但少数活跃的情况, select效率较低
例如: 作为极端的一种情况, 3-1023文件描述符全部打开, 但是只有1023有发送数据, select就显得效率低 下
4 最大支持1024个客户端连接
select最大支持1024个客户端连接不是有文件描述符表最多可以支持1024个文件描述符限制的, 而是由 FD_SETSIZE=1024限制的.
FD_SETSIZE=1024 fd_set使用了该宏, 当然可以修改内核, 然后再重新编译内核, 一般不建议这么做.
多路io复用Select [补档-2023-07-16]的更多相关文章
- Redis03——Redis之单线程+多路IO复用技术
Redis 是单线程+多路IO复用技术 多路复用:使用一个线程来检查多个文件描述符的就绪状态 如果有一个文件描述符就绪,则返回 否则阻塞直到超时 得到就绪状态后进行真正的操作可以在同一个线程里执行,也 ...
- 多路IO复用模型--select, poll, epoll
select 1.select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开的文件描述符个数并不能改变select监听文件个数 2.解决1024以下客户端时使用se ...
- Linux C++ 网络编程学习系列(2)——多路IO之select实现
select实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/select 源码说明: server.cpp: 监听127.1:6666 ...
- 基于select类型多路IO复用,实现简单socket并发
还有很多缺限,如客户断开无限重复 以下转至老师博客: server: #!/usr/bin/env python # -*- coding: utf-8 -*- __author__ = " ...
- Linux企业级项目实践之网络爬虫(27)——多路IO复用
与多线程和多进程相比,I/O多路复用的最大优势是系统开销小,系统不需要建立新的进程或者线程,也不必维护这些线程和进程. 主要应用: (1)客户程序需要同时处理交互式的输入和服务器之间的网络连接 (2) ...
- io复用select方法编写的服务器
摘要:io多路复用是通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般都是读就绪或者写就绪),就能通知应用程序进行相应的读写操作.select函数作为io多路复用的机制,第一个参数nfds是f ...
- 协程与多路io复用epool关系
linux上其实底层都基于libevent.so模块实现的,所以本质一样 gevent更关注于io和其它 epool只是遇到io就切换,而gevent其它等待也切换
- IO复用——select系统调用
1.select函数 此函数用于在一段时间内,监听用户感兴趣的文件描述符上的可读.可写和异常等事件. #include<sys/select.h> int select(int nfds, ...
- 1高并发server:多路IO之select
1 select A:select能监听的文件描写叙述符个数受限于FD_SETSIZE,一般为1024.单纯改变进程打开 的文件描写叙述符个数并不能改变select监听文件个数 B:解决1024 ...
- python3.x 多路IO复用补充asyncio
asyncio模块是python之父写的模块,按说应该是靠谱的,python3.6版本定义为稳定版本. 说明书:https://docs.python.org/3/library/asyncio.ht ...
随机推荐
- Spring Boot 2.x基础教程:使用LDAP来管理用户与组织数据
很多时候,我们在做公司系统或产品时,都需要自己创建用户管理体系,这对于开发人员来说并不是什么难事,但是当我们需要维护多个不同系统并且相同用户跨系统使用的情况下,如果每个系统维护自己的用户信息,那么此时 ...
- AIsing Programming Contest 2020 游记 (ABC水题,D思维)
补题链接:Here A - Number of Multiples 水题 B - An Odd Problem 水题 C - XYZ Triplets 水题,注意数组不要开小了 D - Anythin ...
- ifelse优化方案
优化 if else:https://blog.csdn.net/FBB360JAVA/article/details/103832405 利用java8特性优化: https://blog.csdn ...
- AI正在改变人类社会 - 内容行业的衰落
现在的 AI 技术,每天都在进化.我有一种感觉,普通人大概没意识到,它马上就要改变人类社会了. 历史上,这种事一再发生.在你不知不觉中,某些大事件悄悄酝酿,突然就冲击到了你的生活,将你的人生全部打乱, ...
- 07-逻辑仿真工具VCS-Post processing with VCD+ files
逻辑仿真工具-VCS 编译完成不会产生波形,仿真完成之后,生成波形文件,通过dve产看波形 vcd是波形文件的格式,但是所占的内存比较大,后面出现了vpd(VCD+)波形文件 将一些系统函数嵌入到源代 ...
- MyBatis02:CRUD操作
CRUD操作 namespace 这个是命名空间,不可缺少,更不能写错 配置文件中namespace中的名称为对应Mapper接口或者Dao接口的全限定类名,完整包名,必须一致! Select(用于查 ...
- [转帖]【软件测试】Jmeter性能测试(性能测试,Jmeter使用与结果分析)
文章目录 前言 一.性能测试 1. 什么是性能测试? 2. 性能测试的重要性 3. 性能指标--QPS和TPS ①QPS ②TPS 二.压测工具Jmeter 1. 什么是Jmeter? 2. Jmet ...
- [转帖]如何在 Linux 中使用ss命令监控网络连接
https://zhuanlan.zhihu.com/p/99421574 ss命令是用于在Linux系统上显示与网络套接字相关的信息的工具. 该工具显示netstat命令的更多详细信息,该命令用于显 ...
- [转帖]SPEC-cpu2006的详细使用一键安装、手动安装。
一.SPEC-cpu2006简介 SPEC CPU 2006 benchmark是SPEC新一代的行业标准化的CPU测试基准套件.重点测试系统的处理器,内存子系统和编译器. 说明:由于spec2006 ...
- [转帖]docker编译speccpu2017
实验步骤: 1.下载docker和speccpu2017 2.docker下载镜像,创建容器 3.将下载的宿主机speccpu2017拷贝到docker创建的容器中(docker cp) 4.在doc ...