Linux网络编程|I/O复用:select调用详解(代码实现:待补充)

IO复用

1.TCP的编程
在这里插入图片描述
从TCP的编程流程可以看出,一个客户端连接上服务器后,并不是时刻在给服务器发送数据,所以服务器程序大部分时间是在recv阻塞,为了提高服务器效率,需要将recv操作放在客户端发送数据之后,服务器程序不需要再recv阻塞,引出------->I/O复用。
2.I/O复用技术使得一个程序可以同时监管多个文件描述符,当某些文件描述符上有事件发生,程序在再去处理发生事件对的文件描述符。IO复用技术使得程序能够同时监听多个文件描述符,这对提高程序的性能至关重要。
3.Linux下实现IO复用的系统调用主要有select、poll和epoll,这三个都是负责对多个文件描述符监听特定的关注事件,其中epoll是Linux系统独有的。
——————————————————————————————————

本文主要来看select,poll和epoll在后面文章介绍

以下情况下需要使用io复用:
1、客户端同时处理多个socket(如:非阻塞的connect技术);
2、客户端程序需要同时处理用户输入和网络连接(如:聊天室程序);
3、TCP服务器要同时处理监听socket和连接socket;
4、服务器要同时处理TCP请求和UDP请求(如:回射服务器);
5、服务器同时监听多个端口,或者处理多种服务(xinetd服务器);

———————————————————————————————————

select

1.相关概念
(1)轮询:指的是一遍又一遍的循环监听从0到我们记录的这些文件描述符中的最大值。
(2)fd_set:一个结构体,该结构体中仅包含一个整型数组,这个整型数组的每个元素的每个位代表一个文件描述符(按位来表示文件描述符),故select能同时处理的文件描述符的最大数量是由这个数组的总位数决定,这个最大值为<sys/select.h>头文件中定义的一个常量FD_SETSIZE。
2.select系统调用原型

#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
				fd_set *exceptfds, struct timeval *timeout);

(1)nfds 参数指定了被监听的文件描述符的总数,它通常是readfds,writefds,exceptfds这三个描述符集中的最大描述符编号加1,因为文件描述符是从0开始计数的。
(2)readfds,writefds,exceptfds分别指向可读,可写,异常事件对应的文件描述符集合。应用程序调用select函数时,通过这三个参数传入自己感兴趣的文件描述符,轮询等待这些描述符有事件产生。select调用返回时,内核将修改它们来通知应用程序哪些文件描述符已经就绪。这3个参数是fd_set结构指针类型。
(3)头文件<sys/select.h>中fd_set结构体的定义是:

/* Maximum number of file descriptors in `fd_set'.  */
#define	FD_SETSIZE		__FD_SETSIZE

/* The fd_set member is required to be an array of longs.  */
typedef long int __fd_mask;

/* Some versions of <linux/posix_typeshygfdfsdgfhjkl.h> define these macros.  */
#undef	__NFDBITS
/* It's easier to assume 8-bit bytes than to get CHAR_BIT.  */
#define __NFDBITS	(8 * (int) sizeof (__fd_mask))

/* fd_set for select and pselect.  */
typedef struct
  {
    /* XPG4.2 requires this member name.  Otherwise avoid the name
       from the global namespace.  */
#ifdef __USE_XOPEN
    __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->fds_bits)
#else
    __fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->__fds_bits)
#endif
  } fd_set;

fd_set结构体仅包含一个整形数组,该数组的每个元素的每一位(bit)标记一个文件描述符。fd_set能容纳文件描述符的数量由FD_SETSIZE指定,这就限制了select能同时处理的文件描述符的总量。

文件描述符过于繁琐,一般使用下面的一系列宏来访问fd_set结构体中的位:

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

(4)timeout参数用来设置select函数的超时时间,表示select愿意等待多长时间。它是一个timeval结构类型的指针,不过我们不能完全信任select调用返回后的timeout值,比如调用失败时timeout的值是不确定的,timeval结构体的定义如下:

struct timeval{
        long  tv_sec;     //单位:秒
        long  tv_usec;    //单位:微秒
};

由以上定义可见,select给我们提供了一个微妙级的定时方式

a.如果给timeval的成员都赋值0,则select将立即返回;
b.如果timeout设置为NULL,则select将一直阻塞,直到某个文件描述符就绪。
——————————————————————————————————
select成功时返回就绪的文件描述符的总数,如果在超时时间内没有任何描述符就绪,select返回0,select失败返回-1并设置errno。如果在select等待期间,程序收到信号,则select立即返回-1,并设置errno为EINTR。
———————————————————————————————————

select文件描述符的就绪条件

首先要明确一点,哪些情况下文件描述符可以被认为是可读、可写或者出现异常,对于select的使用是非常关键的。
1.socket可读的情况:
(1)socket内核接收缓存区中的字节数大于或等于其低水位标记SO_RCVLOWAT。此时可以无阻塞地读该socket,并且读操作返回的字节数大于0;
(2)socket通信对方关闭连接。此时对该socket读操作将返回0;
(3)监听socket上有新的连接请求;
(4)socket上有未处理的错误,此时我们可以使用getsockopt来读取和清除该错误。
2.socket可写的情况:
(1)socket内核发送缓冲区中的可用字节数大于或等于其低水位标记SO_SNDLOWAT。此时我们可以无阻塞写该socket,并且写操作返回的字节数大于0;
(2)socket写操作被关闭,对写操作被关闭的socket执行写操作将触发一个SIGPIPE信号;
(3)socket使用非阻塞connect连接成功或者失败(超时)之后;
(4)socket上有未处理的错误,此时我们可以使用getsockopt来读取和清除该错误。
3.网络程序中,select能处理的异常情况只有一种:socket上接收到带外数据

关于带外数据------------->参考此处<--------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值