スティックパック現象とは何ですか

シンプルなリモートコマンド実行プログラムの開発(30分)

ユーザーソケットはドライポイントである場合にはダウンビジネスにええ、私たちは、リモートコマンド実行プログラムを記述する必要があり、ソケットクライアントがWindowsで終了指示を送り、ソケットサーバはLinux側でコマンドを実行し、結果をクライアントに返す書きます

私たちの学んだ友人サブプロセスモジュールと、その後確かに、コマンドを実行しますが、注意注意:

res = subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=subprocess.PIPE,stdout=subprocess.PIPE)

コーディングコマンドの結果は、それがWindowsの場合、現在のシステムを対象とし、(res.stdout.read)GBKが符号化され読み取る受信端で必要GBK復号化を使用する、唯一のパイプから読み取ることができます結果

SSHサーバ

import socket
import subprocess

ip_port = ('127.0.0.1', 8080)


tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)

while True:
    conn, addr = tcp_socket_server.accept()
    print('客户端', addr)

    while True:
        cmd = conn.recv(1024)
        if len(cmd) == 0: break
        print("recv cmd",cmd)
        res = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                               stdout=subprocess.PIPE,
                               stdin=subprocess.PIPE,
                               stderr=subprocess.PIPE)

        stderr = res.stderr.read()
        stdout = res.stdout.read()
        print("res length",len(stdout))
        conn.send(stderr)
        conn.send(stdout)

sshクライアント

import socket
ip_port = ('127.0.0.1', 8080)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
res = s.connect_ex(ip_port)

while True:
    msg = input('>>: ').strip()
    if len(msg) == 0: continue
    if msg == 'quit': break

    s.send(msg.encode('utf-8'))
    act_res = s.recv(1024)

    print(act_res.decode('utf-8'), end='')

LS、pwdコマンドを実行しようと、あなたは正しい結果を得るために、見つけることが驚いています!

しかし、Moは早すぎる、などトップ-bn 1限りのコマンドの実装の結果は、あなたはまだあなたが結果を得ることができますが、我々は、DFの-hを実行した場合、その後、あなたはあなたが得ることを見つけることがわかり、この時点でdfコマンドではありません幸せその結果が、部分的な結果のtopコマンドで。なぜ絞りますか?なぜ絞りますか?なぜ絞りますか?

比較的長いのtopコマンドが、クライアントのみのrecv(1024)の結果は、ように行う方法を、ここで成長し、1024以上の結果であるが、クライアントも離れ没収IOバッファサーバに一時的に生き残る、とすることができますので、クライアントは、次の時間を受け取ったので、クライアント第二コールのrecv(1024)は、最後のデータが最初のダウンを受け没収した後、dfコマンドの結果を受け終えることが初めてとなるとき。

次に、どのようにそれを解決するには?一部の学生は事前にお互いから返された結果を知ることができないので、直接のrecv(1024)に、10000 \ 5000に、十分に大きな変更、または何でも。しかし、私のプロなので、乾燥し、それが本当の問題を解決していませんしないことを言いますこれは本当にのrecvが簡単に特に大きな変更できない一方、下のビッグビッグデータは、関係なく、あなたがどのように変化するかくらい、他の結果は、あなたよりも設定する可能性がありません、当局は8192以下を推奨しますが、もう一つの大きな影響があるだろう送信速度と不安定性の送受信

同志は、この現象は、二つの結果が一緒に固執する意味、スティックのパッケージと呼ばれています。ソケットバッファは、外観を引き起こした主な理由に発生します

あなたのプログラムは、実際のカードの動作を指示する権利、あなたは、オペレーティング・システム・インターフェースを介してユーザプログラムにさらされているカード操作されていないプログラムでは、リモート与えるためにデータを送信するたびに、実際には、最初のユーザーモードからデータをコピーすることであるということですカーネルモードでは、このような動作は、リソース集約的で時間で、カーネルモードとユーザモードが切り替え頻繁に前のデータは必然的に後に収集センダしばしば十分なデータ、伝送効率が低下につながり、したがって、伝送効率を向上させるためにソケットう一度だけ、相互にデータを送信します。連続した送信データはほとんど必要とされない場合、受信側は、粘着性のデータパケットを受信するように、典型的にはTCPソケットは、TCPセグメントアウト最適化アルゴリズムの合成に従ってデータを送信します。

スティックパッケージは、TCP、UDPない唯一の問題であり、

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

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

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

概要

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

UDPベースのコマンド実行プログラム(10分)

その上で、UDPスティックパッケージは問題ではありません、私たちは例を見て

UDPサーバー

import socket
import subprocess

ip_port = ('127.0.0.1', 9003)
bufsize = 1024

udp_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_server.bind(ip_port)

