目录
1.IO函数
1.1 recv()
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
从套接字s中接收数据到buf中,buf的长度为len,flag参数的定义如下
值 | 含义 |
MSG_DONTWAIT | 非阻塞操作,立即返回 |
MSG_ERRQUEUE | 错误消息从套接字错误队列接收 |
MSG_OOB | 接收带外数据 |
MSG_PEEK | 查看刻度数据,执行recv后,内核不会将这些数据丢弃 |
MSG_TRUNC | 返回所有数据,即使指定的缓冲区过小 |
MSG_WAITALL | 灯带所有的消息 |
MSG_CMSG_CLOEXEC | recvmsg() only |
recv通常用于TCP类型的套接字,UDP常用recvfrom,在UDP绑定地址跟端口后也可以使用recv.
recv函数从内核的接受缓冲区中复制数据到用户指定的缓冲区内,当内核中的数据比指定缓冲区大小小的时候,一般情况下会复制缓冲区中的所有内容到用户缓冲区,并返回数据长度。当内核的数据长度大于用户指定缓冲区大小的时候,内核会将接收缓冲区的数据按照用户指定的长度len复制到用户指定地址,然后将剩余数据下次调用接收函数的时候返回。
1.2 send()
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
当send返回的数据小于len时,表明缓冲区中仍然有部分数据没有发送,这时需要重新计算剩余没有发送的数据的长度。通常的剩余数据发送的方法是对原来的buf中的数据位置偏移已成功发送的字节数。
1.3 readv()和writev()
#include <sys/uio.h>
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
struct iovec {
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes to transfer */
};
在调用readv的时候必须制定iov_base的长度.参数vector指向一块结构vector的内存,大小由iovcnt制定。readv()向量的结构如下图:
1.4 recvmsg()和sendmsg()
#include <sys/types.h>
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);
struct msghdr {
void *msg_name; /* optional address */
socklen_t msg_namelen; /* size of address */
struct iovec *msg_iov; /* scatter/gather array */
size_t msg_iovlen; /* # elements in msg_iov */
void *msg_control; /* ancillary data, see below */
socklen_t msg_controllen; /* ancillary data buffer len */
int msg_flags; /* flags on received message */
};
recvmsg的含义如1.1表格所示。recvmsg的向量结构如下图所示:
1.5IO函数的比较
IO函数比较 | |||||||
名称 | 任何描述符 | 只对套接字描述符 | 单个缓冲区 | 多个缓冲区 | 可选标志 | 可选对方标志 | 可选控制信息 |
read()/write() | √ | √ | |||||
readv()/writev() | √ | √ | |||||
recv()/send() | √ | √ | √ | ||||
recvfrom()/writeto() | √ | √ | √ | √ | |||
recvmsg()/sendmsg() | √ | √ | √ | √ | √ |
使用IO的例子:
https://github.com/lixiangsheng2018/linux_network_programming/tree/master/chp9/send_recv
https://github.com/lixiangsheng2018/linux_network_programming/tree/master/chp9/readv_writev
2.IO模型
2.1 阻塞IO模型
在数据没有到来之前程序会一直等待,如下图
2.2 非阻塞IO模型
对每次请求,内核都不会阻塞,会立即返回,当没有数据的时候,会返回一个错误,如下图:
2.3 IO复用
在等待的时候计入超时时间,当超时时间没有到的时候跟阻塞IO一致,当超时时间到大仍没有数据到来系统会返回。
selsect函数会按照一定的时间轮询,直到有数据到来利用recvfrom将数据复制到应用层,如下图:
2.4 信号驱动IO模型
信号驱动IO模型在进程开始的时候注册一个信号处理的回调函数,进程继续进行,当信号发生时,利用注册的回调函数将到来的数据用recvfrom函数接收,如下图:
2.5 异步IO模型
异步IO与信号驱动IO相似,区别在于信号驱动IO当数据到来的时候,使用信号通知注册的信号处理函数,而异步IO在数据复制完成的时候才发动信号通知注册的信号处理函数,如下图:
3.select和pull机制
4.epoll机制
5.几种IO模型的选择