Сеть всегда непредсказуема. 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
Hans L в комментарии ниже, что в python request.recv() не является допустимым вызовом как bufsize, если обязательный параметр. В идеале этот ответ следует удалить или отредактировать. http://docs.python.org/library/socket.html – prashantsunkari
«Это называется« блокирование ввода-вывода »:« Мне нравится разбивка этого на непрофессиональных условиях .... – repzero