Python TCP Socket recv丢失数据[行为异常]

0 投票
3 回答
4426 浏览
提问于 2025-04-18 06:51

我写了一个简单的套接字客户端,用来在 Python 3.4 中读取数据。

我遇到的问题是,当服务器发送少量数据(大约 1000 字节)时,它能正常读取。但是当处理大量数据(大约 9500 字节)时,它只给我一小部分数据(像 1100 字节左右)。我搞不清楚为什么在处理大量数据时会这样不稳定。我知道我的数据大小没有超过 ssize_t 的最大值 32767

处理小数据时一切正常,但处理大量数据时就完全不一样了。我知道这不是 TCP 服务器的问题,因为我用 PHP 的 TCP 客户端测试过,处理大量数据时也能正常工作。

任何帮助都非常感谢。

import socket
import json

# Written in Python 3.4.

class SocketClient:

    def __init__(self, host, port, format, timeout = None):
        # Constructor
        self.host = host
        self.port = port
        self.format = format
        self.timeout = timeout

        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    def send(self, firstname, lastname, parameters = [], format = 'json'):
        if self.socket is not None:
            self.socket.connect((self.host, self.port))

            data = {}
            data['firstname'] = firstname
            data['lastname'] = lastname
            data['parameters'] = parameters
            data['format'] = format

            self.socket.send(bytes(json.dumps(data), "utf-8"))

            result = self.socket.recv(32767)
            result = result.decode()

            return result

    def shutdown(self):
        if socket is not None:
            self.socket.shutdown(socket.SHUT_RDWR)
            self.socket.close()


if __name__ == __main__:
    client = SocketClient("127.0.0.1", 8080, 'json')
    response = client.send('foo', 'bar', ['foobar'])
    print(response)
    client.shutdown()

3 个回答

0

这就是recv在多种编程语言中是如何工作的…… https://docs.python.org/3.4/library/socket.html#socket.socket.recv

1

你可以使用 recv_into(buffer[, nbytes[, flags]]) 这个方法:

def readReliably(s,n):
    buf = bytearray(n)
    view = memoryview(buf)
    sz = 0
    while sz < n:
        k = s.recv_into(view[sz:],n-sz)
        sz += k
    # print 'readReliably()',sz
    return sz,buf

def writeReliably(s,buf,n):
    sz = 0
    while sz < n:
        k = s.send(buf[sz:],n-sz)
        sz += k
    # obj = s.makefile(mode='w')
    # obj.flush()
    # print 'writeReliably()',sz
    return sz

完整的示例可以查看这里: https://stackoverflow.com/a/55446223/966789

while True:
  sk,skfrom = s.accept()
  sz,buf = io.readReliably(sk,4)
  a = struct.unpack("4B",buf)
  print repr(a)
  # ...
  io.writeReliably(sk,struct.pack("4B",*[0x01,0x02,0x03,0x04]))
2

TCP是一种流式协议。数据是以字节块的形式传输的,块的大小受很多因素影响。其中一个因素是内部缓冲区的大小限制,通常只能存储几千个字节。你不可能一次性读取32767个字节。

使用recv时,你只能保证至少会收到1个字节,最多会收到你指定的字节数。你的代码需要处理这种情况,这意味着你可能需要多次调用recv,直到你获得所需的字节数。换句话说,如果你的协议没有结束消息的标识符或者长度编码,那就是有问题的。在你的例子中,你需要解析json字节流,直到收到一个有效的json表达式。但是像1245.6这样的数据,收到1个字节后就算完成了吗?还是收到12个字节后?又或者更多?

为了修复你的协议,简单地在发送json数据时附加一些长度信息。

在发送数据时,建议使用sendall而不是send

撰写回答