我对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线上,而不会读任何东西。
现在,我确定问题不是由连接错误引起的,因为我正在测试同一主机上的两个进程。在您询问“256字节数据包大小是什么?”之前,如果我使用的缓冲区大小为1500,那么这个奇怪的错误会在realFileSize = ntohl(fileSize);
客户端行上引发分段错误。< / p>
你能告诉我在这里缺少什么吗?
编辑:我正在尝试使用不同的文件大小。它似乎处理大于256字节的文件没有问题(它在客户端和服务器上正确地进入和退出while循环),但是当文件大于300 kb时,客户端将开始出现问题。
编辑2:我刚刚调试了程序。显然,服务器在客户端甚至可以进入while循环之前发送整个文件。
编辑3:我想我知道是什么导致了这个问题。似乎服务器在客户端开始读取之前发送了一堆数据包,客户端将读取多达278个数据包,无论其大小如何。如果我在客户端开始读取之前尝试发送,比如279,则不会读取第279个数据包。因此,如果服务器足够快地发送其数据包,则客户端尚未读取的数据包数量将超过278,客户端将无法读取所有数据包。关于如何解决这个问题的任何想法?答案 0 :(得分:1)
long* fileSize
声明了一个指向long的指针,但是在你的代码中,它指向了无处。实际上,它指向一个随机地址。您应将其声明为long fileSize
,然后再拨打read(socketTCP, &fileSize, sizeof(long))
。read
,write
等的返回值,以确保它们没有失败。例如,sendto出错时返回-1。您忽略了这一点,并且无论如何都要使用此值递增sizeCheck
。-Wall -Wextra
编译代码。编译器将为您提供有关可能出错的提示。我发现你在比较中仍在使用*fileSize
,这显然是错误的。*fileSize
问题后,您的循环条件仍然使用错误的值(由于fileSize = ntohl(fileSize)
)。您需要将此值存储在另一个变量中,或更改您的循环条件以使用实际文件大小。关于您的编辑3,您需要以某种方式同步您的客户端&amp;服务器,所以他们可以同时开始传输。但是,比接收器快得多的发送方仍会导致丢包。要解决此问题,您还需要实现数据包确认,如果发送方在超时后没有收到相应发送数据包的ACK,则重新传输数据包。这是TCP已经为您做的事情。
一种更简单(但不是完全可靠)的方法是稍微减慢发送过程 - 可能在每次调用nanosleep
之间使用sendto
。