I/O复用——select(一)

I/O复用

TCP编程流程的一些缺陷

缺点1:收发效率较低,当客户端发送一次数据后就断开,若再次发送只能重新连接
需求:当一个客户端和服务器建立连接,完成整个交互过程(和服务器存在多次的收发数据)之后,再断开连接
解决:给recv()/send()处加个死循环,直至数据交互完成退出

缺点2:当有多个客户端需要和服务器传输数据时,若正在和服务器通信的客户端不结束,其他的客户端只能在listen()的监听队列里等待,假设正在通信的客户端迟迟不收发数据(占着茅坑不拉屎)也不断开连接,此时后面的客户端必将耗费大量的时间等待,处理效率极低
解决:I/O复用

I/O复用

I/O复用使得程序能同时监听多个文件描述符,网络程序在下列情况下需要使用I/O复用技术:

  • TCP服务器同时要处理监听套接字和连接套接
  • 服务器要同时处理TCP请求和UDP请求
  • 程序要同时处理多个套接字
  • 客户端程序要同时处理用户输入和网络连接
  • 服务器要同时监听多个端口

I/O复用虽然能同时监听多个文件描述符,但它本身是阻塞的。 并且当多个文件描述符同时就绪时,如果不采取额外的措施,程序就只能按顺序依处理其中的每一个文件描述符,这使得服务器看起来好像是串行工作的。如果要提高并发处理的能力,可以配合使用多线程或多进程等编程方法

I/O复用的工作流程

I/O复用

select

#include <sys/select.h>

int select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

成功时返回就绪(可读、可写和异常)文件描述符的总数;如果在超时时间内没有任何文件描述符就绪, select将返回0;失败返回-1;如果在 select等待期间,程序接收到信号,则 select立即返回-1,并设置errno为 EINTR

参数含义
maxfd指定的被监听的文件描述符的总数。通常被设置为 select监听的所有文件描述符中的最大值+1
readfds可读事件类型
writefds可写事件类型
exceptfds异常事件类型
timeout指定一个时间(定时时间),若指定为空表示永久阻塞

fd_set结构

#define __FD_SETSIZE 1024
typedef long int __fd_mask;
#define _NFDBITS (8*(int) sizeof(__fd_mask))  // 32
typedef struct
{
#ifdef __USE_XOPEN
	__fd_mask fds_ bits[__FD_SETSIZE/ __NFDBITSI]; // 32
#define __FDS_BITS(set) ((set)->fds bits)
#else
	__fd_mask __fds_bits[__FD_SETSIZE/ __NFDBITSE]; // 32
#define __FDS_BITS(set) ((set)-> __fds_bits)
#endif
}fd_set;
  • fd_set包含一个32个元素的long int类型的数组,即32*32 = 1024位的数组
  • fd_set关注描述符的方式:使用每一个比特位记录一个文件描述符,文件描述符的值在位移上表现
    在这里插入图片描述
  • 将maxfd设置为文件描述符中的最大值+1的含义:提高select底层的执行效率,select只需要监管比maxfd小的位

通过下列宏可以访问 fd_set结构中的位

FD_ZERO (fd_set *fdset)// 清除fdset的所有位
FD_SET (int fd, fd_set *fdset);  // 设置fdset的位fd
FD_CLR (int fd, fd_set *fdset);  // 清除fdset的位fd
int FD_ISSET (int fd, fd_set *fdset);  // 测试fdset的位fd是否被设置

readfds、writefds、exceptfds是在线修改的

select首先会将用户传递的readfds、writefds、exceptfds拷贝到内核结构(死循环)上,来监听所关注的文件描述符上是否有事件发生,结束后会先将用户传递的readfds、writefds、exceptfds清空,再将所以就绪的文件描述符拷贝到readfds、writefds、exceptfds上,此时只有就绪的文件描述符相应的位为1,所以每次调用 select之前,都必须重新设置三个结构体变量,用户程序必须通过某种机制记录所有的文件描述

select的缺陷

  • 只能关注可读、可写、异常三种事件类型,且每种事件类型最多只能关注1024个文件描述符
  • select 的三个fd_set参数是在线修改的,再次调用select之前需要重新设置
  • 返回值只返回就绪的文件描述符的个数,用户程序探索就绪文件描述符的时间复杂度为 O(n)
  • 内核是使用轮询的方式检测就绪文件描述符,时间复杂度 O(n),效率较低
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值