2013-03-06 81 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,直到獲得一個對象。