CaptainCodeman
CaptainCodeman

Reputation: 2201

How to write to a nonblocking socket (when using epoll)

When doing async socket IO with epoll on non-blocking sockets, reading seems straightforward: Just epoll_wait until the socket is ready for reading, and read until you get EAGAIN/EWOULDBLOCK.

But how do you send? Presumably doing a single large send operation would block, so you will always get EAGAIN/EWOULDBLOCK. What are you meant to do?

Upvotes: 1

Views: 2041

Answers (2)

Marco Bonelli
Marco Bonelli

Reputation: 69286

Presumably the kernel will not be able to send all data right away, that's why send on success returns the number of bytes sent. The important thing is that the number returned can (and most likely will) be lower than the total length specified in the call. You will only get EAGAIN/EWOULDBLOCK as an answer if no bytes at all can be sent. So when sending a large amount of data, you just need to correctly handle the (very likely) possibility that not all data is sent at once.

Note: this is different for datagram (UDP) sockets, since partial datagrams cannot be sent. See POSIX: Return value from write() on UDP socket for more information.

Here's an example assuming a SOCK_STREAM (TCP) socket:

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;
}

Upvotes: 5

Remy Lebeau
Remy Lebeau

Reputation: 595827

send/to() will send as many bytes as it can, returning how many bytes it was actually able to give the kernel to send.

If you are using a TCP socket, call send() in a loop until EITHER all of your bytes have been sent OR EAGAIN/EWOULDBLOCK is reported. In the latter case, stop the loop and cache the remaining bytes somewhere.

If you are using a UDP socket, send/to() can only send whole datagrams, so don't use a loop at all, and if EAGAIN/EWOULDBLOCK is reported then cache the entire datagram.

Whenever epoll indicates a socket is writable, send any cached bytes/datagrams for that socket as needed, removing only successful bytes/datgrams from the cache, until EITHER the cache is cleared OR EAGAIN/EWOULDBLOCK is reported. Leave unsent bytes/datagrams in the cache.

Whenever you need to send new TCP bytes, or a new UDP datagram, if the socket's cache is not empty then append the bytes/datagram to the end of the cache and move on, otherwise attempt to send the bytes/datagram immediately, caching if EAGAIN/EWOULDBLOCK is reported, as described above.

Upvotes: 3

Related Questions