【问题标题】:How to write to a nonblocking socket (when using epoll)如何写入非阻塞套接字(使用 epoll 时)
【发布时间】:2020-10-22 18:48:06
【问题描述】:

在非阻塞套接字上使用 epoll 执行异步套接字 IO 时,读取似乎很简单:只需 epoll_wait 直到套接字准备好读取,然后再读取直到获得 EAGAIN/EWOULDBLOCK

但是你怎么发送?大概做一个大的send操作会阻塞,所以你总是会得到EAGAIN/EWOULDBLOCK。你打算做什么?

【问题讨论】:

    标签: c linux sockets epoll


    【解决方案1】:

    大概内核无法立即发送所有数据,这就是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;
    }
    

    【讨论】:

    • 哦,当然,套接字是数据报还是字节流套接字很重要。数据报套接字不能进行部分写入,并使用部分读取来指示数据报边界。
    • 要非常清楚,以非阻塞模式打开的文件描述符上的send(或write)将失败,EAGAIN / EWOULDBLOCK 仅在没有字节的情况下一切都可以无阻塞地转移。此外,部分发送并不意味着立即尝试发送其余数据必然无法发送任何数据。
    【解决方案2】:

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

    如果您使用的是 TCP 套接字,请在循环中调用send(),直到您的所有字节都已发送或报告EAGAIN/EWOULDBLOCK。在后一种情况下,停止循环并将剩余的字节缓存在某处。

    如果你使用的是UDP套接字,send/to()只能发送整个数据报,所以根本不要使用循环,如果报告EAGAIN/EWOULDBLOCK然后缓存整个数据报。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-09
      • 2013-02-23
      • 2015-04-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多