2013-03-06 37 views
1

我正在通过套接字发送和接收数据给另一个实例的应用程序,我对使用“END”标签封装数据的最有效方法感到好奇。举例来说,这里是用来读取和通过套接字连接写两个功能:在python中封装套接字数据的正确方法?

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”,恰好发生在其中一个读取结束时。

有没有一种合适的方式来尽可能减少带宽的增加来封装数据?我曾考虑泡菜或JSON,但担心会增加大量的带宽,因为我相信他们会将二进制数据转换为ASCII。我正确吗?

感谢, 本

回答

0

第零:你真的需要优化呢?

通常你会发送相对较小的消息。当您查看您忽略了多少以太网,IP和TCP开销以及吞吐带宽的RTT时,从512字节消息中删除60个字节通常很愚蠢。

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

看看像HTTP,IMAP等常见的互联网协议。他们大多数使用线分界,人类可读,易调试的纯文本。 HTTP可以以二进制形式发送“剩余的消息”,但是在发送完成后关闭套接字。

99%的时间,这是够好的。如果你认为它不够好,我会仍然写你的协议的文本版本,然后添加一个可选的二进制版本,一旦你有一切调试和工作(然后测试,看看是否它真的有所作为)。


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

首先,如您所知,如果您使用":::END"作为分隔符,并且您的消息可以在其数据中包含该字符串,则您有不明确之处。解决这个问题的常用方法是某种形式的转义或引用。对于一个非常简单的例子:

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

现在在读出侧,你刚才拉过的分隔符,然后replace('r\:', ':')上的消息。 (当然,使用6字节':::END'分隔符来逃避每个冒号是很浪费的 - 你可能只需要使用一个非转义的冒号作为分隔符,或者写一个更复杂的转义机制。)

第二,你正确的是“这次单次写入可能会发生多次读取” - 但是对于这次单次读取也可能发生多次写入。你可以阅读这个消息的一半,再加上下一个消息的一半。这意味着你不能只使用endswith;您必须使用类似partitionsplit的东西,并编写可处理多条消息的代码,并编写可存储部分消息的代码,直至下一次通过read循环。


同时,为您的具体问题:

有封装的数据带宽增加尽可能最小的一个适当的方式?

当然,至少有三种适当的方式:分隔符,前缀或自定义格式。

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

像JSON这样的自定义格式是最简单的解决方案。当最后一个打开的支架/支架关闭时,信息结束,下一个时间到了。

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


我曾想过咸菜或JSON,但担心会增加带宽的显著量,因为我相信他们会二进制数据转换为ASCII

pickle有二进制文件和文本格式。正如the documentation解释,如果您使用协议2,3HIGHEST_PROTOCOL,您将得到一个相当有效的二进制格式。另一方面,JSON只处理字符串,数字,数组和字典。您必须手动将任何二进制数据呈现为字符串(或字符串或数字或任何其他数组),然后才能对其进行JSON编码,然后在另一侧进行相反的处理。执行此操作的两种常用方法是base-64和hex,它们分别为数据大小增加25%和100%,但如果您真的需要,则可以使用更高效的方法。

当然,JSON协议本身会使用比严格必要的更多的字符,所有这些引号和逗号等等,以及您给任何字段的任何名称都会以未压缩的UTF-8格式发送。如果确实存在问题,您可以始终使用BSON,Protocol Buffers,XDR或其他较不“浪费”的序列化格式替换JSON。

同时,pickle不是自行划分的。你必须首先拆分消息,然后才能取消它们。 JSON 自定义分隔符,但除非先分开消息,否则不能只使用json.loads;你将不得不写更复杂的东西。最简单的工作是在缓冲区上重复调用raw_decode,直到获得一个对象。

相关问题