Передача файлов Python (tcp socket), проблема с медленной сетью - PullRequest
0 голосов
/ 29 ноября 2018

Я установил защищенный сокет, используя Tor и носки, но у меня проблема с отправкой большого количества данных

Отправитель:

socket.send(message.encode())

Приемник:

chunks = []

while 1:
    part = connection.recv(4096)
    chunks.append(part.decode())

    if len(part) < 4096:
        break

response = "".join(chunks)

Так как скорость сети не постоянна в цикле, я не всегда заполняю буфер 4096b, поэтому разрывы цикла и я не получаю полные данные.

Уменьшение размера буфера не представляется возможным, поскольку иногда размер "пакета" может составлять всего 20b

Ответы [ 3 ]

0 голосов
/ 29 ноября 2018

Попробуйте использовать структуру, чтобы сначала передать длину входящих данных получателю, "import struct".Таким образом, принимающая сторона точно знает, сколько данных получить.В этом примере байты отправляются через сокет, примеры, которые я заимствовал из моего файла github для загрузки github.com/nsk89/netcrypt для справки, и вырезали шаги шифрования из функции send, а также для отправки сериализованного словаря.

Редактировать Я также должен уточнить, что при отправке данных через сокет, особенно если вы отправляете несколько сообщений, все они помещаются в поток как одно длинное сообщение.Не каждое сообщение имеет длину 4096 байт.Если один из них имеет длину 2048, а следующие 4096 и вы получаете 4096 на свои буферы, вы получите первое сообщение плюс половина следующего сообщения или полностью зависаете, ожидая больше данных, которых не существует.

data_to_send = struct.pack('>I', len(data_to_send)) + data_to_send # pack the length of data in the first four bytes of data stream, >I indicates internet byte order

    socket_object.sendall(data_to_send)  # transport data



def recv_message(socket_object):
    raw_msg_length = recv_all(socket_object, 4)  # receive first 4 bytes of data in stream
    if not raw_msg_length:
        return None

    # unpack first 4 bytes using network byte order to retrieve incoming message length
    msg_length = struct.unpack('>I', raw_msg_length)[0]

    return recv_all(socket_object, msg_length)  # recv rest of stream up to message length

def recv_all(socket_object, num_bytes):
    data = b''
    while len(data) < num_bytes:  # while amount of data recv is less than message length passed
        packet = socket_object.recv(num_bytes - len(data))  # recv remaining bytes/message
        if not packet:
            return None
        data += packet
    return data
0 голосов
/ 30 ноября 2018

Кстати, не нужно декодировать каждую часть, прежде чем объединить их в блок, объединить все части в блок и затем декодировать блок.

В вашей ситуации лучше использовать 2шаги.

Шаг 1: отправитель отправляет размер сообщения, получатель принимает этот размер и готов принять сообщение.

Шаг 2: отправитель отправляет сообщение, получатель объединяет данные при необходимости.

Отправитель

# Step 1
socket.send( str(len(message.encode())).encode() ) 
# Step 2
socket.send(message.encode("utf-8"))   

Получатель

# Step 1
message_size = connection.recv(1024) 
print("Will receive message size:",message_size.decode())

# Step 2
recevied_size  = 0 
recevied_data = b''
  while recevied_size < int(message_size.decode()):
      part = connection.recv(1024)
      recevied_size += len(part)
      recevied_data += part
  else:
      print(recevied_data.decode("utf-8", "ignore"))
      print("message receive done ....",recevied_size)
0 голосов
/ 29 ноября 2018

TCP может разделить данные вашего пакета на любое количество частей.Поэтому никогда не следует полагаться на другой конец сокета на размер полученного пакета.Вы должны изобрести другой механизм для определения конца сообщения / конца файла.

Если вы собираетесь отправлять только один большой двоичный объект и закрывать сокет, то на стороне сервера вы просто читаете, пока не получите значение False:

while True:
    data = sock.recv(1024)
    if data:
        print(data)
        # continue 
    else:
        sock.close()
        break

Если вы собираетесь отправлять несколько сообщений, вам нужно решить, каким будет разделитель между ними.Для текстовых протоколов рекомендуется использовать lineending.После этого вы сможете пользоваться возможностями протокола Twisted LineReceiver и других.

Если вы используете бинарный протокол, обычной практикой является введение в каждое ваше сообщение байта размера / слова / слова.

...