1

I'm using write (man 2 write) to write data to a socket which has an established, blocking and very slow TCP-connection. I'm writing large chunks of data. write returns the actual size written and of course it happens that not all data is written due to reason which are (maybe) out-of-scope of this question.

To be sure I'm encapsulating my write-call in a small loop like this:

do {
    ssize_t ret = write(client, p, count);
    if (ret <= 0)
        break;
    p += ret;
    count -= ret;
} while (count);

if (count != 0) 
    return -ENODEV;

Is there a better way to do so, like setting a flag on the file-descriptor and thus having lower-layers handle it?

Patrick B.
  • 11,773
  • 8
  • 58
  • 101

2 Answers2

6

I suggest using a while loop instead of a do {} while to have consistent behaviour for the case count == 0. Furthermore, some cases of failure are not errors:

while (count > 0) {
    ssize_t ret = write(client, p, count);
    if (ret <= 0) {
        if (ret == 0)
            return -ENODEV;
        if (errno == EINTR)
            continue;
        else
            return -errno;
    }    
    p += ret;
    count -= ret;
}

EINTR is set if the system call was interrupted by a signal before any data was written. The write should be restarted in the case. If the client handle was set to non blocking, you should also deal with EAGAIN and EWOULDBLOCK.

A more compact and elegant version derived from wildplasser's answer:

for (size_t done = 0; done < count; ) {
    ssize_t ret = write(client, p + done, count - done);

    if (ret == 0) return -ENODEV;
    if (ret < 0 && errno != EINTR) return -errno;
    done += ret;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • The `ret` varaible has loop scope. I had to hoist it out of the loop. – wildplasser Mar 14 '15 at 15:07
  • You are right, my bad. Too bad I cant declare it at loop scope because of the infamous `ssize_t` kludge. Hoisting out of the loop sucks, but so does moving the increment down to the end of the `for` body. I shall relunctantly choose the latter. – chqrlie Mar 14 '15 at 22:28
  • So finally, you end up with my solution (except fot the crippled for() loop) and +5 instead of -1 credits) I rest my case. So it goes ... – wildplasser Mar 15 '15 at 00:23
1
size_t done;
ssize_t ret;

for (done = 0; done < size; done += ret) {
    ret = write(client, buff + done, size-done);

    if (ret == 0) return -ENODEV;
    if (ret == -1 && errno == EINTR) { ret = 0; continue; }
    if (ret == -1)  return -errno;
}
wildplasser
  • 43,142
  • 8
  • 66
  • 109
  • Not my down vote! Your answer is more compact and does not modify `p` nor `size`. I'll edit mine accordingly. – chqrlie Mar 14 '15 at 14:43
  • I don't complain about downvotes, don't understand it either. I do think that maintaining two variables in a loop, when only one is needed, is *in general* a bad habit. – wildplasser Mar 14 '15 at 15:06