C套接字,不完整的文件传输

时间:2014-08-24 21:07:52

标签: c sockets network-programming blocking

我正在将 C 程序写入将固定大小的文件(从服务器传输到客户端稍微超过2Mb)。我在 Linux 上使用 TCP套接字,我写的代码如下:

服务器(发件人)

while (1) {
    int nread = read(file, buffer, bufsize);
    if (nread == 0) // EOF 
        break;

    if (nread < 0) {
        // handle errors
    }

    char* partial = buffer;
    while (nread > 0) {
        int nwrite = write(socket, partial, nread);
        if (nwrite <= 0) {
            // handle errors
        }
        nread   -= nwrite;
        partial += nwrite;
    }
}

// file sent
shutdown(socket, SHUT_WR);

客户(接收方)

while (filesize > 0) {
    nread = read(socket, buffer, bufsize);
    if (nread == 0) {
        // EOF - if we reach this point filesize is still > 0
        // so the transfer was incomplete
        break;
    }

    if (nread < 0) {
        // handle errors
    }

    char* partial = buffer;
    while (nread > 0) {
        nwrite = write(file, partial, nread);
        if (nwrite <= 0) {
            // handle errors
        }
        nread    -= nwrite;
        partial  += nwrite;
        filesize -= nwrite;
    }
}

if (filesize > 0) {
    // incomplete transfer
    // handle error
}

close(socket);

在我的笔记本电脑上测试代码时(客户端和服务器&#34;在本地主机上#34;并且通信发生在环回接口上),有时客户端会退出,因为read收到 EOF ,而不是因为它收到了所有filesize个字节。由于我在服务器上使用shutdown,这意味着没有其他数据可供阅读。 (请注意,服务器发送了所有字节并正确执行了shutdown

你能解释一下为什么会这样吗?

丢失的字节在哪里?

-----

编辑1 - 澄清

有些用户提出了一些澄清,所以我在这里发布答案:

  • 该程序正在使用 TCP阻止套接字
  • filesize是一个固定值,在客户端和服务器中都是硬编码的。
  • 没有特殊套接字选项,例如 SO_LINGER 已启用/使用。
  • 发生错误时,服务器(发件人)已经发送了所有数据并正确执行了shutdown
  • 截至今天,当客户端和服务器在不同的机器上测试应用程序(通过真实网络接口传输而不是环回接口)时,从未发生过错误。

编辑2

用户 Cornstalks pointed me一个非常有趣的article关于TCP的非常可靠的行为。

这篇文章非常值得一读,描述了在TCP套接字之间发送未知数据量时的一些技巧。描述的技巧如下:

  1. 利用发件人的SO_LINGER选项。这将有助于在致电close(2)shutdown(2)后打开套接字,直到所有数据都已成功发送。
  2. 在接收器上,请注意实际接收循环之前的可读数据。它可能导致立即重置。
  3. 利用shutdown(2)向发件人发送数据的接收者发出信号。
  4. 让接收者知道在实际发送文件之前将发送的文件的大小。
  5. 让接收方确认发送方接收循环结束。这有助于防止发件人过早关闭套接字。
  6. 阅读完文章后,我升级了我的代码以实现1号和5号技巧。

    这就是我实现5号技巧的方法:

    服务器(发件人)

    // sending loop ...
    
    // file sent
    shutdown(socket, SHUT_WR);
    
    // wait acknowledgement from the client
    ack = read(socket, buffer, bufsize);
    if (ack < 0) {
        // handle errors
    }
    

    客户(接收方)

    // receiving loop..
    
    if (filesize > 0) {
        // incomplete transfer
        // handle error
    }
    
    // send acknowledgement to the server
    // this will send a FIN and trigger a read = 0 on the server
    shutdown(socket, SHUT_WR);
    
    close(socket);
    

    2号,3号和4号技巧怎么样?

    不需要

    Trick number 2 ,因为只要服务器接受连接,应用程序就会继续进行文件传输。没有额外的消息被交换。

    特技3 已经实施

    特技4号也已实施。如前所述,文件大小是硬编码的,因此无需更换文件。

    这是否解决了我原来的问题?

    我的问题没有解决。错误仍在发生,截至今天,只有在localhost上使用客户端和服务器测试应用程序时才会发生错误。

    你怎么看?

    有没有办法阻止这种情况?

1 个答案:

答案 0 :(得分:0)

您&#39;重:

  1. 假设read填充缓冲区,即使
  2. 你在write()没有写完整个缓冲区的情况下进行了很好的辩护。
  3. 您需要执行(1),并且您不需要执行(2)因为您处于阻止模式且POSIX确保write()不会返回所有数据是写的。

    两个循环的简单版本:

    while ((nread = read(inFD, buffer, 0, sizeof buffer)) > 0)
    {
        write(outFD, buffer, 0, nread);
    }
    if (nread == -1)
        ; // error
    

    更正确的版本会检查write()的结果当然是错误。