Matt
Matt

Reputation: 1573

Server writing to socket but data not reaching client

I'm writing a simple server for my networks class and I am having trouble getting data transferred to the client (provided by the prof) correctly.

Once I get through all the setup and the connection is established I start reading chunks of the file and writing them to the socket checking to see that the return from the read and write functions match. I'm also keeping a running total of the bytes that are read/written and comparing that to the total file size (via stat.st_size) and they all match up.

No matter how many times I request the same file the log on the server side always has the correct metrics. The client sporadically loses the end of the file. The difference between the actual and expected size is almost never the same from one invocation to the next and it appears to always be the end of the file that's missing, no pieces from the middle. The size of the arrived file is also a multiple of 512 (the chunk size).

So, it seems that some number of whole chunks are making it and then the rest are getting lost somehow.:w

#define CHUNK_SIZE     512
/* other definitions */

int main()
{
   /* basic server setup: socket(), bind(), listen() ...  
      variable declarations and other setup  */

   while(1)
   {
      int cliSock = accept(srvSock, NULL, NULL);
      if(cliSock < 0)
         ; /* handle error */

      read(cliSock, filename, FILE_NAME_SIZE - 1);
      int reqFile = open(filename, O_RDONLY);
      if( reqFile == -1)
         ; /* handle error */

      struct stat fileStat;
      fstat(reqFile, &fileStat);
      int fileSize = fileStat.st_size;

      int bytesRead, totalBytesRead = 0;
      char chunk[CHUNK_SIZE];
      while((bytesRead = read(reqFile, chunk, CHUNK_SIZE)) > 0)
      {
         totalBytesRead += byteasRead;
         if(write(cliSock, chunk, bytesRead) != bytesRead)
         {
            /* perror(...) */
            /* print an error to the log file */
            bytesRead = -1;
            break;
         }
      }
      if (bytesRead == -1)
      {
         /* perror(...) */
         /* print an error to the log file */
         close(cliSock);
         continue;
      }

      /* more code to write transfer metrics etc to the log file */
   }
}

All of the removed error handling code is some flavor of printing an error message to the log file and getting back to the top of the loop.


Edit flipped a < that should have been >

Upvotes: 1

Views: 2538

Answers (1)

caf
caf

Reputation: 239011

Presumably you are unceremoniously closing the socket with close() when you have written all the data you wanted to the socket (or perhaps just exiting the process, which does the same thing).

This is not right - if the other side has sent some data that you haven't read1, the connection will be reset. The reset can cause unread data to be lost.

Instead, you should use shutdown() to gracefully shutdown the writing side of the socket, then wait for the client to close. Something like:

ssize_t bytesRead;
char chunk[CHUNK_SIZE];

shutdown(cliSock, SHUT_WR);

while((bytesRead = read(cliSock, chunk, CHUNK_SIZE)) != 0)
{
    if (bytesRead < 0)
    {
        if (errno != EINTR)
        {
            /* perror() */
            /* print error in log file */
            break;
        }
    }
    else
    {
        /* maybe log data from client */
    }
}

close(cliSock);


1. This can include EOF, if the other side has closed its writing channel.

Upvotes: 2

Related Questions