while True:
    # 收消息
    cmd, addr = udp_server.recvfrom(bufsize)
    print('用户命令----->', cmd,addr)

    # 逻辑处理
    res = subprocess.Popen(cmd.decode('utf-8'), shell=True, stderr=subprocess.PIPE, stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE)
    stderr = res.stderr.read()
    stdout = res.stdout.read()

    # 发消息
    udp_server.sendto(stdout + stderr, addr)

udp_server.close()

UDPクライアント

from socket import *

import time

ip_port = ('127.0.0.1', 9003)
bufsize = 1024

udp_client = socket(AF_INET, SOCK_DGRAM)

while True:
    msg = input('>>: ').strip()
    if len(msg) == 0:
        continue

    udp_client.sendto(msg.encode('utf-8'), ip_port)
    data, addr = udp_client.recvfrom(bufsize)
    print(data.decode('utf-8'), end='')

 

スティッキーソリューションパッケージ(35分)

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

普通の若いバージョン

サーバー側

import socket,subprocess
ip_port=('127.0.0.1',8080)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

s.bind(ip_port)
s.listen(5)

while True:
    conn,addr=s.accept()
    print('客户端',addr)
    while True:
        msg=conn.recv(1024)
        if not msg:break
        res=subprocess.Popen(msg.decode('utf-8'),shell=True,\
                            stdin=subprocess.PIPE,\
                         stderr=subprocess.PIPE,\
                         stdout=subprocess.PIPE)
        err=res.stderr.read()
        if err:
            ret=err
        else:
            ret=res.stdout.read()
        data_length=len(ret)
        conn.send(str(data_length).encode('utf-8'))
        data=conn.recv(1024).decode('utf-8')
        if data == 'recv_ready':
            conn.sendall(ret)
    conn.close()

クライアント

import socket,time
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(('127.0.0.1',8080))

while True:
    msg=input('>>: ').strip()
    if len(msg) == 0:continue
    if msg == 'quit':break

    s.send(msg.encode('utf-8'))
    length=int(s.recv(1024).decode('utf-8'))
    s.send('recv_ready'.encode('utf-8'))
    send_size=0
    recv_size=0
    data=b''
    while recv_size < length:
        data+=s.recv(1024)
        recv_size+=len(data) #为什么不直接写1024?


    print(data.decode('utf-8'))

なぜ低いですか?

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

ただ、上記の最初のそれはスティックのパッケージの問題(ベアラメッセージの長さを持つことになり、承認ダイレクトメッセージを待たずに、すぐメッセージが受信されたリターンの確認が終了するのを待つ必要があり、ピアにメッセージを送信する前にメッセージの長さを送信する必要がありますがそのメッセージとメッセージの自体が)一緒に固執します。それを最適化するためには良い方法はありませんか?

若いアーティストのバージョン

メッセージの長さを送信しない理由問題を考える、端部を通ってようにように、互いにため頭部と胴体スティックの恐怖から、直ちにメッセージの内容の端(身体がそれを呼ばれる)に(それのメッセージヘッダヘッドとも呼ばれる)と両者を切断する肯定応答メッセージを返します。

これは、直接ヘッド+ボディを送ることができますが、それの本体であるヘッドであり、これを区別するためにピアを作ることができますか?私は私が感じる知性に依存している、に依存しています。

私は頭の中で書かれた、データの第1の固定長のセットを受信し、あれば受信したメッセージの終了時刻として、ヘッドダイが固定長に配置され、考え、このメッセージに属する多くのデータがあり、その後、直接書込みサイクル閉鎖まあ終わっていません!OMGああ、私は本当に気の利きました。

しかし、どのように固定長のヘッダにそれを作るには?あなたは2を送信するメッセージがあると、最初のメッセージの長さは、メッセージ2が200バイトで、3000バイトです。メッセージのみが、メッセージヘッダ長が含まれている場合、最初の2件のメッセージがメッセージであります

len(msg1) = 4000 = 4字节
len(msg2) = 200 = 3字节

どのようにあなたのサーバーは、メッセージ・ヘッダー、それを受けて完成するには?(4)サーバがどのように知っているのrecv(3)またはrecvのですか?疲れすべての私の知識、私は唯一のアナロジーは、空の文字列スプライシングを取るのに十分ではない、100バイト長の固定ヘッダを設定することで、文字列を連結するための方法を考えることができます。

サーバ

import socket,json
import subprocess

ip_port = ('127.0.0.1', 8080)

tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_socket_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #一行代码搞定,写在bind之前
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)

def pack_msg_header(header,size):
    bytes_header = bytes(json.dumps(header),encoding="utf-8")
    fill_up_size = size -  len(bytes_header)
    print("need to fill up ",fill_up_size)

    header['fill'] = header['fill'].zfill(fill_up_size)
    print("new header",header)
    bytes_new_header = bytes(bytes(json.dumps(header),encoding="utf-8"))
    return bytes_new_header

