ソケットネットワークプログラミングの章VIII(8):スティックパック現象およびソリューション(コード補完)

スティックパック現象

.recv(1024)ピット:一度だけのrecvが1024バイトを受け入れるようにするのでデータは、1024bytes時間以上を転送する場合、パイプラインは送信これに戻ってつながるデータ処理の次のrecv→バックログを受信し続けるデータ→バックログを転送します結果は、結果の一部は、最後の内容を返すことがあり、

現象スティックパッケージ:TCPプロトコルメッセージに対応する表示されていない、すなわち、バイト数、(トランスポートストリーム全体のデータである)プロトコルストリームデータです。受信者は、データの混乱を作り、データを区切る方法がわからない受け入れるときにこれが結果。これは、スティックパック現象と呼ばれています

注:のみ、TCPパケットが粘着性の現象があり、UDPはパッケージスティックはありません

参照

スティックパッケージ詳細説明センドとのrecv

まず、我々は強調する:
アプリケーション・ソフトウェアがハードウェアを直接操作することができない、ハードウェアがOSによって呼び出される必要があります。したがって、アプリケーションは、送信およびOSは、データ送信(アプリケーション層)のためのものであるrecvを。
他は完全にOSによって引き継がれている基本的なネットワークプロトコルを実現します。

  • スティックのパッケージの概要:
    • 送信またはrecvのは、データのローカルOSメモリではなく、ターゲット・サーバーとの直接データ交換のために交換されているかどうか

      注意:あなたが唯一の対応が一度ではありませんrecvを送信することができ、任意の回数に対応することができ、任意の回数

    • RECV、処理を送信します。
      • RECV:
        • データを待つ:OSは時間のかかるデータを待つ(待機する必要がありする必要があります)
        • コピーデータ:データは、メモリ、OSのアプリケーションにコピーします
      • 送信:
        • データを送信する:メモリ、OSにアプリケーションソフトからのデータをコピーします
      • スティックパッケージ:
        • メモリの残りの部分のデータように、データをフェッチ受信すると、データパケットは、大きすぎます。
        • データをフェッチ受信すると、(ネーグルによる)複数のパケットなどのデータパケットが受信されました。
    • これは、パッケージを固執する理由です:TCPは、マージされたパケットの送信を最適化するために、Nagleアルゴリズムをストリーミング

      Nagleアルゴリズム:少量のデータパケット送信パケット組成物の比較的短い時間間隔

  • ソリューション:
    • バイトのパケットの正確番号を知っている、受信側が情報のパケットであることができ、事前に伝える→スティックパッケージを回避することが可能です
問題を解決するために、パッケージをスティック

私たちはsshのシミュレーションプログラムの説明を使用します。

何を私は大きなによって設定されたパラメータがrecvを、これは固定値であることができ、最大メモリ容量を超えることはありません、それは良い計画ではありません

  • だから、どのように我々は、ケースを分析します:
    • これらのメッセージは、お互いに送信され、そのデータを送信するときにしています
      • 互いに送信データ長
      • そして、お互いにデータを送信します

server.py

import socket
import subprocess
import struct
import locale

ssh_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ssh_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ssh_server.bind(('127.0.0.1', 8080))
ssh_server.listen(3)

while True:
    conn, addr = ssh_server.accept()
    while True:
        try:
            cmd = conn.recv(1024).decode('utf-8')
            if not cmd: break
            print("excute cmd:", cmd)
            obj = subprocess.Popen(cmd, shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            stdout = obj.stdout.read()
            stderr = obj.stderr.read()

            os_locale = locale.getdefaultlocale()
            os_encode = os_locale[1]
            print(stdout.decode(os_encode))

            # 第一步:生成数据的报头(报头:固定长度):需要用到struct模块
            # 我们首先需要把发送数据的描述信息发送给客户端,但是客户端并不知道这个包是干什么的,而且会和数据的包粘包
            # 所以我们把这段数据作为数据的报头发送
            total_size = len(stdout) + len(stderr)
            header = struct.pack('i', total_size)  # 打包报头,打包返回的数据是一个固定长度(4bytes)的bytes类型
            # struct.pack(模式,报头数据),注意:'i'模式int范围:正负2*10^9之内(因为只有4bytes)
            # 另外有'l'模式:长整型。但是这个模式报头为8bytes。也有范围限制

            # 第二部:发送报头
            conn.send(header)  # 因为是bytes类型所以可以直接发送

            # 把命令结果返回给客户端
            # conn.send(stdout+stderr)  # 这里之前用+连接一起发送,但其实粘包也可以一起发送
            conn.send(stdout)  # 连续发送可以粘包
            conn.send(stderr)  # 与上一行粘包成一个数据包了
        except ConnectionResetError as e:
            print(e)
            break
    conn.close()

ssh_server.close()

client.py

import socket
import struct

ssh_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

ssh_client.connect(('127.0.0.1', 8080))

while True:
    # 发命令
    cmd = input(">>>>:").strip()
    if not cmd:continue
    ssh_client.send(cmd.encode('utf-8'))

    # 第一步:先收报头
    header = ssh_client.recv(4)  # 因为报头固定4byte 所以我们收4

    # 第二部:从报头解析出数据的描述(数据的长度)
    total_size = struct.unpack('i', header)[0]  # 解包报头,返回一个元组类型,这里我们第一个元素是长度
    print(total_size)  # 打印一下报头内容

    # 第三部:接受真实收据
    recv_size = 0
    recv_data = b''
    while recv_size < total_size:
        res = ssh_client.recv(1024)
        recv_data += res
        recv_size += len(res)

    print(recv_data.decode('cp932'))  # 因为粘包问题解决了,这里我们直接打印

ssh_client.close()

おすすめ

転載: www.cnblogs.com/py-xiaoqiang/p/11298995.html