【问题标题】:Should I send a file over sockets in one read\write or multiple chunks?我应该在一个读\写或多个块中通过套接字发送文件吗?
【发布时间】:2015-08-21 06:17:50
【问题描述】:

我有一个关于如何在 C++ 中实现基本文件传输客户端\服务器的基本问题。

我不知道为什么它不是一次读\写发送文件的好方法,为什么好的方法是以小缓冲区的块发送文件?

【问题讨论】:

    标签: c++ sockets ftp


    【解决方案1】:

    如果您想使用基于 IP 的协议来发送此文件,无论如何您都将受限于大约 64K 的 IP 数据包大小。

    如果您使用 UDP,则您的传输不可靠,但最好将数据包大小限制在 450 字节左右,以避免部分数据包丢失。您还必须在每次发送中设置某种序列号,以检测这样的块是否丢失,并且可能是一种确认机制。 TFTP 已经有了这样的机制。

    如果您使用可靠的 TCP,那么您不必切入 450 字节,您可以使用 32000 字节甚至 64K(不是 TCP 的限制,但您避免需要大量内存,读取和发送 32k 块是完全合理的)。 TCP 将根据 Mtu 发送。所以要做好准备,在接收端你不会收到 32000 的数据包。

    出于性能原因,您希望磁盘访问是“更大的块”。每个字节读取和发送字节可能会起作用,但速度会非常慢。

    【讨论】:

    • 你不受IP包大小的限制。 TCP 将负责所有需要的数据包化。您可以根据需要写入 bg 块。然而,比你自己的套接字发送缓冲区大的块是毫无意义的,就像在发送之前尝试将整个文件加载到内存中一样。
    • 正如您在我的声明中看到的那样,TCP 将根据 MTU 进行发送。我们已经确定在内存中加载 1TB 并尝试发送它是不明智的。读取 32k 或 64k 的块很可能会产生类似的结果。所以使用更大的块是没有意义的。
    【解决方案2】:

    我不知道为什么它不是一次读\写发送文件的好方法,为什么好的方法是以小缓冲区的块发送文件?

    如果您有一个大小为 2TB 的文件,您首先需要分配此数量的 RAM 并将整个文件加载到该单个缓冲区中。然后你需要写出所有这个缓冲区。由于内存不足,这对于 2TB 文件可能不会成功,但即使对于较小的文件,这也会浪费资源。由于从磁盘读取和对网卡的写入无论如何都是在内部以块的形式完成的,即使整个文件适合 RAM,您也不会获得更好的性能。

    可能的妥协可能是读取/写入 4k 到 32k 之间的块,最佳大小取决于操作系统、磁盘缓冲区、套接字缓冲区、磁盘和网络的速度等。

    【讨论】:

    • 谢谢!您是否建议使用诸如 open & pread 之类的函数或更简单的函数 fstream 等阅读?
    • fstream 引入了额外的缓冲和开销,因此如果您可以自己进行最佳缓冲区管理,那么 read/readv/pread 会更好。如果您只想从文件传输到网络 sendfile 可能会更好,因为您不必在内核和用户空间之间复制数据。
    • 好吧,你可以在一个块中写入一个大文件,而不需要相应的内存:只需mmap() 文件,然后调用write() 来完成整个事情。内核会解决剩下的问题。
    • @cmaster 假设您的内核可以内存映射 TB。不推荐的技术。
    • @EJP 在 64 位系统上应该没问题。毕竟,内核永远不必一次真正加载整个文件,甚至尝试这样做。它可以简单地重用包含文件数据的内存页面,因为它知道它可以从磁盘重新加载这些数据,而无需实际换出任何东西。尽管如此,由于缺少 2TB 文件,我还没有尝试过,但我知道它适用于大于主内存的文件。当然,使用像sendfile() 这样的零拷贝系统调用可能是最好的主意。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多