Сокет Python получает - входящие пакеты всегда имеют другой размер

DIR="/some/dir"
if [ "$(ls -A $DIR)" ]; then
     echo 'There is something alive in here'
fi
37
задан Paul Rooney 21 January 2016 в 23:43
поделиться

4 ответа

Сеть всегда непредсказуема. 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
42
ответ дан 27 November 2019 в 03:58
поделиться

Такова природа TCP: протокол заполняет пакеты (нижний уровень - это IP-пакеты) и отправляет их. Вы можете иметь некоторую степень контроля над MTU (Maximum Transfer Unit).

Другими словами: вы должны разработать протокол, который работает поверх TCP, в котором определено ваше «разграничение полезной нагрузки». Под «разграничением полезной нагрузки» я подразумеваю способ извлечения единицы сообщения, поддерживаемой вашим протоколом. Это может быть так же просто, как «каждая строка с завершающим NULL».

2
ответ дан 27 November 2019 в 03:58
поделиться

В ответе Ларри Гастингса есть несколько отличных общих советов по поводу сокетов, но есть пара ошибок, связанных с тем, как метод recv (bufsize) работает в Модуль сокета Python.

Итак, чтобы уточнить, поскольку это может сбивать с толку других, которые обращаются к нему за помощью:

  1. Параметр bufsize для метода recv (bufsize) не является необязательным. Вы получите сообщение об ошибке, если вызовете recv () (без параметра).
  2. Размер буфера в recv (bufsize) имеет максимальный размер . . Recv с радостью вернет меньше байтов, если их будет меньше.

См. документацию для подробностей.

Теперь, если вы получаете данные от клиента и хотите знать, когда вы получил все данные, вы ' re, вероятно, придется добавить его в свой протокол - как предлагает Ларри. См. этот рецепт , чтобы узнать о стратегиях определения конца сообщения.

Как указано в этом рецепте, для некоторых протоколов клиент просто отключится, когда закончит отправку данных. В таких случаях ваш цикл while True должен работать нормально. Если клиент , а не отключается, вам нужно придумать способ обозначить длину вашего контента, разграничить сообщения или реализовать тайм-аут.

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

клиент просто отключится, когда закончит отправку данных. В таких случаях ваш цикл while True должен работать нормально. Если клиент , а не отключается, вам нужно придумать какой-нибудь способ сигнализировать о длине вашего контента, разграничить сообщения или реализовать тайм-аут.

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

клиент просто отключится, когда закончит отправку данных. В таких случаях ваш цикл while True должен работать нормально. Если клиент , а не отключается, вам нужно придумать какой-нибудь способ сигнализировать о длине вашего контента, разграничить сообщения или реализовать тайм-аут.

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

128
ответ дан 27 November 2019 в 03:58
поделиться

В качестве альтернативы вы можете использовать recv (x_bytes, socket.MSG_WAITALL) , который, кажется, работает только в Unix и вернет точно x_bytes .

17
ответ дан 27 November 2019 в 03:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: