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

Стандартный метод отправки данных в потоковый сокет всегда состоял в том, чтобы вызвать send с порцией данных для записи, проверить возвращаемое значение, чтобы увидеть, были ли отправлены все данные, а затем продолжать вызывать 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 упоминается, что

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

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

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

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

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

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

Ответы [ 3 ]

20 голосов
/ 12 апреля 2010

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

1 голос
/ 12 апреля 2010

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

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

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

0 голосов
/ 12 апреля 2010

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

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

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

...