Как разрезать байты на отдельные части из сокета - PullRequest
1 голос
/ 03 августа 2020

Хорошо, поэтому заголовок МОЖЕТ быть сформулирован лучше, но вот в чем проблема

Я ДЕЙСТВИТЕЛЬНО отправляю данные в сокет, и мне нужно, чтобы каждая отправляемая информация была отдельной

Например:

import pickle
#Sending side
while True:
     data = pickle.dumps((listelement1, listelement2, listelement3)) #All list elements may change value
     conn.send(data)


import pickle
#Revieving side
while True:
     dataget = conn.recv(1024)
     data_arr = pickle.loads(dataget)
     print(data_arr)

Это, конечно, не настоящий код, а мой лучший его пример. Я пытаюсь убедиться, что «dataget» - это ВСЕГДА целые «данные», без случайного усечения или обрезания передней части данных, как мне это сделать?

1 Ответ

2 голосов
/ 03 августа 2020

Основная проблема c в том, что вам нужен способ разметки сообщений в потоке байтов TCP. Это быстро усложняется. Маринованные потоки знают свой размер, и если все, что вы планируете делать, это передавать маринованные продукты туда и обратно, вы можете использовать это знание. Предполагая, что вы придерживаетесь блокировки ввода-вывода, на стороне отправки это легко, просто выберите и отправьте, но используйте s.sendall(), чтобы убедиться, что это действительно работает. send() может отправить меньше, чем вы просите, и вы должны проверить его возврат и продолжить отправку. sendall сделает это за вас.

Принимающая сторона более сложна. pickle.loads требует, чтобы вы сначала загрузили байтовую строку (и как узнать, сколько она длится?), И не сообщает вам, сколько потока она потребляет. pickle.Unpickler делает то, что вы хотите, но использует файловый объект, а не сокет. Если вы продолжаете блокировать сокеты, вы можете использовать socket.makefile() для создания файлового объекта для сокета и использовать его с unpickler.

Вот рабочий пример

import sys
import socket
import pickle

PORT = 8722

def client():
    s = socket.socket()
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.connect(('localhost', PORT))
    f = s.makefile(mode='wb')
    pickle.dump(["list 1", 1,2,3], f)
    pickle.dump(["list 2", 4, 5, 6], f)
    pickle.dump(["list 3", 7, 8, 9], f)
    f.close()
    s.close()

def server():
    s = socket.socket()
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(('localhost', PORT))
    s.listen(1)
    conn, addr = s.accept()
    f = conn.makefile(mode='rb')
    unpkl = pickle.Unpickler(f)
    try:
        while True:
            obj = unpkl.load()
            print(obj)
    except EOFError:
        print('done')

try:
    if sys.argv[1] == "client":
        client()
    elif sys.argv[1] == "server":
        server()
    else:
        raise IndexError()
except IndexError:
    print("test.py client|server")
    exit(1)

Альтернативно, вы можете реализовать какой-нибудь метод разграничения каждого буфера рассола. Возможно, небольшой заголовок, записанный через struct, содержит размер. Затем это двухэтапный процесс: прочитайте заголовок, затем используйте этот размер для чтения данных. Вы даже можете активизировать его и использовать другой метод, например HTTP rp c или zeromq, et c ...

...