IO模型
阻塞IO模型
默认情况下, 所有的套接字都是阻塞的. 如下图, 应用进程在调用recvfrom后, 如果数据还没准备好, 就会一直阻塞(期间应用进程不能做任何事情, 只能干等着), 直到数据准备好, 并且从内核复制到用户空间, recvfrom才返回.
非阻塞IO模型
可以通过fcntl等其他手段将套接字设置为非阻塞的. 当应用进程调用recvfrom后, 如果数据未准备好, 不进行阻塞, 而是返回一个错误:EWOULDBLOCK
(error would block). 如下图, 如果单单靠非阻塞IO, 就只能通过在while循环调用recvfrom, 直至成功返回. 这样的话, 效率甚至不如阻塞IO模型. 所以, 非阻塞IO通常是搭配IO复用的.
IO复用模型
在IO复用模型中, 通常有三个IO复用函数: select, poll, epoll. 可以将多个感兴趣的加入监听, 每当调用IO复用函数时, 就会将那些你关注的\感兴趣的并且有相应时间发生的套接字返回. 需要注意的是, select, poll, epoll本身是阻塞的.
信号驱动式IO模型
异步IO模型
信号驱动IO和异步IO还没理解, 后补.
IO复用函数
select
这里只介绍文件描述符的就绪条件(重要)
- 异常条件
- 套接字的带外数据到达.
- 可读条件
- 该套接字的接收缓冲区的数据字节数大于等于其接收缓冲区低水位标志
- 该连接读半部分关闭(也就是接收了FIN分节的TCP连接). 对于这样的套接字, read不阻塞, 并返回0 (EOF)
- 该套接字是监听套接字, 而且已完成连接队列非空
- 该套接字上有一个套接字待处理
- 可写条件
- 该套接字的发送缓冲区的可用空间字节数大于其发送缓冲区的低水位标志
- 该连接的写半部分关闭. (对这样的套接字写会产生SIGPIPE信号)
- 使用非阻塞connect的套接字已连接, 或者connect以失败告终 (???, 不懂)
- 该套接字上有一个套接字待处理
poll
poll识别三类型数据: 普通(normal), 优先级带(priority band), 高优先级(high priority)
- 普通数据:
- 所有正规的TCP和UDP数据(POLLIN, POLLRDNORM 或 POLLOUT)
- TCP连接的读半部关闭
- TCP连接存在错误(EPOLLERR) (read会返回-1)
- 监听套接字上有新连接
- 非阻塞connect的完成
- 优先级带数据:
- TCP的带外数据
当不再关心某个特定的文件描述符, 可以将pollfd结构体的fd成员设置成一个负值.
poll函数忽略这样的结构体, 返回时将其revents置为0.
shutdown
这里只介绍第三个参数howto不同值的行为:
-
SHUT_RD:值为0,关闭连接的读这一半。**套接字不再有数据可接收, 而且套接字的接收缓冲区数据全都被丢弃. **
-
SHUT_WR:值为1,关闭连接的写这一半。套接字不再可写, 但是TCP会将发送缓冲区的数据发送完, 才发送FIN分节
-
SHUT_RDWR:值为2,连接的读和写都关闭。相当于调用两次shutdown, howto分别为SHUT_RD和SHUT_WR.