Python Socket Flush

时间:2010-12-10 10:24:46

标签: python sockets

我试图确保每次调用socket.send函数时,我的缓冲区被发送(刷新)到我的服务器(使用unix套接字在C中)。

根据我的理解(以及我在这个板上看到的内容),只是禁用了naggle算法。应该这样做,但我的服务器仍然以4096字节(默认设置)...

的块接收我的数据

我在Python v2.5.4中使用以下代码:

 self.sck = socket( AF_INET, SOCK_STREAM )

 self.sck.setsockopt( IPPROTO_TCP, TCP_NODELAY, 1 ) # That doesn't seems to work...

 self.sck.connect( ( "127.0.0.1", "12345" ) )

 while( 1 ):
      self.sck.send( "test\n" )
      self.sck.send( "" ) # Still trying to flush...

启用/禁用TCP_NODELAY似乎没有任何影响......这是一个错误还是我遗漏了什么?

TIA

5 个答案:

答案 0 :(得分:7)

TCP不提供任何类型的保证“数据包”发送到另一端。您正在尽可能快地发送数据,并且TCP有助于将数据批处理到尽可能多的时间。您的服务器一次接收4096字节的数据,这可能是因为它要求的内容(在recv()调用中)。

TCP是一种流协议,因此您必须自己实现某种框架。没有内置的消息边界。

答案 1 :(得分:5)

我获得同花顺(回到C客户端)的唯一方法是使用:

my_writer_obj = mysock.makefile(mode='w', ...)
my_writer_obj.write('my stuff')
my_writer_obj.flush()

最大的好处是my_writer_obj默认为文本模式,因此不再需要字节翻译。

创建一个阅读器对象并不顺利,但我不需要那边的同花顺。

答案 2 :(得分:4)

无法确保发送的数据块的大小。如果要确保发送所有要发送的数据,可以关闭连接:

self.sck.close()

另请注意,n = socket.send()返回实际发送的字节数。如果您确定要发送所有数据,则应使用

self.sck.sendall()

或循环数据发送:

while data:
    n = self.sck.send(data)    
    data = data[n:]

(但这与sendall()大致相同)。 如果要以更大的块接收数据,可以在recv()中增加缓冲区的大小,但这只会使可能的块大小更大。没有保证数据以这些尺寸到达。

答案 3 :(得分:1)

这是关于TCP协议的常见问题。 TCP本身无法以特定块发送数据。它仅用于发送数据流。如果您需要此类功能,则应自行实施。例如,在单独的行中发送您的块,或者首先发送块大小,然后发送块本身。

在大多数情况下,您不需要关心Naggle算法。该算法更好地用名称TCP_NODELAY描述。如果你禁用它,你可以实现小块的较小延迟,但同时降低大块的速度。

答案 4 :(得分:0)

在 Linux 中,这可以通过调用 ioctl 来实现:

<块引用>

SIOCOUTQ
返回套接字发送队列中未发送的数据量。 > 套接字不得处于 LISTEN 状态,否则将返回错误 (EINVAL)。 SIOCOUTQ 在 中定义。或者,您可以使用 > 在 中定义的同义 TIOCOUTQ。

快速查看linux/sockios.h

/* Linux-specific socket ioctls */
#define SIOCINQ         FIONREAD
#define SIOCOUTQ        TIOCOUTQ        /* output queue size (not sent + not acked) */

因此,应用此方法相当容易:

import socket

def flush_socket(sock: socket.socket) -> bool:
    from ctypes import c_ulong
    from time import sleep
    from termios import TIOCOUTQ
    from fcntl import ioctl

    while sock.fileno() != -1: # if fd is -1, then it has been probably close()'d
        remaining = c_ulong.from_buffer_copy(
            ioctl(sock.fileno(), TIOCOUTQ, bytearray(8), False)).value

        if remaining == 0:
            # all data has been sent and ACKed
            return True

        # wait a bit before retrying,
        # sleep(0) was meant like yield current thread,
        # but will probably be close to busy-waiting,
        # feel free to change it to fit your needs
        sleep(0)

    # not all data has been sent
    return False