用python封装socket数据的正确方法?

2024-06-02 06:41:03 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在开发一个应用程序,它通过套接字向另一个实例发送和接收数据,我很好奇用“END”标记封装数据的最有效方法。例如,这里有两个函数用于在套接字连接上读写:

def sockWrite(conn, data):
    data = data + ":::END"
    conn.write(data)

def sockRead(conn):
    data = ""
    recvdata = conn.read()
    while recvdata:
        data = data + recvdata
        if data.endswith(':::END'):
            data = data[:len(data)-6]
            break
        recvdata = conn.read()
    if data == "":
        print 'SOCKR: No data')
    else:
        print 'SOCKR: %s', data)
    return data

我基本上是在写操作上加上“:::END”,因为单次写入可能会发生多个读取。因此,读取循环,直到到达“:::END”。在

如果数据变量包含字符串“:::END”,这当然会导致一个问题,而字符串恰好出现在一次读取的末尾。在

有没有适当的方式来封装尽可能少的带宽?我曾考虑过pickle或json,但担心这会增加大量带宽,因为我相信它们会将二进制数据转换为ASCII。我说得对吗?在

谢谢, 本


Tags: 数据实例字符串标记应用程序readdataif
1条回答
网友
1楼 · 发布于 2024-06-02 06:41:03

你真的需要优化吗?在

通常你发送的信息相对较小。当你看到忽略了多少以太网、IP和TCP开销,以及占用带宽的RTT时,从512字节的消息中减去60字节通常是愚蠢的。在

另一方面,当你发送大量消息时,通常不需要在同一个连接上发送多个消息。在

看看常见的因特网协议,如HTTP、IMAP等,它们大多使用行分隔的、可读的、易于调试的纯文本。HTTP可以以二进制形式发送“其余的消息”,但在发送完成后关闭套接字。在

99%的时候,这已经足够好了。如果你觉得在你的情况下还不够好,我会写下你的协议的文本版本,然后在你把所有的东西都调试好并正常工作后,再添加一个可选的二进制版本(然后测试它是否真的有区别)。在


同时,你的代码有两个问题。在

首先,正如您所认识到的,如果您使用":::END"作为分隔符,并且您的消息可以在其数据中包含该字符串,那么您就存在歧义。解决这个问题的通常方法是某种形式的转义或引用。举个简单的例子:

def sockWrite(conn, data):
    data = data.replace(':', r'\:') + ":::END"
    conn.write(data)

现在在read端,只需去掉分隔符,然后在消息上replace('r\:', ':')。(当然,为了使用一个6字节的':::END'分隔符来转义每个冒号是非常浪费的—您可以使用一个未转义的冒号作为分隔符,或者编写一个更复杂的转义机制。)

第二,你说得对,“一次写入可能会发生多个读取”——但是对于这一次读取,可能会发生多个写入也是正确的。你可以读这封信的一半,再加上下一封的一半。这意味着您不能只使用endswith;您必须使用类似partition或{},编写可以处理多个消息的代码,还可以通过read循环存储部分消息。在


同时,对于您的具体问题:

Is there a proper way to encapsulate the data with as minimum of bandwidth addition as possible?

当然,至少有三种正确的方法:分隔符、前缀或自定界格式。在

你已经找到了第一个。它的问题是:除非有一些字符串永远不可能出现在数据中(例如,'\0'在人类可读的UTF-8文本中),否则没有可以选择的不需要转义的分隔符。在

像JSON这样的自定界格式是最简单的解决方案。当最后一个打开的大括号/括号关闭时,消息就结束了,到了下一个的时候了。在

或者,您可以为每个消息添加一个包含长度的头。这就是许多低层协议(如TCP)所做的。其中一种最简单的格式是netstring,其中头就是以字节为单位的长度,作为一个整数,表示为一个普通的以10为基数的字符串,后跟一个冒号。netstring协议也使用逗号作为分隔符,这增加了一些错误检查。在


I had thought about pickle or json, but worried that will add a significant amount of bandwidth since I believe they will convert the binary data to ASCII

pickle同时具有二进制和文本格式。正如the documentation所解释的,如果您使用协议23、或{},您将得到一个相当有效的二进制格式。在

另一方面,JSON只处理字符串、数字、数组和字典。在对二进制数据进行JSON编码之前,您必须手动将任何二进制数据呈现为字符串(或字符串或数字数组,或其他任何形式),然后在另一端进行反转。两种常见的方法是base-64和hex,这两种方法分别会增加25%和100%的数据大小,但如果您真的需要,还有更有效的方法。在

当然,JSON协议本身使用的字符比严格要求的要多一些,包括所有的引号和逗号等等,并且无论您给任何字段指定什么名称,都将作为未压缩的UTF-8发送。您总是可以将JSON替换为BSONProtocol BuffersXDR或其他不太“浪费”的序列化格式(如果确实是个问题的话)。在

同时,pickle不是自定界的。你必须先把信息分开,然后才能解开它们。JSON自限定的,但是除非您首先将消息分开,否则您不能只使用json.loads;您将不得不编写更复杂的内容。最简单的方法是反复调用缓冲区上的^{},直到得到一个对象。在

相关问题 更多 >