简体   繁体   中英

Transferring files using UDP sockets in C

I'm fairly new to socket programming in C, so the code below may have a ton of newbie mistakes. I'm trying to make a client-server application in which the server will transfer a file to the client using an UDP socket. Both the client and the server will run on Linux hosts. It's an assignment, so it has to be done that way. Other client-server communications may use a TCP socket, but the file transfer MUST be via UDP. The program works correctly for small files, but if I try to send a slightly larger file (say, a 600 kb text file), the client will stop receiving the packets, even though the server will send them all. Here's the file transfer part of the server code:

   FILE* myFile;
   long fileSize, readBytes, sentBytes, sizeCheck;
   uint32_t encodedFileSize;

   myFile = fopen(fileName, "rb");
   if(myFile == NULL)
   {
      perror("Error when opening file.");
      exit(1);
   }   

   fseek(myFile, 0, SEEK_END);
   fileSize = ftell(myFile);
   encodedFileSize = htonl(fileSize);
   rewind(myFile);
   sizeCheck = 0;

   write(myTCPSocket, &encodedFileSize, sizeof(encodedFileSize));

   if(fileSize > 255)
   {
      while(sizeCheck < fileSize)
      {
         readBytes = fread(bufferRW, 1, 256, myFile);
         sentBytes = sendto(sockfdUDP, bufferRW, readBytes, 0, (struct sockaddr*)&cli_addr, udpAddressSize);
         sizeCheck += sentBytes;
      }
   }
   else
   {
      readBytes = fread(bufferRW, 1, 256, myFile);
      sentBytes = sendto(sockfdUDP, bufferRW, readBytes, 0, (struct sockaddr*)&cli_addr, udpAddressSize);
   }

   if(fileSize == sizeCheck)
   {
      printf("Success.\n");
   }
   else
   {
      printf("Fail.\n");
   }
   fclose(myFile);
   fflush(stdout);
   close(sockfdUDP);

As you can see, I used a TCP socket to send the client the file size. Here's the client code:

   FILE *myFile;
   long receivedBytes, writtenBytes, sizeCheck;
   long fileSize, realFileSize;
   char ack2[5] = "Ok";   
   sockfdUDP = socket(AF_INET, SOCK_DGRAM, 0);

   read(socketTCP, &fileSize, sizeof(long));

   realFileSize = ntohl(fileSize);

   myFile = fopen(fileName, "wb");

   if(myFile == NULL)
   {
     perror("Error when creating file.");
     exit(1);
   }

   sizeCheck = 0;

   if((realFileSize) > 255)
   {
      while(sizeCheck < (realFileSize))
      {
         receivedBytes = recvfrom(sockfdUDP, bufferRW, 256, 0, (struct sockaddr*)&serv_addr, &serv_addr_size);
         writtenBytes = fwrite(bufferRW, 1, receivedBytes, myFile);
         fflush(myFile);
         sizeCheck += writtenBytes;
      }
   }
   else
   {
        receivedBytes = recvfrom(sockfdUDP, bufferRW, 256, 0, (struct sockaddr*)&serv_addr, &serv_addr_size);
        fwrite(bufferRW, 1, receivedBytes, myFile);
        fflush(myFile);
   }

   if(realFileSize == sizeCheck)
   { 
     printf("Success.");
   }
   else
   {
      printf("Fail.");
   }
   fclose(myFile);
   close(sockfdUDP);

The "bufferRW" buffer was originally declared as char bufferRW[256] and passed to the function as an argument. Same goes for other undeclared variables. Like I said before, the server will (apparently) send the whole file without any issues. However, the client will stop receiving packets after it's written about 423936 bytes (this may vary between executions). It'll just stay at the recvfrom line, without reading anything.

Now, I'm sure the problem is not being caused by a faulty connection since I'm testing both processes on the same host. And before you ask "What is it with the 256 byte packet size?", there's this weird bug that will throw me a segmentation fault on the realFileSize = ntohl(fileSize); client line if I use a buffer size of, say, 1500.

Could you please tell me what am I missing here?

EDIT: I'm trying with different file sizes now. It seems to handle files larger than 256 bytes without issue (it enters and exits the while loops correctly on both client and server), but the client will start having problems when the file is bigger than, say, 300 kb.

EDIT 2: I just debugged the program. Apparently, the server sends the entire file before the client can even enter its while loop.

EDIT 3: I think I know what's causing the issue. It seems like if the server sends a bunch of packets before the client starts reading, the client will read up to 278 packets, regardless of their size. If I try sending, say, 279 before the client starts reading it won't read the 279th packet. So if the server sends its packets fast enough, the number of packets that the client hasn't read yet will exceed 278 and the client won't finish reading all of the packets. Any ideas on how to fix this?

  1. long* fileSize declared a pointer to a long, but in your code, it's pointing nowhere. In fact, it's pointing to a random address. You should declare it as long fileSize , and call read(socketTCP, &fileSize, sizeof(long)) instead.
  2. You should check the return value of read , write , etc, to guarantee they did not fail. For example, sendto returns -1 on error. You're ignoring this, and incrementing sizeCheck with this value anyway.
  3. UDP is not a reliable protocol for file transfers, but if you cannot do without it, you better implement some controls that TCP already gives you for free, like packet reordering, data checksum, etc. And that can be a very complex task by itself.
  4. Compile your code with -Wall -Wextra . The compiler will give you hints about what could be potentially wrong. I see you're still using *fileSize in a comparison, which is clearly wrong.
  5. After you fix the *fileSize issue, your loop-condition is still using the wrong value (due to fileSize = ntohl(fileSize) ). You need to store this value in another variable, or change your loop-condition to use the real file size.

Regarding your EDIT 3, you need to somehow synchronise your client & server, so they can start the transmission at the same time. However, a sender that is much faster than the receiver will still cause packet loss. To solve this, you'll also need to implement packet acknowledgement, and retransmit a packet if the sender doesn't receive an ACK for a respective sent packet after a timeout. This is something TCP already does for you. A simpler (but not completely reliable) way, would be to slow down the sending process a bit - maybe using nanosleep between each call to sendto .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM