为什么在Python中从套接字接收字符串直到\n换行符?

9 投票
1 回答
32597 浏览
提问于 2025-04-17 07:44

我本来是想通过一个TCP连接接收两行字符串,但实际上我只收到了第一行,直到换行符为止。

     socket = socket(AF_INET,SOCK_STREAM)
     socket.connect((ip,port))
     data = socket.recv(1024)
     print "%s" % data

如果我现在调用接收函数,它会接收到换行后的第二部分字符串。但这并不是我想要的。其实它应该在第一次调用时就返回整个字符串。当我用nc命令连接到服务器时,刚开始连接时我能正常收到字符串。为什么会这样呢?

1 个回答

11

因为TCP是一种流式协议(通过SOCK_STREAM可以看出来),所以它没有固定的消息边界或数据包。你不能仅仅通过一次调用recv就确定你收到了所有的数据。你需要在一个循环中不断调用recv,把接收到的数据添加到一个缓冲区中,直到你觉得数据够了为止。大多数只处理文本的协议会使用换行符来表示读取结束,并对接收到的数据进行处理。其他协议可能会使用其他字符或字节序列。

在你的情况下,如果没有特殊字符来表示当前数据的结束,你有两个解决方案:

  1. 使用超时:如果一段时间内没有收到新数据,就打印已经接收到的数据。

  2. 非阻塞套接字:简单地在循环中读取数据,把数据添加到内部缓冲区。当调用recv时出现错误,且errno等于errno.EWOULDBLOCK,这时就表示暂时没有更多数据可以读取,可以打印已接收到的数据。

第二种方案,加上select这个包,可能是最好的选择。

编辑

这里有一个简单的例子来说明我的意思。这个例子可能不能直接工作,需要一些调整,但希望它能给你提供一些基础。

# Need to import: package socket, package select, package errno

# Create socket and connect to server

# Make the socket non-blocking (see http://docs.python.org/library/socket.html#socket.socket.setblocking)
socket.setblocking(0)

run_main_loop = True
while run_main_loop:
    # Wait for events...
    read_ready, _, _ = select.select([socket], None, None)

    if socket in read_ready:
        # The socket have data ready to be received
        buffer = ''
        continue_recv = True

        while continue_recv:
            try:
                # Try to receive som data
                buffer += socket.recv(1024)
            except socket.error, e:
                if e.errno != errno.EWOULDBLOCK:
                    # Error! Print it and tell main loop to stop
                    print 'Error: %r' % e
                    run_main_loop = False
                # If e.errno is errno.EWOULDBLOCK, then no more data
                continue_recv = False

    # We now have all data we can in "buffer"
    print '%r' % buffer

socket.close()

撰写回答