在Python中发送文件的最佳块大小?

时间:2014-07-14 17:37:28

标签: python sockets

我实际上使用python的(2.7)ftplib将文件发送到FTP服务器,但它使用socket.sendall。感兴趣的功能如下:

def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
    """Store a file in binary mode.  A new port is created for you.

    Args:
      cmd: A STOR command.
      fp: A file-like object with a read(num_bytes) method.
      blocksize: The maximum data size to read from fp and send over
                 the connection at once.  [default: 8192]
      callback: An optional single parameter callable that is called on
                on each block of data after it is sent.  [default: None]
      rest: Passed to transfercmd().  [default: None]

    Returns:
      The response code.
    """
    self.voidcmd('TYPE I')
    conn = self.transfercmd(cmd, rest)
    while 1:
        buf = fp.read(blocksize)
        if not buf: break
        conn.sendall(buf)
        if callback: callback(buf)

    conn.close()
    return self.voidresp()

我正在尝试选择最佳块大小,或至少了解影响它的事物。该代码目前在本地千兆网络上运行,在Ubuntu内核3.2上对FTP服务器的ping时间为0.2ms(是,0.2ms,而不是0.2s)。我对TCP窗口缩放和发送/接收/拥塞窗口有一个很好的理解。我正在通过该网络发送2GB文件,并且在实践中发现传输速度随块大小而增加,使用256KB块大小时高达533Mb / s。作为参考,64KB的块大小约为330Mb / s。

我不是以任何方式抱怨这些速度,而是想了解为什么256KB块大小是最佳的。到目前为止我发现的所有内容都表明~64KB是所需的大块大小。我已经定时storebinary函数的子组件,以确保发送文件的总时间实际上减少,因为块大小增加到256KB(而不是读取文件所花费的时间)。

我传输这些2GB文件的代码最终将在许多网络上运行(尽管操作系统,内核,python版本相同)。我担心256KB在其他网络上是次优的,我很好奇为什么256KB块大小提供最快的传输速度。任何见解将不胜感激。

编辑:对于那些关心我如何定时实际执行socket.sendall调用的人,这里是我用来计时的函数的修改版本。从64KB块到256KB块,读取时间从~19s到~14s,发送时间从~18s到~10s。

def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
    """Store a file in binary mode.  A new port is created for you.

    Args:
      cmd: A STOR command.
      fp: A file-like object with a read(num_bytes) method.
      blocksize: The maximum data size to read from fp and send over
                 the connection at once.  [default: 8192]
      callback: An optional single parameter callable that is called on
                on each block of data after it is sent.  [default: None]
      rest: Passed to transfercmd().  [default: None]

    Returns:
      The response code.
    """
    self.voidcmd('TYPE I')
    conn = self.transfercmd(cmd, rest)
    totalTime = 0
    totalSendTime = 0
    totalCallbackTime = 0
    while 1:
        startTime = time.time()
        buf = fp.read(blocksize)
        endTime = time.time()
        if not buf: break
        totalTime += (endTime - startTime)
        startTime = time.time()
        conn.sendall(buf)
        endTime = time.time()
        totalSendTime += (endTime - startTime)
        startTime = time.time()
        if callback: callback(buf)
        endTime = time.time()
        totalCallbackTime += (endTime - startTime)

    print 'Total read time was %s'%str(totalTime)
    print 'Total send time was %s'%str(totalSendTime)
    print 'Total callback time was %s'%str(totalCallbackTime)
    conn.close()
    return self.voidresp()

1 个答案:

答案 0 :(得分:1)

ftp中的位基于数据报,因此它们通过固定路径以特定大小的数据包发送。要发送所有数据,您需要确定整个文件的大小,然后在ftp端预期相同。更好的方法是在文件末尾添加结束分隔符。因此,当您遍历ftp端的文件内容并找到结束分隔符时,您必须停止期望来自同一客户端的文件中的更多数据。保持单次发送中传输的位的标称大小约为1024,由于各种原因,这是首选大小(请在Google上找到原因,您可以轻松找到)。