簡體   English   中英

在python中封裝套接字數據的正確方法?

[英]Proper way to encapsulate socket data in 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。 我對嗎?

謝謝,本

Zeroth:您真的需要對此進行優化嗎?

通常,您發送相對較小的消息。 當您忽略多少以太網,IP和TCP開銷以及浪費帶寬的RTT時,從512字節消息中減少60字節通常很愚蠢。

在另一方面,當發送郵件龐大,往往沒有必要在同一連接上發送多條消息。

看一下常見的Internet協議,例如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為基數的字符串,后跟冒號。 netstring協議使用逗號作為分隔符,這增加了一些錯誤檢查。


我曾考慮過pickle或json,但擔心會增加大量帶寬,因為我相信它們會將二進制數據轉換為ASCII

pickle具有二進制和文本格式。 由於該文件解釋說,如果你使用協議23 ,或HIGHEST_PROTOCOL ,你會得到一個合理有效的二進制格式。

另一方面,JSON僅處理字符串,數字,數組和字典。 您必須先將任何二進制數據呈現為字符串(或字符串數​​組或數字數組,或其他形式),然后才能對其進行JSON編碼,然后在另一端進行反轉。 兩種常見的實現方法是base-64和hex,它們分別將數據大小增加了25%和100%,但是如果您確實需要,可以使用更有效的方法。

當然,JSON協議本身使用的字符比嚴格使用的字符多,所有這些引號和逗號等等,以及您為任何字段指定的任何名稱均以未壓縮的UTF-8格式發送。 如果確實存在問題,您總是可以用BSONProtocol BuffersXDR或其他不太浪費的序列化格式替換JSON。

同時, pickle不是自定界的。 您必須先將消息分開,然后才能對其進行修補。 JSON 自定界的,但是除非您首先將消息分開,否則不能只使用json.loads 您將不得不編寫更復雜的內容。 最簡單的方法是在緩沖區上重復調用raw_decode ,直到獲得對象為止。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM