Отправка длинных utf8 сообщений по TCP

пишу клиент-сервер на python и написал небольшой протокол поверх TCP что бы было удобнее отправлять сообщения

from json import dumps

def receive(socket):
    data = socket.recv(1024).decode('utf8')
    length = int(data[:16])
    data = data[16:]
    while length - 1:
        data += socket.recv(1024).decode('utf8')
        length -= 1
    return data


def send(socket, data):
    if type(data) == dict:
        data = chunk_string(dumps(data), 1000 - 16)
    else:
        data = chunk_string(str(data), 1000 - 16)
    length = len(data)
    data[0] = str(length).zfill(16) + data[0]
    for chunk in data:
        socket.send(bytes(chunk, encoding='utf8'))


def chunk_string(string, chunk_size):
    return [string[i:i + chunk_size] for i in range(0, len(string), chunk_size)]

При отправлении данные нарезаются на чанки по 1000 симоволов и в первые 16 символов добаляется количество чанков.

Работает всё, в принипе, нормально, но есть одна проблема:

Мультибайтовые символы юникода. Иногда из-за них в receive вылетает UnicodeDecodeError: 'utf-8' codec can't decode bytes in position 1022-1023: unexpected end of data (Позиция байтов не именно 1022-1023, раньше размер чанков был не 1000)

Как избежать этой проблемы? Пока обхожусь пересылкой обычный символов, избегая мультибайтовых, но, конечно, хотелось бы иметь возможность и их пересылать


Ответы (1 шт):

Автор решения: Alex F

TCP это stream. На уровне socket понятие пакета в нем не определено. Это значит, что если сокет А выполняет две операции send:

send('abc')
send('123')

то сокет В может принять эти данные одной или несколькими операциями receive, например: abc123 или a,bc123 или abc, 123 - любые сочетания возможны. Принимающая сторона должна накапливать данные в буфере и делать parsing только тогда, когда он возможен, то есть вся информация получена.

Каким образом это делать - для этого нужно разработать протокол, надстройку над TCP. Самый распространенный (но не единственный) способ - это посылать в начале каждого логического пакета его длину. Накапливайте данные в буфере (возможно, от одного или более receive) и делайте parsing только при получении полного пакета.

→ Ссылка