it-swarm.dev

Python المقبس يتلقى كمية كبيرة من البيانات

عندما أحاول تلقي كميات أكبر من البيانات ، يتم قطعها وعلي الضغط على إدخال للحصول على بقية البيانات. في البداية ، تمكنت من زيادتها قليلاً لكنها ما زالت لن تحصل عليها. كما ترى ، لقد قمت بزيادة المخزن المؤقت على conn.recv () لكنه لا يزال لا يحصل على جميع البيانات. انها تقطعها في مرحلة معينة. لا بد لي من الضغط على إدخال على بلدي الخام من أجل الحصول على بقية البيانات. هل هناك أي حال يمكنني الحصول على جميع البيانات في وقت واحد؟ إليك الكود.

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

TCP/IP هو مستند إلى دفق بروتوكول ، وليس مستند إلى الرسائل بروتوكول. ليس هناك ما يضمن أن كل مكالمة send() بواسطة نظير واحد تؤدي إلى مكالمة واحدة recv() من قِبل النظير الآخر الذي يتلقى البيانات الدقيقة المرسلة — قد تتلقى رسالة نصية للبيانات ، مقسمة عبر عدة recv() المكالمات ، بسبب تجزئة الحزمة.

تحتاج إلى تعريف بروتوكولك القائم على الرسائل أعلى TCP من أجل التمييز بين حدود الرسائل. ثم ، لقراءة رسالة ، تواصل الاتصال recv() حتى تقوم " لقد قرأت رسالة كاملة أو حدث خطأ.

طريقة بسيطة لإرسال رسالة هي بادئة كل رسالة بطولها. ثم لقراءة رسالة ، تقرأ أولاً الطول ، ثم تقرأ عدد البايتات هذا. إليك كيفية القيام بذلك:

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

ثم يمكنك استخدام send_msg و recv_msg وظائف لإرسال واستقبال الرسائل بأكملها ، ولن تواجه أي مشاكل في تقسيم الحزم أو دمجها على مستوى الشبكة.

108
Adam Rosenfield

يمكنك استخدامه على النحو التالي: 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
19
JadedTuna

الإجابة المقبولة جيدة لكنها ستكون بطيئة حقًا مع الملفات الكبيرة - السلسلة عبارة عن فئة ثابتة وهذا يعني أنه يتم إنشاء المزيد من الكائنات في كل مرة تستخدم فيها + sign ، باستخدام list كك بنية مكدس سيكون أكثر فعالية.

هذا يجب أن تعمل بشكل أفضل

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

print "".join(fragments)
8
Mina Gabriel

قد تحتاج إلى الاتصال بـ conn.recv () عدة مرات لاستلام جميع البيانات. لا يُعتبر استدعاءها مرة واحدة مضمونًا لإحضار جميع البيانات التي تم إرسالها ، نظرًا لحقيقة أن TCP تدفقات لا تحتفظ بحدود الإطار (أي أنها تعمل فقط كدفق من الخام بايت ، وليس دفق منظم من الرسائل).

راجع هذه الإجابة للحصول على وصف آخر لهذه المشكلة.

لاحظ أن هذا يعني أنك بحاجة إلى طريقة ما لمعرفة متى تلقيت جميع البيانات. إذا كان المرسل سيرسل دائمًا 8000 بايت بالضبط ، فيمكنك حساب عدد البايتات التي تلقيتها حتى الآن وطرح ذلك من 8000 لمعرفة العدد المتبقي لتلقيها ؛ إذا كانت البيانات متغيرة الحجم ، فهناك العديد من الطرق الأخرى التي يمكن استخدامها ، مثل جعل المرسل يرسل رأسًا لعدد البايتات قبل إرسال الرسالة ، أو إذا كان ASCII) التي يتم إرسالها يمكنك البحث عن حرف السطر الجديد أو NUL.

4
Jeremy Friesner

صيغة تستخدم دالة المولد (التي أعتبرها أكثر بيثونيك):

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

تعديل كود آدم روزنفيلد:

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

ومع ذلك ، فإنني أشجع بشدة استخدام النهج الأصلي.

1
sjMoquin

تصف معظم الإجابات نوعًا من الأسلوب recvall(). إذا كان عنق الزجاجة عند تلقي البيانات ينشئ صفيف البايت في حلقة for ، فقد حددت ثلاثة طرق لتخصيص البيانات المستلمة في الأسلوب recvall():

طريقة سلسلة البايت:

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

طريقة القائمة:

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

الأسلوب المخصص مسبقًا bytearray:

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

النتائج:

enter image description here

0
Jacob Stern

لأي شخص آخر يبحث عن إجابة في الحالات التي لا تعرف فيها طول الحزمة السابقة. إليك حل بسيط يقرأ 4096 بايت في وقت واحد ويتوقف عند استلام أقل من 4096 بايت. ومع ذلك ، لن ينجح ذلك في الحالات التي يكون فيها الطول الإجمالي للحزمة المستلمة 4096 بايتًا بالضبط - ثم سوف يستدعي recv() مرة أخرى ويعلق.

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

يمكنك أن تفعل ذلك باستخدام التسلسل

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))

ملاحظة: إذا كنت ترغب في مشاركة ملف باستخدام الكود أعلاه ، فأنت بحاجة إلى تشفير/فك تشفيره إلى base64

0
John Albert