68

大量のデータを受信しようとすると途切れてしまい、残りのデータを取得するには Enter キーを押す必要があります。最初は少し増やすことができましたが、まだすべてを受け取ることはできません。ご覧のとおり、conn.recv() のバッファーを増やしましたが、まだすべてのデータを取得していません。それはある時点でそれを遮断します。残りのデータを受け取るには、raw_input で Enter キーを押す必要があります。一度にすべてのデータを取得できる方法はありますか? これがコードです。

port = 7777
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('0.0.0.0', port))
sock.listen(1)
print ("Listening on port: "+str(port))
while 1:
    conn, sock_addr = sock.accept()
    print "accepted connection from", sock_addr
    while 1:
        command = raw_input('shell> ')
        conn.send(command)
        data = conn.recv(8000)
        if not data: break
        print data,
    conn.close()
4

12 に答える 12

152

TCP/IP は、メッセージベースのプロトコルではなく、ストリームベースのプロトコルです。1 つのピアによるすべての呼び出しが、送信された正確なデータを受信するもう1 つのピアによる 1 つの呼び出しになるという保証はありません。パケットの断片化により、複数の呼び出しに分割されてデータを断片的に受信する可能性があります。send()recv()recv()

メッセージ境界を区別するために、TCP の上に独自のメッセージベースのプロトコルを定義する必要があります。次に、メッセージを読むために、メッセージrecv()全体を読むか、エラーが発生するまで呼び出しを続けます。

メッセージを送信する簡単な方法の 1 つは、各メッセージにその長さのプレフィックスを付けることです。次に、メッセージを読み取るには、最初に長さを読み取り、次にそのバイト数を読み取ります。これを行う方法は次のとおりです。

def send_msg(sock, msg):
    # Prefix each message with a 4-byte length (network byte order)
    msg = struct.pack('>I', len(msg)) + msg
    sock.sendall(msg)

def recv_msg(sock):
    # Read message length and unpack it into an integer
    raw_msglen = recvall(sock, 4)
    if not raw_msglen:
        return None
    msglen = struct.unpack('>I', raw_msglen)[0]
    # Read the message data
    return recvall(sock, msglen)

def recvall(sock, n):
    # Helper function to recv n bytes or return None if EOF is hit
    data = bytearray()
    while len(data) < n:
        packet = sock.recv(n - len(data))
        if not packet:
            return None
        data.extend(packet)
    return data

次に、 関数send_msgrecv_msg関数を使用してメッセージ全体を送受信できます。ネットワーク レベルでパケットが分割または合体する問題は発生しません。

于 2013-07-16T04:27:25.460 に答える
30

次のように使用できます。data = recvall(sock)

def recvall(sock):
    BUFF_SIZE = 4096 # 4 KiB
    data = b''
    while True:
        part = sock.recv(BUFF_SIZE)
        data += part
        if len(part) < BUFF_SIZE:
            # either 0 or end of data
            break
    return data
于 2013-07-17T10:43:35.507 に答える
5

すべてのデータを受信するには、 conn.recv() を複数回呼び出す必要がある場合があります。TCP ストリームはフレーム境界を維持しないため (つまり、メッセージの構造化されたストリームではなく、生のバイトのストリームとしてのみ機能します) .

この問題の別の説明については、この回答を参照してください。

これは、すべてのデータをいつ受信したかを知る何らかの方法が必要であることを意味することに注意してください。送信者が常に正確に 8000 バイトを送信する場合、これまでに受信したバイト数を数え、8000 からそれを引いて、受信する残りのバイト数を知ることができます。データが可変サイズの場合、メッセージを送信する前に送信者にバイト数のヘッダーを送信させるなど、使用できる他のさまざまな方法があります。または、送信されているのが ASCII テキストの場合は、改行または NUL 文字。

于 2013-07-16T04:21:29.737 に答える
0

Adam Rosenfield のコードの変更:

import sys


def send_msg(sock, msg):
    size_of_package = sys.getsizeof(msg)
    package = str(size_of_package)+":"+ msg #Create our package size,":",message
    sock.sendall(package)

def recv_msg(sock):
    try:
        header = sock.recv(2)#Magic, small number to begin with.
        while ":" not in header:
            header += sock.recv(2) #Keep looping, picking up two bytes each time

        size_of_package, separator, message_fragment = header.partition(":")
        message = sock.recv(int(size_of_package))
        full_message = message_fragment + message
        return full_message

    except OverflowError:
        return "OverflowError."
    except:
        print "Unexpected error:", sys.exc_info()[0]
        raise

ただし、元のアプローチを使用することを強くお勧めします。

于 2015-02-04T23:07:33.343 に答える