Reputation: 1207
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
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
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
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