粘着性の問題を解決するために103パッケージ

まず、スティックパッケージは何ですか

問題があれば、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を送信すると仮定します。

  1. 二回サーバは、2つの別々のデータパケット、それぞれD1およびD2を読み、パッケージアンパックを付着しないように。
  2. サーバはTCPスティックパッケージと呼ばれる、2は、D1およびD2は、互いに結合されているパケットを受信します。
  3. 二回サーバは2つのデータパケット、最初の時間を読み取るために、パッケージの完全読出しパケットD1およびD2部品、TCPはアンパックと呼ばれ、残りのパッケージの内容D2、第2読取。
  4. パッケージ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()

おすすめ

転載: www.cnblogs.com/XuChengNotes/p/11480768.html