while True:
    conn, addr = tcp_socket_server.accept()
    print('客户端', addr)

    while True:
        cmd = conn.recv(1024)
        if len(cmd) == 0: break
        print("recv cmd",cmd)
        res = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                               stdout=subprocess.PIPE,
                               stdin=subprocess.PIPE,
                               stderr=subprocess.PIPE)

        stderr = res.stderr.read()
        stdout = res.stdout.read()
        print("res length",len(stdout))

        msg_header = {
            'length':len(stdout + stderr),
            'fill':''
        }
        packed_header = pack_msg_header(msg_header,100)
        print("packed header size",packed_header,len(packed_header))
        conn.send(packed_header)
        conn.send(stdout + stderr)

クライアント

import socket
import json

ip_port = ('127.0.0.1', 8080)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
res = s.connect_ex(ip_port)

while True:
    msg = input('>>: ').strip()
    if len(msg) == 0: continue
    if msg == 'quit': break

    s.send(msg.encode('utf-8'))
    response_msg_header = s.recv(100).decode("utf-8")

    response_msg_header_data = json.loads(response_msg_header)
    msg_size = response_msg_header_data['length']

    res = s.recv(msg_size)
    print("received res size ",len(res))
    print(res.decode('utf-8'), end='')

若いアーティスト版

バイトのカスタム・ストリームプラス固定長ヘッダは、サードパーティのモジュール構造体の使用によってであってもよいです

import json,struct
#假设通过客户端上传1T:1073741824000的文件a.txt

#为避免粘包,必须自定制报头
header={'file_size':1073741824000,'file_name':'/a/b/c/d/e/a.txt','md5':'8f6fbf8347faa4924a76856701edb0f3'} #1T数据,文件路径和md5值

#为了该报头能传送,需要序列化并且转为bytes
head_bytes=bytes(json.dumps(header),encoding='utf-8') #序列化并转成bytes,用于传输

#为了让客户端知道报头的长度,用struck将报头长度这个数字转成固定长度:4个字节
head_len_bytes=struct.pack('i',len(head_bytes)) #这4个字节里只包含了一个数字,该数字是报头的长度

#客户端开始发送
conn.send(head_len_bytes) #先发报头的长度,4个bytes
conn.send(head_bytes) #再发报头的字节格式
conn.sendall(文件内容) #然后发真实内容的字节格式

#服务端开始接收
head_len_bytes=s.recv(4) #先收报头4个bytes,得到报头长度的字节格式
x=struct.unpack('i',head_len_bytes)[0] #提取报头的长度

head_bytes=s.recv(x) #按照报头长度x,收取报头的bytes格式
header=json.loads(json.dumps(header)) #提取报头

#最后根据报头的内容提取真实的数据,比如
real_data_len=s.recv(header['file_size'])
s.recv(real_data_len)

次のように構造体モジュールは、使用して実装します

サーバ

import socket,struct,json
import subprocess
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加

phone.bind(('127.0.0.1',8080))

phone.listen(5)

while True:
    conn,addr=phone.accept()
    while True:
        cmd=conn.recv(1024)
        if not cmd:break
        print('cmd: %s' %cmd)

        res=subprocess.Popen(cmd.decode('utf-8'),
                             shell=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        err=res.stderr.read()
        print(err)
        if err:
            back_msg=err
        else:
            back_msg=res.stdout.read()

        headers={'data_size':len(back_msg)}
        head_json=json.dumps(headers)
        head_json_bytes=bytes(head_json,encoding='utf-8')

        conn.send(struct.pack('i',len(head_json_bytes))) #先发报头的长度
        conn.send(head_json_bytes) #再发报头
        conn.sendall(back_msg) #在发真实的内容

    conn.close()

クライアント

from socket import *
import struct,json

ip_port=('127.0.0.1',8080)
client=socket(AF_INET,SOCK_STREAM)
client.connect(ip_port)

while True:
    cmd=input('>>: ')
    if not cmd:continue
    client.send(bytes(cmd,encoding='utf-8'))

    head=client.recv(4) #先收4个bytes,这里4个bytes里包含了报头的长度
    head_json_len=struct.unpack('i',head)[0] #解出报头的长度
    head_json=json.loads(client.recv(head_json_len).decode('utf-8')) #拿到报头
    data_len=head_json['data_size'] #取出报头内包含的信息

    #开始收数据
    recv_size=0
    recv_data=b''
    while recv_size < data_len:
        recv_data+=client.recv(1024)
        recv_size=len(recv_data)

    print(recv_data.decode('utf-8'))
    #print(recv_data.decode('gbk')) #windows默认gbk编码

おすすめ

転載: www.cnblogs.com/yuexijun/p/11410305.html