Каковы наилучшие методы отправки (или записи) и получения (или чтения) в/из сокета TCP?
Предположим, обычный блокирующий ввод-вывод на сокетах. Из того, что я понимаю:
if ((nbytes_w = write(sock, buf, nb)) < nb)
/* something bad happened : error or interrupted by signal */
всегда должно быть правильно?while ((nbytes_r = read(sock, buf, MAX)) > 0) {
/* do something with these bytes */
/* break if encounter specific application protocol end of message flag
or total number of bytes was known from previous message
and/or application protocol header */
}
Я прав ? Или есть какой-то «маленький размер сообщения» или другие условия, позволяющие безопасно читать вне цикла?
Я в замешательстве, потому что я видел примеры «голого чтения», например, в Tanenbaum-Wetherall:
read(sa, buf, BUF_SIZE); /* read file name in socket */
Проверьте этот ответ, чтобы отправить/написать: stackoverflow.com/a/14399717/1076479. Подводя итог, можно сказать, что большинство/все реализации блокируют будем при отправке, но ни одна из спецификаций реализаций на самом деле не блокирует гарантия. Поскольку не будет ошибкой, если реализация вернет короткую запись, лучше всего предположить, что это может произойти. А так как реализовать цикл несложно, то стоит просто сделать. Это даст вам максимальную переносимость в отношении как платформ, так и транспортных механизмов.
И, кстати, в вашем заявлении write выше отсутствуют скобки, необходимые для выполнения того, что вы намереваетесь. Так должно быть if ((nbytes_w = write(sock, buf, nb)) < nb)
@GilHamilton Спасибо за опечатку, она отредактирована. Спасибо. Что касается записи в циклах, я думаю, вы правы, но я все еще не понимаю. Зачем вообще внедрять блокировку записи, если вы не можете на нее рассчитывать?
Блокирующая запись по существу гарантирует, что в системном вызове произойдет прогресс некоторый (или будет возвращена ошибка). Альтернатива неблокирующей записи привела бы к тому, что ваша программа (потенциально) вращалась бы, тратя впустую циклы ЦП и ничего не выполняя, пока не будет достигнут прогресс. Следовательно, это делает парадигму программирования намного проще: нет необходимости использовать select или другие сложные механизмы для реализации простого последовательного процесса.
Разработчику приложений было бы бы проще, если бы ОС гарантировала, что весь send будет завершен до возврата. И, на практике, вы, вероятно, можете рассчитывать на это. Я думаю, что это не гарантировано, потому что первоначальные разработчики не хотели ограничивать будущие реализации. (Кроме того, IMO это согласуется с общей философией ОС: рассмотрите попытку записи блока 16G в файл на диске, но диск был заполнен во время записи. Естественный способ справиться с этим - вернуть успешно записанные байты. Затем вызывающий может повторить попытку напишите с оставшимися байтами, чтобы обнаружить ошибку.)





Да, вы должны зацикливаться на приеме
Раз в неделю я отвечаю на вопрос, где именно по этой причине перестает работать чье-то приложение TCP. Настоящим убийцей является то, что они разработали клиент и сервер на одной машине, поэтому они получают петлевое соединение. Почти все время петля будет получать сообщения отправки в тех же блоках, в которых они были отправлены. Это делает код правильным.
Действительно большая проблема заключается в том, что это означает, что вам нужно знать перед циклом, насколько велико сообщение, которое вы собираетесь получить. Возможности
У меня всегда была бы функция «вытащить следующие n байтов».
Написание тоже должно зацикливаться, но это просто, это просто вопрос зацикливания.
Вы также должны зациклить посыл. send() может принимать меньше байтов, чем запрошено, точно так же, как recv() может возвращать меньше байтов, чем запрошено. Обе функции возвращают фактическое количество принятых/полученных байтов. Обратите внимание на это значение.
«двойной crlf в конце HTTP-запроса» — HTTP-сообщения не заканчиваются двойным crlf. Только ЗАГОЛОВКИ есть, а ТЕЛА нет.
@pm100 Большое спасибо. Не могли бы вы дать ссылки (RFC или справочные страницы) о написании необходимых циклов? Мне не имеет смысла реализовывать блокировку записи, если нет гарантии, что все будет записано. Это нормально в системах Unix?
@Villetaneuse man7.org/linux/man-pages/man2/write.2.html — прочитать комментарии «возвращаемое значение»
@pm100 Спасибо. Но тогда примеры незавершенной записи на этой справочной странице очень специфичны. При записи в сокет это может происходить только при прерывании сигналом? Извините, если это выглядит педантично, но я хотел бы добраться до сути.
@Villetaneuse проверьте read_n и write_n здесь github.com/fpagliughi/sockpp/blob/master/src/stream_socket.cpp
Для достаточно малых буферов люди склонны предполагать, что шансы на получение пакета в нескольких фрагментах слишком малы, чтобы о них беспокоиться. Не будьте одним из них. ;)