Pythonのソケット - スティックパッケージを解決するためのTCP方法

1.なぜパッケージが固執あります?

 

私たちは、最初のTCPベースのリモートコマンド実行プログラムを作ってみましょう(1:コマンド実行エラー2:LS 3を実行します。ifconfigを実行します)

注意注意注意:

RES = subprocess.Popen(cmd.decode( 'UTF-8')、
殻= Trueを、
標準エラー= subprocess.PIPE、
STDOUT = subprocess.PIPE)

符号化システムは、それがWindowsの場合、被検者の現在の位置の結果に基づいて、その後れる(res.stdout.read)GBKが符号化され読み取る受信端で必要GBKデコードを使用します

 

送信側はK K送信データであってもよいし、受信側アプリケーションは、最初の3K又は6Kデータを引き出す、または一度だけデータの数バイトを撤回することも可能コースのデータを引き出すための2つのK 2 K、缶換言すれば、アプリケーションは、全体として、データを見て、またはそれは、ストリーム(ストリーム)、アプリケーションが表示されていないバイト数のメッセージであるため、TCPプロトコルが容易であり、ストリーム指向のプロトコルであります理由スティックパッケージの問題。UDPは、メッセージ指向のプロトコルであるが、各セグメントは、UDPメッセージは、メッセージのデータユニットを抽出する必要があり、アプリケーション内の任意の1バイトのデータを抽出し、それがTCPと異なっていることができません。ニュースを定義するには?他の側面は、使い捨て書き込み/メッセージなどのデータを送信してもよいこと、他方は関係なく、基礎となるタイルセグメントは、TCPプロトコル層が完了した後、メッセージ全体を構成するデータセグメントをソートするかを、メッセージを送信しない場合が理解されるべきカーネルバッファに提示。

サーバーにファイルをアップロードする例TCPソケットベースのクライアントの場合は、ファイルの内容を送信する送信されたバイトストリームのセクションに従っている、私はどこから始めれば、ファイルストリームのバイトで知りませんでした、受信機を見て、どこ停止します

受信機は、メッセージ間の限界を知らないので、いわゆるスティックパッケージの問題は、主に、タイム抽出によるデータのバイト数を知りません。

さらに、送信者スティックパッケージによる自体がTCPプロトコルによって引き起こされる、伝送効率を向上させるためにTCPは、送信者はしばしば、TCPセグメントを送信する前に十分なデータを収集します。連続送信データはほとんど必要ない場合は、通常、TCPの最適化に基づいたアルゴリズム、受信側は、粘着性のデータパケットを受信するように、データ伝送時間合成後のTCPセグメント外へ。

  1. TCP(トランスポート制御プロトコル、伝送制御プロトコル)接続指向ストリーム指向の、信頼性の高いサービスです。受信機と送信機(クライアントおよびサーバー)がソケットの11ペアを持っている必要があり、従って、複数のパケットを終了送信するために最適化法(Nagleアルゴリズム)を使用して、送信するために、他のより効率的な、受信側宛、時間は、小さな間隔及びデータのより小さな量のデータは、データの大きなブロックは、パケットに組み合わせます。このようにして、受信側では、違いを見分けるのは難しいですし、科学的な開梱メカニズムを提供しなければなりません。すなわち、無指向通信メッセージ・ストリームは、境界を保護されています。
  2. UDP(ユーザデータグラムプロトコル、ユーザデータグラムプロトコル)コネクション、メッセージ指向、効率的なサービスを提供することです。UDPは、各UDPの各UDPパケットの到着を記録するためにチェーン構造を使用して多くのモード、skbuff(ソケットバッファ)の受信側であるため、最適化アルゴリズムの併用は,,ブロックをサポートしていません受信側では、プロセスを区別することが容易であるように、我々は、パッケージ(情報源アドレスおよびポート)メッセージヘッダを有します。 そのメッセージは、メッセージ指向の通信保護の境界です。
  3. 送受信されたメッセージはすべて、あなたが入力した場合でも、UDPデータグラムに基づいてジャミングプログラムを防ぐために、空のクライアントにメカニズムを扱うメッセージやサーバーを追加する必要と、空にすることはできませんので、TCPは、データストリームに基づいています空のコンテンツ(直接キャリッジリターン)は、それが空のメッセージではなかった、UDPプロトコルは、少し実験あなたはメッセージヘッダをパッケージ化するのに役立ちます

