【问题标题】:File Transfer only complete if server disconnects文件传输仅在服务器断开连接时完成
【发布时间】:2025-11-22 21:35:01
【问题描述】:

我对下面的代码有一些问题。它旨在从服务器接收文件。只要服务器在发送后断开连接,它就可以 100% 正常工作。

如果服务器没有断开连接,那么客户端的传输没有完成,客户端继续等待字节并且没有正确地跳出循环(当 fr_block_sz == 0 时),就像它所做的那样当服务器收到文件后断开连接时。它无休止地坐在循环中。

我希望能够接收文件并在之后保持连接打开。有什么建议吗?谢谢。

编辑:根据 Casey 的建议,我现在正在监视通过下面注释的代码写入的字节数,但出现了导致文件传输无法正确完成的奇怪行为。任何人都可以在我的代码中看到任何错误吗?谢谢。

编辑 2:我已经发布了关于我的问题的调试信息,以使其更清楚。

// Reciever of file (client)
int TotalSize = 7374; // hardcoded file size to expect
int FileSize = 0; // counter
int fr_block_sz = 0; 
while ((fr_block_sz = recv(sd, revbuf, 1, 0)) > 0)  //LENGTH == 512
{
    if (fr_block_sz < 0)
        if (errno == EAGAIN)
            continue;
        else
            printf("File write failed");

        if (fr_block_sz == 0)           
            break; //done

        // edit based on comment - does not change behavior
        if(FilzeSize > TotalSize)
        {
            fr_block_sz = fr_block_sz - (FileSize - TotalSize);   
        }            

        int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr);
        // add total bytes recvd every loop
        FileSize += write_sz;
        // if bytes recieved equals expect file size, break
        if (FileSize >= TotalSize)// this is where my problem is, as sometimes 
                                  // FileSize == 7376 or 7672 instead of 7374
                                  // When it fails, fr_block_sz == 24 which
                                  // i don't think is right either. Even when
                                  // set to break if FileSize > TotalSize, the 
                                  // loop still doesn't complete
            break;
        printf("Recieved size == %i of %i \n", FilzeSize, TotalSize);

        if (write_sz < fr_block_sz)
        {
            printf("File write failed");
        }

        bzero(revbuf, 1);

}
printf("Transfer complete \n");

控制台输出:

Recieved size == 512 of 7374 
Recieved size == 1024 of 7374 
Recieved size == 1368 of 7374 
Recieved size == 1880 of 7374 
Recieved size == 2392 of 7374 
Recieved size == 2736 of 7374 
Recieved size == 3248 of 7374 
Recieved size == 3760 of 7374 
Recieved size == 4104 of 7374 
Recieved size == 4616 of 7374 
Recieved size == 5128 of 7374 
Recieved size == 5472 of 7374 
Recieved size == 5984 of 7374 
Recieved size == 6496 of 7374 
Recieved size == 6840 of 7374 
Recieved size == 7352 of 7374 
Recieved size == 7376 of 7374 

一旦它到达这里,客户端将在循环中永远在这里等待,并且没有任何反应。然后我必须从控制台手动停止程序,在程序停止后,只写入了 4096 字节的文件。如果有人能阐明我的问题,或者用“正常”的方式来解决这个问题,我将不胜感激。谢谢你。

编辑 - 已修复(有点)。我找到了一种让它每次都能 100% 可靠工作的方法,那就是通过更改 LENGTH == 1,因此设置一个字节的缓冲区大小,如果内核正在逐字节写入,则无法获得短/长读取.这可行,但显然不是最快的解决方案。我仍然想听听其他人的意见,并感谢您对 Rajal 的帮助。

【问题讨论】:

  • 所以你必须改为查看服务器代码
  • 无关:在用其他数据填充缓冲区之前先用零填充缓冲区是没有意义的。
  • Casey - 已修复,bbonev - 含糊评论,跟进。

标签: c linux file transfer


【解决方案1】:

您的问题是如果服务器没有关闭连接,您的客户端不知道文件传输何时完成。你怎么能传达这个事实?

【讨论】:

  • 我已经尝试对服务器进行编码以在发送后发送确认字节,并且确实如此。它仅在服务器断开连接时才有效,然后客户端写入文件 + 1 个字节(这是确认),我无法弄清楚如何与循环中的特定字节进行比较。我假设我需要让服务器发送一个字节,比如说 0xAA,然后当客户端收到该字节时,它可以跳出循环?
  • 但是如果文件数据中有 0xAA 会发生什么?请记住,您不能相信您从recv 获得的数据块按照发送时完全相同的方式进行划分:您可以保证排序,但这些数据块可以拆分或合并。
  • 提示:让服务器在发送数据之前告诉客户端数据的长度。
  • 是的,我看到通过设置一个计数器并执行 counter += write_sz 每次迭代,您可以看到有多少文件被写入并在达到一定大小时中断。谢谢。
  • 嘿,凯西,我回去发布了最初有效的内容,但现在我收到的写入大小有时比文件大小大 2 个字节,这导致代码挂起。你知道我上面的代码有什么问题吗?
【解决方案2】:

我建议在客户端和服务器之间遵循一些协议。我可以举一个例子如下。

  1. 假设在服务器和客户端中,任何通信的前 4 个字节都是文件的大小。前 4 个字节由服务器为每个已建立的连接发送,直到文件完全发送。对于另一个文件 /conenction,服务器将再次发送 4 个字节来告知文件的大小。
  2. 因此,发送给客户端的前 4 个字节将准确指示它必须处理的数据量。
  3. 客户端最初只会读取 4 个字节,而不是假设缓冲区已满,因为数据会将 4 个字节转换为整数,以便它可以获取要接收的确切字节数。不需要确认字节,放在这里也没用。
  4. 一旦接收到的字节数等于服务器发送的字节数,则假定文件已接收并且服务器可以关闭连接。
  5. 您可以在接收到文件中的字节数后假设下一个字节作为服务器准备发送另一个文件的指示符,或者如果您希望每个连接接收一个文件,那么如果您在缓冲区中收到额外的字节,只需丢弃它。

除了我们有时在下载管理器中看到的以外,还有其他接收文件的方式。就像将文件分成块或使用相同的连接接收多个文件,但我相信一旦您设置了协议,就可以轻松找到增强功能。

希望对你有帮助。

只需丢弃任何超过文件大小的字节。在 fwrite 之前尝试以下操作。

    if(FilzeSize > TotalSize)
    {
        fr_block_sz = fr_block_sz - (FilezeSize - TotalSize);   
    }
    int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr);

它应该写入 revbuf,不包括最后 2 个字节。

问候 卡哈尔

【讨论】:

  • 感谢 Kajal 的回复。我已经成功完成了步骤 1 - 3(虽然有点不同)。然而,在第 4 步中,它经常只是处于循环中,并且文件不会成功完成,尽管它完成了一次。
  • 另外,对于第 5 步,您能否发布一些关于如何忽略多余字节的伪代码,您是否发现我发布的代码有任何问题?谢谢。
  • 酷!尝试在同一连接中一个接一个地发送多个文件:)
  • 我想这样做,但我需要先成功。我在我的代码中发布了调试打印语句并发布了控制台输出,看来内核每次都想多写 2 个字节。你知道它可能是什么吗?
  • 我也认为您在提到 #4 时误解了我 Kajal,我不希望服务器在每次发送文件时都关闭连接。