Сеть всегда непредсказуема. TCP избавляет вас от этого случайного поведения. TCP делает одну замечательную вещь: он гарантирует, что байты будут поступать в том же порядке. Но! Это не гарантирует, что они прибудут рублеными таким же образом. Вы просто не можете предположить, что каждый send () с одного конца соединения приведет к точно одному recv () на дальнем конце с точно таким же количеством байтов.
Когда вы говорите socket.recv(x)
, вы говорите «не возвращайтесь, пока не прочитаете x байтов из сокета». Это называется «блокировка ввода / вывода»: вы будете блокировать (ждать), пока ваш запрос не будет выполнен. Если бы каждое сообщение в вашем протоколе было ровно 1024 байта, вызов socket.recv(1024)
работал бы отлично Но, похоже, это не так. Если ваши сообщения имеют фиксированное число байтов, просто передайте это число в socket.recv()
и все готово.
Но что, если ваши сообщения могут быть разной длины? Первое, что вам нужно сделать: прекратить звонить socket.recv()
с явным номером. Изменение этого:
data = self.request.recv(1024)
к этому:
data = self.request.recv()
означает, что recv()
всегда будет возвращаться при получении новых данных.
Но теперь у вас возникла новая проблема: откуда вы знаете, когда отправитель отправил вам полное сообщение? Ответ таков: нет. Вы должны будете сделать длину сообщения явной частью вашего протокола. Вот лучший способ: префикс каждого сообщения с длиной, либо в виде целого числа фиксированного размера (преобразуется в сетевой порядок байтов с использованием socket.ntohs()
или socket.ntohl()
, пожалуйста!), Либо в виде строки, за которой следует некоторый разделитель (например, «123:»). ). Этот второй подход часто менее эффективен, но он проще в Python.
После того, как вы добавили это в свой протокол, вам нужно изменить код для обработки recv()
, возвращающей произвольные объемы данных в любое время. Вот пример того, как это сделать. Я пытался написать это в виде псевдокода или с комментариями, чтобы сказать вам, что делать, но это было не очень понятно. Поэтому я написал это явно, используя префикс длины в виде строки цифр, оканчивающихся двоеточием. Вот, пожалуйста:
length = None
buffer = ""
while True:
data += self.request.recv()
if not data:
break
buffer += data
while True:
if length is None:
if ':' not in buffer:
break
# remove the length bytes from the front of buffer
# leave any remaining bytes in the buffer!
length_str, ignored, buffer = buffer.partition(':')
length = int(length_str)
if len(buffer) < length:
break
# split off the full message from the remaining bytes
# leave any remaining bytes in the buffer!
message = buffer[:length]
buffer = buffer[length:]
length = None
# PROCESS MESSAGE HERE