ゼロ:本当にこれを最適化する必要がありますか?
通常、比較的小さなメッセージを送信します。512バイトのメッセージから60バイトを削減することは、無視しているイーサネット、IP、TCPのオーバーヘッド、および帯域幅を圧倒するRTTを見ると、通常はばかげています。
一方、巨大なメッセージを送信する場合、同じ接続で複数のメッセージを送信する必要がないことがよくあります。
HTTP、IMAPなどの一般的なインターネットプロトコルを見てください。それらのほとんどは、行で区切られ、人間が読める形式で、簡単にデバッグできるプレーンテキストを使用しています。HTTPは「残りのメッセージ」をバイナリで送信できますが、送信が終了したらソケットを閉じます。
99%の場合、これで十分です。あなたのケースでそれが十分ではないと思うなら、私はまだあなたのプロトコルのテキストバージョンを書き、そしてあなたがすべてをデバッグして動作させたらオプションのバイナリバージョンを追加します(そしてそれが本当にあるかどうかをテストします違いを生む)。
一方、コードには2つの問題があります。
まず、":::END"
ご存知のように、区切り文字として使用していて、メッセージにその文字列をデータに含めることができる場合は、あいまいさがあります。この問題を解決する通常の方法は、何らかの形でエスケープまたは引用することです。本当に簡単な例:
def sockWrite(conn, data):
data = data.replace(':', r'\:') + ":::END"
conn.write(data)
読み取り側では、区切り文字を削除してからreplace('r\:', ':')
、メッセージを削除します。(もちろん、6バイトの':::END'
区切り文字を使用するためだけにすべてのコロンをエスケープするのは無駄です。エスケープされていないコロンを区切り文字として使用するか、より複雑なエスケープメカニズムを作成することもできます。)
次に、「この1回の書き込みで複数の読み取りが発生する可能性がある」というのは正しいですが、この1回の読み取りで複数の書き込みが発生する可能性があることも事実です。このメッセージの半分と次のメッセージの半分を読むことができます。これは、あなたがただ使うことができないことを意味しますendswith
; partition
またはのようなものを使用しsplit
、複数のメッセージを処理できるコードを記述し、次にread
ループを通過するまで部分的なメッセージを格納できるコードを記述しなければなりません。
その間、あなたの特定の質問に:
帯域幅の追加を最小限に抑えてデータをカプセル化する適切な方法はありますか?
確かに、少なくとも3つの適切な方法があります。区切り文字、プレフィックス、または自己区切り形式です。
あなたはすでに最初のものを見つけました。そして、それに関する問題:データに表示されない可能性のある文字列(たとえば、'\0'
人間が読めるUTF-8テキスト)がない限り、エスケープを必要としない区切り文字を選択することはできません。
JSONのような自己区切り形式が最も簡単なソリューションです。最後に開いたブレース/ブラケットが閉じると、メッセージは終了し、次のメッセージの時間です。
または、各メッセージの前に長さを含むヘッダーを付けることもできます。これは、多くの低レベルプロトコル(TCPなど)が行うことです。このための最も単純な形式の1つは、netstringです。ここで、ヘッダーは、通常の基数10の文字列として表される整数としてのバイト単位の長さであり、その後にコロンが続きます。netstringプロトコルは、区切り文字としてコンマも使用します。これにより、エラーチェックが追加されます。
私はpickleまたはjsonについて考えていましたが、バイナリデータをASCIIに変換すると信じているため、かなりの帯域幅が追加されるのではないかと心配していました。
pickle
バイナリ形式とテキスト形式の両方があります。ドキュメントで説明されているように、プロトコル、、、またはを使用する2
と3
、HIGHEST_PROTOCOL
かなり効率的なバイナリ形式が得られます。
一方、JSONは、文字列、数値、配列、および辞書のみを処理します。JSONエンコードする前に、バイナリデータを手動で文字列(または文字列や数値の配列など)にレンダリングしてから、反対側で逆にする必要があります。これを行う2つの一般的な方法は、base-64とhexです。これらは、データのサイズにそれぞれ25%と100%を追加しますが、本当に必要な場合は、より効率的な方法があります。
そしてもちろん、JSONプロトコル自体は、厳密に必要な文字よりも数文字多く使用します。これらすべての引用符やコンマなどを使用すると、フィールドに付けた名前はすべて非圧縮UTF-8として送信されます。JSONは、 BSON、Protocol Buffers、XDR、またはそれが本当に問題である場合は「無駄」が少ない他のシリアル化形式にいつでも置き換えることができます。
一方、pickle
自己区切りではありません。メッセージの選択を解除する前に、まずメッセージを分割する必要があります。JSONは自己区切りですが、json.loads
最初にメッセージを分割しない限り、単に使用することはできません。もっと複雑なものを書く必要があります。最も簡単に機能するのはraw_decode
、オブジェクトを取得するまでバッファを繰り返し呼び出すことです。