问题描述
在非阻塞套接字上使用epoll执行异步套接字IO时,读取似乎很简单:只需epoll_wait
直到套接字准备好读取,然后读取直到获得EAGAIN
/ EWOULDBLOCK
但是您如何发送?大概执行一个大的send
操作会阻塞,因此您将始终获得EAGAIN
/ EWOULDBLOCK
。你打算做什么?
解决方法
大概内核将无法立即发送所有数据,这就是send
成功返回已发送字节数的原因。重要的是,返回的数字可以(并且很可能会)小于调用中指定的总长度。如果根本没有字节可以发送,那么您只会得到EAGAIN/EWOULDBLOCK
作为答案。因此,在发送大量数据时,您只需要正确地处理(很可能)并非一次发送所有数据的可能性。
注意:对于数据报(UDP)套接字,这是不同的,因为无法发送部分数据报。有关更多信息,请参见 POSIX: Return value from write() on UDP socket 。
以下示例假设一个SOCK_STREAM
(TCP)套接字:
size_t len;
size_t sent;
char *buf;
/* ... prepare data ... */
while (sent < len) {
ssize_t n;
if ((n = send(sockfd,buf + sent,len - sent,flags)) == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
continue;
} else {
perror("send failed");
exit(1);
}
}
sent += n;
}
,
PrintfType
将发送尽可能多的字节,并返回其实际能够发送给内核的多少字节。
如果使用的是TCP套接字,请循环调用send/to()
,直到所有字节发送完毕或报告send()
/ EAGAIN
。在后一种情况下,请停止循环并将其余字节缓存在某个位置。
如果您使用的是UDP套接字,EWOULDBLOCK
只能发送整个数据报,因此根本不要使用循环,并且如果报告了send/to()
/ EAGAIN
,则缓存整个数据报。
只要EWOULDBLOCK
指示套接字可写,就根据需要发送该套接字的所有已缓存字节/数据报,仅从缓存中删除成功的字节/数据报,直到清除缓存或epoll
/报告了EAGAIN
。在缓存中保留未发送的字节/数据报。
每当您需要发送新的TCP字节或新的UDP数据报时,如果套接字的缓存不为空,则将字节/数据报附加到缓存的末尾并继续操作,否则尝试立即发送字节/数据报,如上所述,缓存是否报告EWOULDBLOCK
/ EAGAIN
。