我正在开发一个应用程序,它通过套接字向另一个实例发送和接收数据,我很好奇用“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。我说得对吗?在
谢谢, 本
你真的需要优化吗?在
通常你发送的信息相对较小。当你看到忽略了多少以太网、IP和TCP开销,以及占用带宽的RTT时,从512字节的消息中减去60字节通常是愚蠢的。在
另一方面,当你发送大量消息时,通常不需要在同一个连接上发送多个消息。在
看看常见的因特网协议,如HTTP、IMAP等,它们大多使用行分隔的、可读的、易于调试的纯文本。HTTP可以以二进制形式发送“其余的消息”,但在发送完成后关闭套接字。在
99%的时候,这已经足够好了。如果你觉得在你的情况下还不够好,我会写下你的协议的文本版本,然后在你把所有的东西都调试好并正常工作后,再添加一个可选的二进制版本(然后测试它是否真的有区别)。在
同时,你的代码有两个问题。在
首先,正如您所认识到的,如果您使用
":::END"
作为分隔符,并且您的消息可以在其数据中包含该字符串,那么您就存在歧义。解决这个问题的通常方法是某种形式的转义或引用。举个简单的例子:现在在read端,只需去掉分隔符,然后在消息上
replace('r\:', ':')
。(当然,为了使用一个6字节的':::END'
分隔符来转义每个冒号是非常浪费的—您可以使用一个未转义的冒号作为分隔符,或者编写一个更复杂的转义机制。)第二,你说得对,“一次写入可能会发生多个读取”——但是对于这一次读取,可能会发生多个写入也是正确的。你可以读这封信的一半,再加上下一封的一半。这意味着您不能只使用},编写可以处理多个消息的代码,还可以通过
endswith
;您必须使用类似partition
或{read
循环存储部分消息。在同时,对于您的具体问题:
当然,至少有三种正确的方法:分隔符、前缀或自定界格式。在
你已经找到了第一个。它的问题是:除非有一些字符串永远不可能出现在数据中(例如,
'\0'
在人类可读的UTF-8文本中),否则没有可以选择的不需要转义的分隔符。在像JSON这样的自定界格式是最简单的解决方案。当最后一个打开的大括号/括号关闭时,消息就结束了,到了下一个的时候了。在
或者,您可以为每个消息添加一个包含长度的头。这就是许多低层协议(如TCP)所做的。其中一种最简单的格式是netstring,其中头就是以字节为单位的长度,作为一个整数,表示为一个普通的以10为基数的字符串,后跟一个冒号。netstring协议也使用逗号作为分隔符,这增加了一些错误检查。在
pickle
同时具有二进制和文本格式。正如the documentation所解释的,如果您使用协议2
、3
、或{另一方面,JSON只处理字符串、数字、数组和字典。在对二进制数据进行JSON编码之前,您必须手动将任何二进制数据呈现为字符串(或字符串或数字数组,或其他任何形式),然后在另一端进行反转。两种常见的方法是base-64和hex,这两种方法分别会增加25%和100%的数据大小,但如果您真的需要,还有更有效的方法。在
当然,JSON协议本身使用的字符比严格要求的要多一些,包括所有的引号和逗号等等,并且无论您给任何字段指定什么名称,都将作为未压缩的UTF-8发送。您总是可以将JSON替换为BSON,Protocol Buffers、XDR或其他不太“浪费”的序列化格式(如果确实是个问题的话)。在
同时,} ,直到得到一个对象。在
pickle
不是自定界的。你必须先把信息分开,然后才能解开它们。JSON是自限定的,但是除非您首先将消息分开,否则您不能只使用json.loads
;您将不得不编写更复杂的内容。最简单的方法是反复调用缓冲区上的^{相关问题 更多 >
编程相关推荐