ironicaldiction
ironicaldiction

Reputation: 1207

Writing a full buffer using write() system call

I want to write a buffer filled with BUFF_SIZE bytes over a TCP socket using the write system call:

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

The documentation states that

write() writes up to count bytes from the buffer pointed buf to the file referred to by the file descriptor fd.

Of course, the number of actual bytes written can be detected by the return value. However, if I want to ensure that my whole buffer of bytes gets sent over the connection, what would be a good way to do this? So far, I was thinking:

while ( (ch = fgetc(pipe) ) != EOF )
{
    buff[ 0 ] = ch;
    bytes_in_buff++;

    // fill a buffer's worth of data from the pipe
    for (int i = 1; i < BUFF_SIZE; ++i, ++bytes_in_buff)
    {
        if ( (ch = fgetc(pipe) ) == EOF)
            break;

        buff[ i ] = ch;
    }

    // write that buffer to the pipe
    int bytes_sent = 0;
    while (bytes_sent < BUFF_SIZE)
    {
        bytes_sent = write(fd, buff, bytes_in_buff);
    }
}

But of course, some redundant bytes will be sent if I continue to send the entire buffer each time bytes_sent < BUFF_SIZE .

Upvotes: 1

Views: 11586

Answers (3)

user207421
user207421

Reputation: 311055

If write() returns less than BUFF_SIZE your suggested loop will never terminate; and you need to check for errors.

You need something like this:

while (bytes_in_buff > 0)
{
    bytes_sent = write(fd, buff, bytes_in_buff);
    if (bytes_sent < 0)
    {
        perror("write");  // or whatever
        break;
    }
    buff += bytes_sent;
    bytes_in_buff -= bytes_sent;
}

However this issue was discussed in extenso on news:comp.protocols.tcp-ip some years ago, which is the place where TCP/IP implementators hang out, and it was agreed there that, in blocking mode, both write() and send() must send the entire buffer before returning.

Upvotes: 2

alk
alk

Reputation: 70981

Have a look at the following function which loops around write() until s bytes from b had been written or a fatal error had occurred:

int writen(const int sd, const char * b, const size_t s, const int retry_on_interrupt)
{
  size_t n = s;
  while (0 < n)
  {
    ssize_t result = write(sd, b, n);
    if (-1 == result)
    {
      if ((retry_on_interrupt && (errno == EINTR)) || (errno == EWOULDBLOCK) || (errno == EAGAIN))
      {
        continue;
      }
      else
      {
        break;
      }
    }

    n -= result;
    b += result;
  }

  return (0 < n) ?-1 :0;
}

Call it like this:

int retry_on_interrupt = ... /* {0|1} depending on wether a signal reception shall abort the write operation or not. */
int result = writen(fd, buff, sizeof(buf), retry_on_interrupt)
if (-1 == result)
{
  perror("writen()");
}

Upvotes: 3

mc110
mc110

Reputation: 2833

You need to edit the parameters you give to write to account for the data you've already sent. So something like this:

int bytes_sent = 0;
int remaining = BUFF_SIZE;
while (remaining) 
{
  bytes_sent = write(fd, buff, remaining);
  remaining -= bytes_sent;
  buff += bytes_sent;
}

Upvotes: 1

Related Questions