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