在 C 中跳出 recv() 循环的正确方法

Proper way to break out of a recv() loop in C

我写了一个 server/client 程序,我可以在其中将文件从客户端复制到服务器。问题是当写入文件完成时,我无法跳出服务器中的 while 循环。我只能在关闭我的服务器程序时打开新复制的文件,因为该文件不会用 fclose(copyFile) 关闭。文件每次都成功复制。然而,当我在同一台机器上 运行 server/client 但当我将客户端移动到另一台电脑时,它确实工作正常,服务器一直阻塞在我服务器的 recv() 上。

服务器:

while (1)
{
    int res = recv(s, buf, BUFSIZ, 0);
    if (res == SOCKET_ERROR)
    {
        printf("3 error %d\n", WSAGetLastError);
        break;
    }
    fwrite(buf, 1, res, copyFile);
    if (strncmp(buf, "exit", 4) == 0)
    {
        break;
    }
}
fclose(copyFile);

客户:

while (size == BUFSIZ)
{
    size = fread(buf, 1, BUFSIZ, originalFile);
    int r = send(ClientSocket, buf, size, 0);
    if (r == SOCKET_ERROR)
    {
        printf("1 error: %d\n", WSAGetLastError);
    }
}
int r = send(ClientSocket, "exit", 4, 0);
if (r == SOCKET_ERROR)
{
    printf("2 error: %d\n", WSAGetLastError);
}
fclose(originalFile);

如何正确退出服务器中的 while() 循环?

您正试图用“退出”一词指示文件的结尾,但至少有两个问题:

  • 如果正在传输的文件包含单词“exit”,那么它可能会被误解为 end-of-file 标记,并且

  • 您似乎假设您在客户端的 send() 调用将与 one-to-one 在服务器端的 recv() 调用配对,因此“退出" 可以依赖于在服务器接收缓冲区时出现在缓冲区的开头。那是不是一个安全的假设。

(另请注意,即使服务器碰巧在缓冲区的开头接收到“exit”,您仍然会在将其识别为 end-of-file 标记之前将其写入文件。如果这个是唯一的问题,那么很容易解决。)

您需要一个可行的 application-level 协议来帮助客户端和服务器进行通信 -- 在套接字连接提供的平面字节流之上将所需的附加结构分层。 “退出”终止符是一种尝试,但它不是一个可行的解决方案,至少它本身不是。

相比之下,考虑 HTTP:消息由一系列 headers 组成,这些序列可以单独或作为一个组通过其词法形式识别,后跟消息 body。 headers 可以传达的是消息的长度 body,这就是接收者如何在发送者关闭连接的情况下识别消息的结尾 body。

您不需要实现 HTTP,但可以从中得到启发。如果您想传输任意长度和内容的消息,而不通过关闭连接来表示消息结束,那么您最好的办法是提前告诉接收者消息中预期有多少字节。这可能就像在 fixed-length message-length 字段前添加一样简单。