0

我对 C 中的套接字编程相当陌生,所以下面的代码可能有很多新手错误。我正在尝试制作一个客户端-服务器应用程序,其中服务器将使用 UDP 套接字将文件传输到客户端。客户端和服务器都将在 Linux 主机上运行。这是一项任务,因此必须以这种方式完成。其他客户端-服务器通信可能使用 TCP 套接字,但文件传输必须通过 UDP。该程序适用于小文件,但如果我尝试发送稍大的文件(例如,600 kb 的文本文件),客户端将停止接收数据包,即使服务器会将它们全部发送。这是服务器代码的文件传输部分:

   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);

如您所见,我使用 TCP 套接字向客户端发送文件大小。这是客户端代码:

   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);

“bufferRW”缓冲区最初声明为 char bufferRW[256] 并作为参数传递给函数。其他未声明的变量也是如此。就像我之前说的,服务器(显然)会毫无问题地发送整个文件。但是,客户端将在写入大约 423936 字节后停止接收数据包(这可能因执行而异)。它只会停留在 recvfrom 行,而不读取任何内容。

现在,我确定问题不是由连接错误引起的,因为我正在同一主机上测试这两个进程。realFileSize = ntohl(fileSize);在你问“256 字节的数据包大小是怎么回事?”之前,如果我使用 1500 的缓冲区大小,有一个奇怪的错误会在客户端线路上引发分段错误。

你能告诉我我在这里错过了什么吗?

编辑:我现在正在尝试使用不同的文件大小。它似乎可以毫无问题地处理大于 256 字节的文件(它在客户端和服务器上都正确地进入和退出 while 循环),但是当文件大于 300 kb 时,客户端将开始出现问题。

编辑2:我刚刚调试了程序。显然,服务器在客户端甚至可以进入其 while 循环之前发送整个文件。

编辑3:我想我知道是什么导致了这个问题。似乎如果服务器在客户端开始读取之前发送了一堆数据包,那么客户端将读取多达 278 个数据包,而不管它们的大小。如果我尝试在客户端开始读取之前发送 279,它将不会读取第 279 个数据包。因此,如果服务器发送数据包的速度足够快,则客户端尚未读取的数据包数量将超过 278 个,并且客户端将无法完成所有数据包的读取。有想法该怎么解决这个吗?

4

1 回答 1

1
  1. long* fileSize声明了一个指向 long 的指针,但在您的代码中,它没有指向任何地方。事实上,它指向一个随机地址。您应该将其声明为long fileSize, 并read(socketTCP, &fileSize, sizeof(long))改为调用。
  2. 您应该检查 , 等的返回值readwrite以确保它们没有失败。例如,sendto出错时返回 -1。你忽略了这一点,并且sizeCheck无论如何都会增加这个值。
  3. UDP 不是一个可靠的文件传输协议,但如果你不能没有它,你最好实现一些 TCP 已经免费提供的控制,如数据包重新排序、数据校验和等。这本身可能是一项非常复杂的任务.
  4. 用 编译你的代码-Wall -Wextra。编译器会提示您可能出现的错误。我看到您仍在*fileSize比较中使用,这显然是错误的。
  5. 解决*fileSize问题后,您的循环条件仍在使用错误的值(由于fileSize = ntohl(fileSize))。您需要将此值存储在另一个变量中,或更改循环条件以使用实际文件大小。

关于您的 EDIT 3,您需要以某种方式同步您的客户端和服务器,以便它们可以同时开始传输。但是,比接收者快得多的发送者仍然会导致丢包。为了解决这个问题,您还需要实现数据包确认,如果发送方在超时后没有收到相应发送数据包的 ACK,则重新传输数据包。这是 TCP 已经为您做的事情。一种更简单(但不完全可靠)的方法是稍微减慢发送过程 - 可能nanosleep在每次调用sendto.

于 2013-04-29T03:14:56.057 回答