Подтвердить что ты не робот

Сокет Python получает большое количество данных

Когда я пытаюсь получить большее количество данных, он отключается, и я должен нажать enter, чтобы получить остальную информацию. Сначала мне удалось немного увеличить его, но он все равно не получит его. Как вы видите, я увеличил буфер на conn.recv(), но он все равно не получает все данные. В какой-то момент это сокращает его. Я должен нажать enter на моем raw_input, чтобы получить остальную часть данных. В любом случае, я могу получить все данные сразу? Вот код.

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()
4b9b3361

Ответ 1

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

Затем вы можете использовать функции send_msg и recv_msg для отправки и получения целых сообщений, и у них не возникнет проблем с разделением или объединением пакетов на уровне сети.

Ответ 2

Вы можете использовать его как: 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

Ответ 3

Принятый ответ хорош, но он будет очень медленным с большими файлами -string - неизменный класс, это означает, что каждый раз, когда вы используете знак +, создается больше объектов, использование list в качестве структуры стека будет более эффективным.

Это должно работать лучше

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

print "".join(fragments)

Ответ 4

Вам может потребоваться несколько раз вызвать conn.recv() для получения всех данных. При вызове его один раз не гарантируется получение всех отправленных данных из-за того, что потоки TCP не поддерживают границы кадров (т.е. Они работают только как поток необработанных байтов, а не структурированный поток сообщений).

См. этот ответ для другого описания проблемы.

Обратите внимание, что это означает, что вам нужен какой-то способ узнать, когда вы получили все данные. Если отправитель всегда будет отправлять ровно 8000 байт, вы можете подсчитать количество полученных байтов до сих пор и вычесть из 8000, чтобы узнать, сколько осталось получить; если данные имеют переменный размер, существуют различные другие методы, которые могут использоваться, например, когда отправитель отправляет заголовок с номерами байтов перед отправкой сообщения или если он отправляет текст ASCII, вы можете искать символ новой строки или NUL.

Ответ 5

Вариант с использованием функции-генератора (который я считаю более питоническим):

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

Ответ 6

Большинство ответов описывают какой-то метод 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

Ответ 7

Изменение кода Адама Розенфилда:

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

Однако я бы очень хотел использовать оригинальный подход.

Ответ 8

Вы можете сделать это с помощью сериализации

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.

Ответ 9

Для тех, кто ищет ответ в тех случаях, когда вы не знаете длину пакета ранее. Здесь простое решение, которое считывает 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