max
max

Reputation: 2657

how to write to a socket properly

In the man pages of Linux about write() it's stated that in case of EINTR or EAGAIN write() will return -1. While performing a read() one may retry if such errors are encountered. What should be done in such situations when performing a write()?

Upvotes: 2

Views: 447

Answers (3)

user207421
user207421

Reputation: 310840

You can retry in the case of EINTR.

You can only get EAGAIN/EWOULDBLOCK if you're in non-blocking mode, and in those cases it isn't appropriate to just retry in a loop. You should select() until the socket becomes writable, and then retry. That can only happen when the peer reads something, which is why it isn't appropriate to burn CPU cycles at the sending end.

Upvotes: 1

1203_dube
1203_dube

Reputation: 214

It is true that you may wrap calls to write() by checking for EINTR or EAGAIN. A simple loop to try the write() again in those cases is suggested in this question, and may work well:

Upvotes: -1

VHarisop
VHarisop

Reputation: 2826

As far as I know, a way to handle such cases involves wrapping the write() call inside a function that checks the return value as well as the errno variable.

Cases like EAGAIN and EINTR indicate situations where the write() was unsuccessful because some signal interrupted it or O_NONBLOCK was set but the write() would block. This means you can retry the write() by probing the errno status.

I'm not sure, however, if it is guaranteed that write() will eventually succeed if you insist (and, of course, if you don't get any different errno status).

You could do something like

ssize_t insist_write(int fd, const void * buff, size_t cnt)
{
    ssize_t ret; 
    size_t original_cnt = cnt;
    while (cnt > 0)
    {
        ret = write(fd, buff, cnt);
        if (ret < 0) 
        {
            // EINTR for interrupted writes, EAGAIN - EWOULDBLOCK for file/socket
            // blocking case (see man page)
            if ((errno != EINTR) && (errno != EAGAIN) && (errno != EWOULDBLOCK))
               return ret;
            else 
               ret = 0;
        }
        // update buff and remaining bytes to write
        buff += ret;
        cnt -= ret;
    }
    return original_cnt;
}

I have also seen code where the above case is handled by doing

if (ret < 0) { return ret; }

instead of what I wrote, implying that such cases would be handled on a per-application basis (one such example is specifying a maximum number of write() attempts before returning -1).

Upvotes: 1

Related Questions