Y> Xのデータが失われた場合のrecvfromがブロックされるUDP、のrecvfrom(x)がのみ(Y)sendinto、でも完了した場合、すべてのxバイトのデータを終了しなければならない、これはそれがUDPパケットを固執しないことを意味し、それは信頼できない、データを失うことになります

TCPプロトコルのデータが失われない完全なパッケージを受信しませんでした、次の受信は、最後の受信を継続していきます、常にレシートACK時にバッファの内容をクリアします終了しました。データは信頼性があるが、パッケージに固執します。

 

スティックパッケージは、どちらの場合に発生します。

送信側は、バッファが満杯などである(データ送信時間間隔は、データが少ない、短いスティックパッケージを生成するために一緒に参加する)スティックパッケージをもたらす送出する必要があります

_ * _コード:UTF-8 _ * _ 
__author__ = 'Linhaifeng' 
ソケットインポートから * 
ip_port =( '127.0.0.1'、8080 

tcp_socket_server = ソケット(AF_INET、SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen( 5 


CONN、ADDR = tcp_socket_server.accept()


DATA1 = conn.recv(10 
DATA2 = conn.recv(10 

印刷( ' ----->'、data1.decode( 'UTF-8 '))
プリント(' -----> 'data2.decode(' UTF-8 「))

はconn.close()

服务端
_ * _コード:UTF-8 _ * _ 
__author__ = ' Linhaifeng ' 
インポートソケット
BUFSIZE = 1024 
ip_port =(' 127.0.0.1 '、8080 

、S = socket.socket(はsocket.AF_INET、socket.SOCK_STREAM)
RES = s.connect_ex (ip_port)


s.send(' ハロー' .encode(' UTF-8 ' ))
s.send(' 風水' .encode(' UTF-8 ' ))

客户端

受信者は、適時、(クライアントによって送信されたデータの一部を受信するサーバは、サーバの小部分のみ次回受信またはバッファから最後の残りのデータを閉じるために時間がかかり、複数のパケットで、その結果、パケットバッファを受信して​​いません生成スティック包装) 

コーディング_ * _#:_ * _ 8 UTF- 
__author__ = 'Linhaifeng' 
インポート*ソケットから
ip_port =ため( '127.0.0.1'、8080)

tcp_socket_server =ソケット(AF_INET、SOCK_STREAM)
tcp_socket_server.bind(ip_port用)
tcp_socket_server.listen(5)。


CONN、ADDR = tcp_socket_server.accept()


DATA1 = conn.recv(2)完全に受信していない#
DATA2を= conn.recv(10)#は、次の時間を閉じて、最初に古いデータを取得し、新しいかかります

'(印刷を-----> 'data1.decode(' UTF-8 '))
を印刷(' -----> 'data2.decode(' UTF-8「))

はconn.close()

サーバー

  

#_ * _コード:UTF-8 _ * _ 
__author__ = 'Linhaifeng' 
インポートソケット
BUFSIZE = 1024 
ip_port =( '127.0.0.1'、8080)

、S = socket.socket(はsocket.AF_INET、socket.SOCK_STREAM)
RES = s.connect_ex (ip_port)


s.send( 'ハローfeng'.encode(' UTF-8' ))

客户端

  

開梱の発生

送信側バッファの長さは、ネットワークカードのMTUよりも大きい場合、データが送信された複数のデータパケットにTCP分割を送信します。

補足質問:なぜ、信頼性の高い伝送TCPは、UDPは信頼性の低い伝送であります

TCPベースのデータ伝送、データ転送中に私の他の記事http://www.cnblogs.com/linhaifeng/articles/5937962.html,tcpを参照してください、送信者は、その後、独自のキャッシュを置くためにデータを送信しますピアに送信され、キャッシュ制御プロトコル・データは、ピアがACK = 1、送信側のデータバッファがクリーンである、ピア戻る= 0、再伝送データをACKを返すので、信頼性の高いTCP

UDPと送信データ、ピアは確認応答メッセージは、したがって、信頼性の低いない返します

補足2:(バイトストリーム)とRECV(1024)を送信し、sendall

recvはキャッシュから外に1024バイトのデータで指定された1024年のこと

バイトストリームは、データが失われ、それがsendallとサイクルコールを送信し、送信されるバイトストリームのサイズは残りのバッファスペースよりも大きい場合、キャッシュは、端部にコンテンツを送信するためのプロトコルによって制御される、ヘキシル側キャッシュに送信することです、データが失われることはありません

 

問題の根は、受信機が転送される送信元のバイトストリームの長さを知らないということなので、ソリューション・スティックパッケージには、データを送信する前に、送信者を取得する方法、周りにある、バイトストリーム受信を可能にするために送信されます、独自の合計サイズその終わり、その後、死亡のフェッチ受信周期は、すべてのデータを受信しました

送信バイトストリームの長さの送信前に、いくつかのバイトを送信する前に、本実施形態は、ネットワーク遅延のパフォーマンスコストを増幅して、ネットワークの伝送速度よりもはるかに速い速度を実行します

2.スティックパッケージを解決する方法

バイトのカスタム・ストリームに加え、次いで固定長ヘッダ、バイトストリームに含まれるヘッダ長、及び最後までペアを送信するが、受信側開始バッファ時の固定長ヘッダを削除し、実際のデータを取ります

構造体モジュールは、

このモジュールは、数字、固定長に変換などのタイプ、バイトであってもよい

>>> struct.pack(「I」は、1,111,111,111,111) 


struct.error:「私は」フォーマットは-2147483648必要です <=数<= 2147483647# これは範囲で

インポートJSONは、構造体
#1 1Tがクライアントによってアップロードされたと仮定:ファイル1073741824000 A.TXT 

#の回避スティックパッケージを、からヘッダにカスタマイズされなければならない
ヘッダを= { 'FILE_SIZE':1073741824000、 'FILE_NAME': '/ A / B / C / D / E / A.TXT'、 'MD5': '8f6fbf8347faa4924a76856701edb0f3'}#1の1T データ、ファイルパスとMD5値

#ヘッダの送信、及びスイッチをシリアル化する必要バイトができる
送信するため、head_bytesバイト=(json.dumps(ヘッダ)、エンコード=「UTF-8」)#シリアライズとバイトに変換し

、クライアントは、ヘッダの長さを知ることができるようにするために位デジタル-固定長のヘッダの長さに打た:4バイト
head_len_bytes = struct.pack(「i」は、 LEN(head_bytes))# 4バイトは、ヘッダの長さだけ番号が含まれ

#クライアントが送信を開始します
CONNを。

#サーバが受信を開始
head_len_bytes = s.recv(4)#最初のコレクションヘッダ4バイト、バイト・フォーマットを取得するヘッダ長
( 'i'は、X = struct.unpack head_len_bytes)を[0]# ヘッダの抽出された長さを

head_bytes =荷電ヘッダXに従ってフォーマットs.recv(x)が#ヘッダ長をバイト
ヘッダ= json.loads(json.dumps(ヘッダ) )#の抽出ヘッダ

#最終抽出物実際のデータは、コンテンツヘッダによれば、そのような
real_data_len = s.recv (ヘッダ[ 'FILE_SIZE'])
s.recv(real_data_len)

  

私たちは、送信される辞書は、実際のデータの詳細が含まれている、ヘッダ辞書を作ることができ、その後、JSONシリアライズは、その後、(自分自身を使用する4十分)4に詰めバイトのシーケンス後のデータ長を打ちました

場合に送信されます:

第1の送信機のヘッド長

ヘッダを再符号化した後、コンテンツを送信します

髪の最後に、本当のコンテンツ

受信:

先手ヘッダ長は、構造体と取り出し

帯電したコンテンツ長ヘッダは、その後、除去デコード、デシリアライズ

データの詳細は、テイクデシリアライズから結果を取り出すことにすると、実際のデータは、コンテンツをフェッチ

バイト#カスタムストリームプラス固定長ヘッダ、バイトストリームに含まれるヘッダ長、およびピアを送信するためには、
#ピア除去固定長ヘッダバッファの開始が受信され、その後、実際のデータを取ります

#structモジュール
モジュールは、数値として、タイプとすることができる。#は、固定長のバイトに変換



「」 ' 
我々は辞書からなるヘッダを置くことができ、辞書は、実際のデータを送信する詳細を含む、次いでJSONシリアライズ、
次いで4バイトにパックのシーケンスのデータ長(4十分に使用すること自体)打た
:送信する
第1送信ヘッドの長さが
送信、ヘッダコンテンツを再エンコードし、
最後に送信実際のコンテンツを

:受信
構造体を取り出しと共に、上ハンドヘッダ長を
荷電抽出されたコンテンツヘッダの長さは、次にデコード、デシリアライズ
デシリアライズ結果から抽出されるデータへのアクセスの詳細を、次に実のデータ内容フェッチ
「」 ' 
#>>> struct.pack( 『I』を、 "ABC") トレースバック(最新のラストコール): ファイル"<。pyshell 1#>"、1行目、<Module1を>インチ
#struct.pack( "I"、 "ABC")
#1 struct.error:intege引数が必要とされていない

#サーバ(少し複雑なカスタムヘッダー)

輸入ソケット、構造体、JSON
インポートサブプロセス 
            back_msg = ERR


電話= socket.socket(はsocket.AF_INET、socket.SOCK_STREAM)
ip_sort =( "127.0.0.1"、8080)
back_log = 5 
phone.setsockopt(socket.SOL_SOCKET、socket.SO_REUSEADDR、1)
phone.bind(ip_sort)
電話。 (back_logを)聞く

:しばらく真
    CONN、ADDR = phone.accept()
    トゥルーながら:
        CMD = conn.recv(1024)
        CMDない場合:ブレーク
        プリント( "CMD:%s"は%のCMD)
        RES = subprocess.Popen(CMD .decode( "UTF-8")、シェル= Trueの場合、STDOUT = subprocess.PIPE、標準エラー出力= subprocess.PIPE 
                             、STDIN = subprocess.PIPE)
        ERR = res.stderr.read()
        プリント(ERR)
        ERRの場合:
        他:
            back_msg = res.stdout.read()
 
        ヘッダー= { 'DATA_SIZE':LEN(back_msg)} 
        head_json = json.dumps(ヘッダ)#シリアル化された文字列
        を印刷(タイプ(head_json))
        head_json_bytesバイト=(head_json、エンコーディング= "UTF-8")


        #1 struct.pack(変換パケットに"I"タイプ、第二の引数は数値でなければなりません)
        conn.send(struct.pack( "I"、LEN(head_json_bytes)))#始動ヘッダの長さ
        conn.send(head_json.encode( "UTF-8 "))# 再送信ヘッド
        conn.sendall(back_msg)#再発実コンテンツ

    はconn.close()

  

メソッドは、クライアントのスティックパッケージ解決するために
輸入ソケット、構造体、JSON 

ip_port =(" 127.0.0.1 "、8080 

tcp_client = socket.socket(はsocket.AF_INET、socket.SOCK_STREAM)

tcp_client.connect_ex(ip_port)

:真の
    CMD =をINPUT(" >> " IF  ていない CMD:続行
    tcp_client.send(cmd.encode('UTF-8 ' ))
    のデータ(4)= tcp_client.recv メッセージヘッダ長受信 
    = struct.unpack(NUM " Iの"、データ)[0]

アウトアンパックアンパックタプル
    プリント(NUM)
    ヘッダ = json.loads(tcp_client.recv(NUM).decode(" UTF-8 ")) 受信ヘッダのヘッダ長を受信することにより、 
    ヘッダ= [DATA_LEN " DATA_SIZE " ] メッセージの長さは送信される

    recv_size = 0 
    recv_data = B '' 
    ながら recv_size < :DATA_LEN 
        recv_data + = tcp_client.recv(1024 
        recv_size = LEN(recv_data)

    プリント(recv_data.decode(" GBK " ))
    印刷(recv_data.decode( "GBKは"))のデフォルトエンコーディングはGBKです#windows

 

おすすめ

転載: www.cnblogs.com/tangcode/p/11620151.html