如何写入非阻塞套接字(使用 epoll 时)

问题描述 投票:0回答:2

在非阻塞套接字上使用 epoll 进行异步套接字 IO 时,读取似乎很简单:只需

epoll_wait
直到套接字准备好读取,然后读取直到获得
EAGAIN
/
EWOULDBLOCK

但是如何发送呢?大概做一个大的

send
操作会阻塞,所以你总是会得到
EAGAIN
/
EWOULDBLOCK
。你到底想做什么?

c linux sockets epoll
2个回答
5
投票

大概内核无法立即发送所有数据,这就是为什么成功时

send
返回发送的字节数。重要的是返回的数字can(并且很可能会)低于调用中指定的总长度。如果
根本没有字节
可以发送,您只会得到EAGAIN/EWOULDBLOCK作为答案。所以当发送大量数据时,你只需要正确处理(很可能)不是所有数据一次发送的可能性即可。

注意:这对于数据报 (UDP) 套接字有所不同,因为无法发送部分数据报。请参阅 POSIX:从 UDP 套接字上的 write() 返回值了解更多信息。

这是一个假设有

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;

        perror("send failed");
        exit(1);
    }
    
    sent += n;
}

3
投票

send/to()
将发送尽可能多的字节,返回它实际上能够给内核发送的字节数。

如果您使用 TCP 套接字,请循环调用

send()
,直到发送完所有字节或报告
EAGAIN
/
EWOULDBLOCK
。在后一种情况下,停止循环并将剩余字节缓存在某处。

如果您使用 UDP 套接字,

send/to()
只能发送整个数据报,因此根本不要使用循环,如果报告
EAGAIN
/
EWOULDBLOCK
,则缓存整个数据报。

每当

epoll
指示套接字可写时,根据需要发送该套接字的任何缓存字节/数据报,仅从缓存中删除成功的字节/数据报,直到清除缓存或报告
EAGAIN
/
EWOULDBLOCK
。将未发送的字节/数据报留在缓存中。

每当您需要发送新的 TCP 字节或新的 UDP 数据报时,如果套接字的缓存不为空,则将字节/数据报附加到缓存末尾并继续,否则尝试立即发送字节/数据报,缓存如果报告

EAGAIN
/
EWOULDBLOCK
,如上所述。

© www.soinside.com 2019 - 2024. All rights reserved.