ディレクトリ
まず、スティックパッケージは何ですか
問題があれば、TCPプロトコルの使用など、ソケットプログラミングによる問題も生成されます場合でもので、スティックのパッケージすべての言語は、問題を持っているだろうということです。
注意:唯一のTCPパケットが粘着性の現象があり、UDPは、私が説明に耳を傾け、なぜ、パッケージを、固執することはありません。
まず第一に、私たちは、ソケットの送信を持っているとメッセージ原理を受信する必要があります
送信側はK K送信データであってもよいし、受信側アプリケーションは、最初の3K又は6Kデータを引き出す、または一度だけデータの数バイトを撤回することも可能コースのデータを引き出すための2つのK 2 K、缶換言すれば、アプリケーションは、全体として、データを見て、またはそれは、ストリーム(ストリーム)、アプリケーションが表示されていないバイト数のメッセージであるため、TCPプロトコルが容易であり、ストリーム指向のプロトコルであります理由スティックパッケージの問題。UDPは、メッセージ指向のプロトコルであるが、各セグメントは、UDPメッセージは、メッセージのデータユニットを抽出する必要があり、アプリケーション内の任意の1バイトのデータを抽出し、それがTCPと異なっていることができません。ニュースを定義するには?他の側面は、使い捨て書き込み/メッセージなどのデータを送信してもよいこと、他方は関係なく、基礎となるタイルセグメントは、TCPプロトコル層が完了した後、メッセージ全体を構成するデータセグメントをソートするかを、メッセージを送信しない場合が理解されるべきカーネルバッファに提示。
例えば、ファイルの内容が送信されたバイトのストリームのセクションに従っている送信、サーバーにファイルをアップロードするためにTCPソケットクライアントに基づいて、私はどこから始めれば、ファイルストリームのバイトで知りませんでした、受信機を見て、どこを停止します。
受信機は、メッセージ間の限界を知らないので、いわゆるスティックパッケージの問題は、主に、タイム抽出によるデータのバイト数を知りません。
さらに、送信者スティックパッケージによる自体がTCPプロトコルによって引き起こされる、伝送効率を向上させるためにTCPは、送信者はしばしば、TCPセグメントを送信する前に十分なデータを収集します。連続送信データがほとんど必要ありませんしている場合、受信側は、粘着性のデータパケットを受信するように、一般的にTCPは、TCPセグメントうち合成に従って、データ最適化アルゴリズムを送信します。
- TCP(トランスポート制御プロトコル、伝送制御プロトコル)接続指向ストリーム指向の、信頼性の高いサービスです。受信機と送信機(クライアントおよびサーバー)がソケットの11ペアを持っている必要があり、従って、複数のパケットを終了送信するために最適化法(Nagleアルゴリズム)を使用して、送信するために、他のより効率的な、受信側宛、時間は、小さな間隔及びデータのより小さな量のデータは、データの大きなブロックは、パケットに組み合わせます。このようにして、受信側では、違いを見分けるのは難しいですし、科学的な開梱メカニズムを提供しなければなりません。すなわち、無指向通信メッセージ・ストリームは、境界を保護されています。
- UDP(ユーザデータグラムプロトコル、ユーザデータグラムプロトコル)コネクション、メッセージ指向、効率的なサービスを提供することです。UDPは、各UDPの各UDPパケットの到着を記録するためにチェーン構造を使用して多くのモード、skbuff(ソケットバッファ)の受信側であるため、最適化アルゴリズムの併用は,,ブロックをサポートしていません受信側では、プロセスを区別することが容易であるように、我々は、パッケージ(情報源アドレスおよびポート)メッセージヘッダを有します。そのメッセージは、メッセージ指向の通信保護の境界です。
- 送受信されたメッセージはすべて、あなたが入力した場合でも、UDPデータグラムに基づいてジャミングプログラムを防ぐために、空のクライアントにメカニズムを扱うメッセージやサーバーを追加する必要と、空にすることはできませんので、TCPは、ストリームベースであります空のコンテンツ(直接キャリッジリターン)は、それが空のメッセージではなかった、UDPプロトコルは、少し実験あなたはメッセージヘッダをパッケージ化するのに役立ちます
Y> Xのデータが失われた場合のrecvfromがブロックされるUDP、のrecvfrom(x)がのみ(Y)sendinto、でも完了した場合、すべてのxバイトのデータを終了しなければならない、これはそれがUDPパケットを固執しないことを意味し、それは信頼できない、データを失うことになります
TCPプロトコルのデータが失われることはありません、完全なパッケージを受信しませんでした、次の受信は、最後の受信を継続していきます、常にレシートACK時にバッファの内容をクリアします終了しました。データは信頼性があるが、パッケージに固執します。
第二に、データが送信された4例のTCP
以下の4例があるかもしれないことに起因バイト読みの数にサーバは、不確実である、クライアントがサーバに二つのパケットD1およびD2を送信すると仮定します。
- 二回サーバは、2つの別々のデータパケット、それぞれD1およびD2を読み、パッケージアンパックを付着しないように。
- サーバはTCPスティックパッケージと呼ばれる、2は、D1およびD2は、互いに結合されているパケットを受信します。
- 二回サーバは2つのデータパケット、最初の時間を読み取るために、パッケージの完全読出しパケットD1およびD2部品、TCPはアンパックと呼ばれ、残りのパッケージの内容D2、第2読取。
- パッケージD1_1 D1、全体の包装袋の残りのパッケージコンテンツD1_2 D1及びD2への第2の読み出しに2つのデータパケット、最初の読み出し部分を読み取る回サーバ。
例外:この時点では、サーバーがTCPスライディングウィンドウを受信した場合には非常に小さく、かつデータパケットD1およびD2が比較的大きい、第五が発生する可能性が非常に高い、そのサービスは、パケット受信D1およびD2を完了するために、ポイントを数回終了します、中に発生しました繰り返し開梱。
三、構造体モジュール
どのように使用するには:
import struct
#把一个数字打包成固定长度的4字节,得到字节格式数据
obj=struct.pack('i',1024) # 'i'是格式
print(obj)
print(len(obj))
# 解包,得到元祖类型数据
l=struct.unpack('i',obj)[0]
print(l)
この問題を解決するための第四に、スティックパッケージ
問題の根は、受信機が転送される送信元のバイトストリームの長さを知らないということなので、ソリューション・スティックパッケージには、データを送信する前に、送信者を取得する方法、周りにある、バイトストリーム受信を可能にするために送信されます、独自の合計サイズその終わり、と死のフェッチ受信周期は、すべてのデータを受信しました。
シンプルなソリューションバージョン4.1
サーバー:
import socket
import subprocess
import struct
HOST = "192.168.11.237"
PORT = 8082
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
soc.bind((HOST,PORT))
soc.listen(5)
while 1:
print("等待连接。。。")
conn,addr = soc.accept()
print("连接成功。。。\n")
while 1:
try:
data = conn.recv(1024)
if len(data)==0: # 长度0说明断开了连接。在windows中没用,在linux中才有用
break
# 把执行正确的内容放到管道中
obj = subprocess.Popen(str(data, encoding="utf8"), shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
# 执行的结果 b 格式,gbk编码(windows平台)
suc_data = obj.stdout.read()
fail_data = obj.stderr.read()
if suc_data:
# 1.先打包数据长度,为四个字节,在发过去
head = struct.pack("i", len(suc_data))
conn.send(head) # 作为头信息发过去,字节格式
# 2.发真正的数据信息
conn.send(suc_data)
else:
# 1.先打包数据长度,为四个字节,在发过去
head = struct.pack("i", len(fail_data))
conn.send(head) # 作为头信息发过去,字节格式
# 发错误信息
conn.send(fail_data)
except:
print("客户机连接中断。。。")
break
conn.close()
soc.close()
クライアント:
import socket
import struct
HOST = "192.168.11.237"
PORT = 8082
soc = socket.socket()
soc.connect((HOST,PORT))
while 1:
try:
cmd = input("请输入需要执行的命令")
soc.send(cmd.encode("utf8"))
# 1.得到数据的头信息并解包
head = soc.recv(4)
head_data = struct.unpack("i",head)[0]
if head == 0:
break
# 2.根据头信息,拿到数据长度来接收数据
data = b""
while head_data>1024:
# 先拿1024
data += soc.recv(1024)
head_data -= 1024
data += soc.recv(head_data)
print(data.decode("gbk"))
except Exception as a:
print("服务器关闭了:", a)
break
# 4.关闭连接
soc.close()
アルティメットエディション4.1ソリューション(XC版)
ソリューションのシンプルなバージョンは、本当にスティックパッケージの問題を解決することができますが、データが送信される場合、問題があまりにもいつ発生します。ここに私の解決策はあります
サーバー:
import socket
import subprocess
import struct
import json
HOST = "192.168.11.24"
PORT = 8080
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
soc.bind((HOST,PORT))
soc.listen(5)
while 1:
print("等待连接。。。")
conn,addr = soc.accept()
print(f"主机{addr}连接成功,准备接收消息中。。。")
while 1:
try:
# 接收命令
cmd = conn.recv(1024)
# 处理命令
obj = subprocess.Popen(cmd.decode("utf8"), shell=True, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
suc_data = obj.stdout.read()
fail_data = obj.stderr.read()
if suc_data:
# 1.先构造一个字典,把头信息放到字典中
head_dic = {"data_size": len(suc_data), "md5":None, "file_name": None}
# 2.对该字典序列化,得到字符串(json串)
head_dic_json_byte = (json.dumps(head_dic)).encode("utf8")
# 3.将 序列化后的字典 长度打包,作为头部
head = struct.pack("i",len(head_dic_json_byte))
# 4.先发头部
conn.send(head)
# 5.再发 序列化后的字典(字节类型)
conn.send(head_dic_json_byte)
# 6.最后再发真正的数据
conn.send(suc_data)
else:
# 1.先构造一个字典,把头信息放到字典中
head_dic = {"data_size": len(fail_data), "md5": None, "file_name": None}
# 2.对该字典序列化,得到字符串(json串)
head_dic_json_byte = (json.dumps(head_dic)).encode("utf8")
# 3.将 序列化后的字典 长度打包,作为头部(字节类型)
head = struct.pack("i", len(head_dic_json_byte))
# 4.先发头部
conn.send(head)
# 5.再发 序列化后的字典(字节类型)
conn.send(head_dic_json_byte)
# 6.最后再发真正的数据
conn.send(fail_data)
except:
print("客户机连接中断。。。\n")
break
conn.close()
soc.close()
クライアント
import socket
import struct
import json
HOST = "192.168.11.24"
PORT = 8080
soc = socket.socket()
soc.connect((HOST,PORT))
while 1:
try:
cmd = input("请输入需要执行的命令")
soc.send(cmd.encode("utf8"))
# 1.先拿到头数据,并解包
head = soc.recv(4)
head_dic_len = struct.unpack("i",head)[0]
if len(head) == 0: # 服务器关闭了
break
# 2.根据头字典长度拿到头字典信息,进行反序列化,拿到头字典
head_dic_btye = soc.recv(head_dic_len)
head_dic = json.loads(head_dic_btye) # json可以直接反序列化bytes类型
# 3.根据头字典中的数据长度拿到真正的数据
data = b""
data_len= head_dic.get("data_size")
while data_len > 1024:
# 先拿1024
data += soc.recv(1024)
data_len -= 1024
data += soc.recv(data_len)
print(data.decode("gbk"))
except Exception as a:
print("服务器关闭了:", a)
break
# 4.关闭连接
soc.close()