Подтвердить что ты не робот

Почему предполагается, что отправка может возвращаться с меньшими запрошенными данными, передаваемыми на блокирующий сокет?

Стандартный метод отправки данных в сокет потока всегда заключался в том, чтобы вызвать send с куском данных для записи, проверить возвращаемое значение, чтобы увидеть, были ли все данные отправлены, а затем продолжить вызов снова, пока не появится все сообщение принимаются.

Например, это простой пример общей схемы:

int send_all(int sock, unsigned char *buffer, int len) {
  int nsent;

  while(len > 0) {
    nsent = send(sock, buffer, len, 0);
    if(nsent == -1) // error
      return -1;

    buffer += nsent;
    len -= nsent;
  }
  return 0; // ok, all data sent
}

Даже в справочной системе BSD упоминается, что

... Если в сокете нет пространства сообщений для хранения передаваемого сообщения, отправьте() обычно блокировать...

Что означает, что мы должны предположить, что отправка может вернуться без отправки всех данных. Теперь я нахожу это довольно сломанным, но даже В. Ричард Стивенс предполагает это в своем стандартном справочнике о сетевом программировании, а не в первых главах, но более сложные примеры используют свою собственную функцию writeen (write all data) вместо вызова write.

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

Я имею в виду, что в примере кода выше, что произойдет, если send возвращается с меньшим количеством отправленных данных, так это то, что он будет снова вызван с новым запросом. Что изменилось после последнего звонка? При максимальном количестве нескольких сотен циклов процессора прошло так, что буфер по-прежнему заполнен. Если отправка теперь принимает данные, почему они не могли принять ее раньше?

В противном случае мы закончим upp с неэффективным циклом, в котором мы пытаемся отправить данные в сокет, который не может принимать данные и продолжать попытки, или же?

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

4b9b3361

Ответ 1

То, что отсутствует в описании выше, в Unix, системные вызовы могут прерваться сигналами. Именно поэтому блокировка send(2) может вернуть короткий счет.

Ответ 2

По существу, поведение просто добавляет гибкости.

Рассмотрим, что произойдет, если внутренние буферы отправки имеют место для еще 300 байтов, и вы просите send() отправить 800 байтов.

Что имеет место, так это то, что он принимает первые 300 байтов и помещает их в буфер сокета, а затем возвращается. Если вы действительно хотите заблокировать до тех пор, пока все 800 байтов не исчезнут, вы просто повторно вызовите send() для последних 500 байт, и он будет блокироваться (поскольку буфер отправки теперь заполнен). С другой стороны, если вы хотите сейчас сделать что-то еще и попытаться отправить оставшиеся 500 байт, вы тоже можете это сделать.

Ответ 3

Это довольно важное требование для эффективного транспортного механизма, который включает в себя драйверы устройств. Король кучи - TCP, он гарантирует, что независимо от того, что вы на него набросите, он сделает все возможное, чтобы получить данные по проводу. Он делает это довольно хорошо, у него есть гарантия на 9 девяток. Обычно для этого требуется землетрясение или кто-то, спотыкающийся о шнур питания, чтобы не выполнить это обещание.

Это означает, что вам не нужно беспокоиться о том, что при записи данных в сокет он не будет доставлен. Это означает, что драйвер ядра может позволить себе сказать: "У меня есть место, чтобы сохранить это, принесите его!" и дать программе режима пользователя быстрое возвращение к более важным вещам, например, обновление пользовательского интерфейса.

Хорошая вещь. Если по какой-то причине ваша гарантия доставки должна быть более сильной, может быть, 12 девяток, тогда вы не должны использовать TCP. Это было сделано, "надежный UDP" хорошо работает. Хотя он, как правило, снова изобретается снова и снова. Удачи!