it-swarm.com.de

Python Socket empfängt große Datenmengen

Wenn ich versuche, größere Datenmengen zu erhalten, werden diese abgeschnitten und ich muss die Eingabetaste drücken, um die restlichen Daten zu erhalten. Anfangs konnte ich es etwas erhöhen, aber es wird immer noch nicht alles erhalten. Wie Sie sehen, habe ich den Puffer für conn.recv () erhöht, aber es werden immer noch nicht alle Daten angezeigt. Es schneidet es an einem bestimmten Punkt ab. Ich muss auf meinem raw_input die Eingabetaste drücken, um die restlichen Daten zu erhalten. Kann ich trotzdem alle Daten gleichzeitig abrufen? Hier ist der Code. 

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()
35
user2585107

TCP/IP ist ein Stream-basiertes Protokoll, kein Message-basiertes Protokoll. Es kann nicht garantiert werden, dass jeder send()-Aufruf eines Peers dazu führt, dass ein einzelner recv()-Anruf vom anderen Peer die genauen gesendeten Daten empfängt. Möglicherweise erhält er die Datenmahlzeit, die aufgrund von Paketfragmentierung auf mehrere recv()-Aufrufe aufgeteilt ist.

Sie müssen über TCP ein eigenes, auf Nachrichten basierendes Protokoll definieren, um die Nachrichtengrenzen zu unterscheiden. Um eine Nachricht zu lesen, rufen Sie weiterhin recv() auf, bis Sie eine gesamte Nachricht gelesen haben oder ein Fehler auftritt.

Eine einfache Methode zum Senden einer Nachricht besteht darin, jeder Nachricht ihre Länge voranzustellen. Um eine Nachricht zu lesen, lesen Sie zuerst die Länge und dann die Anzahl der Bytes. So können Sie das machen:

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 = b''
    while len(data) < n:
        packet = sock.recv(n - len(data))
        if not packet:
            return None
        data += packet
    return data

Dann können Sie die Funktionen send_msg und recv_msg verwenden, um ganze Nachrichten zu senden und zu empfangen. Sie haben keine Probleme mit der Aufteilung oder Zusammenführung von Paketen auf Netzwerkebene.

97
Adam Rosenfield

Sie können es verwenden als: 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
17
JadedTuna

Die akzeptierte Antwort ist in Ordnung, aber mit großen Dateien wird es sehr langsam. -String ist eine unveränderliche Klasse. Das bedeutet, dass bei jeder Verwendung des +-Zeichens mehr Objekte erstellt werden. Die Verwendung von list als Stapelstruktur ist effizienter.

Das sollte besser funktionieren 

while True: 
    chunk = s.recv(10000)
    if not chunk: 
        break
    fragments.append(chunk)

print "".join(fragments)
5
Mina Gabriel

Möglicherweise müssen Sie conn.recv () mehrmals aufrufen, um alle Daten zu erhalten. Ein einmaliger Aufruf ist nicht garantiert, um alle gesendeten Daten einzubringen, da TCP -Streams keine Frame-Grenzen beibehalten (dh sie arbeiten nur als Stream roher Bytes und nicht als strukturiert Nachrichtenstrom).

Siehe diese Antwort für eine andere Beschreibung des Problems.

Beachten Sie, dass dies bedeutet, dass Sie wissen müssen, wann Sie alle Daten erhalten haben. Wenn der Sender immer genau 8000 Bytes sendet, können Sie die Anzahl der Bytes zählen, die Sie bisher erhalten haben, und von 8000 abziehen, um zu wissen, wie viele noch empfangen werden. Wenn die Daten eine variable Größe haben, können verschiedene andere Methoden verwendet werden, z. B. indem der Absender vor dem Senden der Nachricht einen Header mit einer Anzahl von Bytes sendet oder wenn der Text ASCII Ihnen gesendet wird könnte nach einem Newline- oder NUL-Zeichen suchen.

4
Jeremy Friesner

Eine Variation, die eine Generatorfunktion verwendet (die ich für mehr Pythonic halte):

def recvall(sock, buffer_size=4096):
    buf = sock.recv(buffer_size)
    while buf:
        yield buf
        if len(buf) < buffer_size: break
        buf = sock.recv(buffer_size)
# ...
with socket.create_connection((Host, port)) as sock:
    sock.sendall(command)
    response = b''.join(recvall(sock))
3
yoniLavi

Code von Adam Rosenfield ändern:

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

Ich würde jedoch den ursprünglichen Ansatz nachdrücklich ermutigen.

1
sjMoquin

Die meisten Antworten beschreiben eine Art recvall() -Methode. Wenn Ihr Engpass beim Datenempfang darin besteht, das Byte-Array in einer for -Schleife zu erstellen, habe ich drei Methoden zum Zuweisen der empfangenen Daten in der recvall() -Methode verglichen:

Byte-String-Methode:

arr = b''
while len(arr) < msg_len:
    arr += sock.recv(max_msg_size)

Listenmethode:

fragments = []
while True: 
    chunk = sock.recv(max_msg_size)
    if not chunk: 
        break
    fragments.append(chunk)
arr = b''.join(fragments)

Vorab zugewiesene bytearray Methode:

arr = bytearray(msg_len)
pos = 0
while pos < msg_len:
    arr[pos:pos+max_msg_size] = sock.recv(max_msg_size)
    pos += max_msg_size

Ergebnisse:

enter image description here

0
Jacob Stern

Für alle anderen, die nach einer Antwort suchen, wenn Sie die Länge des Pakets vorher nicht kennen. Hier ist eine einfache Lösung, die jeweils 4096 Bytes liest und stoppt, wenn weniger als 4096 Bytes empfangen wurden. Es funktioniert jedoch nicht in Fällen, in denen die Gesamtlänge des empfangenen Pakets genau 4096 Bytes beträgt - dann ruft es recv() erneut auf und hängt.

def recvall(sock):
    data = b''
    bufsize = 4096
    while True:
        packet = sock.recv(bufsize)
        data += packet
        if len(packet) < bufsize:
            break
    return data
0
vatsug

Sie können dies mit der Serialisierung tun

from socket import *
from json import dumps, loads

def recvall(conn):
    data = ""
    while True:
    try:
        data = conn.recv(1024)
        return json.loads(data)
    except ValueError:
        continue

def sendall(conn):
    conn.sendall(json.dumps(data))

HINWEIS: Wenn Sie eine Datei mit dem obigen Code speichern möchten, müssen Sie sie in base64 codieren/decodieren

0
John Albert