6

I'm trying to receive some file through sockets in C. But the server sends me 64-byte packets for a 1000000 byte file for example and I get approximately 999902 bytes on the destination file.

while ((n = read(sd, buffer_in, BUFSIZE ))) //  BUFSIZE = 64 
{
    if(n<0)
    {
       printf("Fail.\n");
       fclose(archivo);
       return -1;
    }

    if(fwrite(buffer_in, n, 1, f) !=1 ) 
    { 
       printf("fwrite error.\n");
       fclose(archivo);
       return -1;
    }

    bytes+=n;
}

printf("We received %d bytes",  bytes);

When used through a local TCP/IP socket it works, but not in a slow connection. I see through debugging that I get a lot of 64 byte chunks, and a 30 byte chunk near EOF. I know that you can get less bytes on read() since the call returns when any data (>1 byte) is available. But this condition shouldn't be catched by the while? Should return when n == 0, that is no more data (EOF).

Thx for your help.

(EDIT)

Sending code as follows:

while (n=read(file_fd, buffer, BUFSIZE))
{
   write (sdaccept, buffer, n)
}

I know that both read() and write () may return N < BUFSIZE, but shouldn't this loop work out that accordingly? I added up n and returns 1000000, the exact size.

(EDIT II)

Tested with a C source with 10673 bytes, receives 10575 without corruption, except that the destination file LACKS the first 98 bytes!!!

Hernán
  • 4,527
  • 2
  • 32
  • 47

1 Answers1

11

The sending code provided ignores the fact that write() (or send() ) on a socket is not obliged to write the whole buffer.

write()/send() might decide to write it partially or not write at all if the underlying subsystem refuses to receive more data (for example the network subsystem may have a queue for the data to send and this queue is already full). That's highly likely on a slow connection.

The sending side should check the return value of write() to detect how much data has been actually written and adjust accordingly.

Write should be done somehow like this:

int readAmount;
while( readAmount = read(file_fd, buffer, BUFSIZE) > 0 )
{
    int totalWritten = 0;
    do {
       int actualWritten;
       actualWritten = write (sdaccept, buffer + totalWritten, readAmount - totalWritten);
       if( actualWritten == - 1 ) {
           //some error occured - quit;
       }
       totalWritten += actualWritten;
    } while( totalWritten < readAmount );
}
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • Sending code is prone exactly to the problem I described. Suggested fix added. – sharptooth Mar 26 '09 at 07:16
  • More specifically, there are 1M bytes write but less received ! :( – Hernán Mar 27 '09 at 04:55
  • You mean, you sum the totalWritten for all the iterations and it's 1M but the receiving party still receives incomplete data? – sharptooth Mar 27 '09 at 05:27
  • The last received data size is 30 bytes , seems that after that receipt reads gets EOF – Hernán Mar 27 '09 at 05:31
  • You should try to detect what data is missing exactly. For example, after you've read a block from disk, write an integer into its first four bytes and increment that integer. Inspect it on the receiving side. – sharptooth Mar 27 '09 at 05:33
  • Used cmp -b file1 file2 both seems to be equal!!! I will try to modify the input like you say. – Hernán Mar 27 '09 at 05:38
  • They can't be equal if they are of different size. Perhaps they are equal from beginning to the end of the shortest file. This means that the end of file gets lost. – sharptooth Mar 27 '09 at 05:41
  • tested now with C source (10673 bytes), receives 10575. Lacks the FIRST 98 bytes, not the last!! – Hernán Mar 27 '09 at 05:42
  • That's strange. Doesn't any of read/write functions ever return -1 in the process? – sharptooth Mar 27 '09 at 05:47
  • Look this is not my code, i'm fixing a total mess. how about sending through the same socket previous to the read in the receiver? This program sends the file name to the server before the while loop. Maybe there are some chars left there? I need to flush that socket.. :) – Hernán Mar 27 '09 at 06:00
  • How do you send the filename? Just the buffer without specifying the name length? – sharptooth Mar 27 '09 at 06:07
  • This program was coded horribly. In the depths of nested function calls, I found a recv lying there!!! MY GOD! I'm on the right path now. The programmer must be shot. – Hernán Mar 27 '09 at 06:12
  • This answer is not correct. In blocking mode, POSIX requires that `write()` and `send()` transfer all the data before returning, unless there is an interrupt or error. – user207421 Mar 17 '17 at 00:53