Здесь возникает вопрос: нельзя ли с помощью sendto
передавать данные через тот же сокет, который в другой нити используется в функции recvfrom
? Документация по этому поводу упорно молчит. Если в нашем чате оставить только один сокет и задействовать его в обеих нитях, то всё вроде как работает. Однако это тот случай, когда эксперимент не может служить доказательством, потому что у ошибок, связанных с неправильной синхронизацией нитей, есть очень неприятная особенность: программа может миллион раз отработать правильно, а на миллион первый дать сбой. Поэтому сколько бы раз такой эксперимент ни завершился удачно, полной гарантии он все же не даёт, так что приходится действовать осторожно и не использовать один сокет в разных нитях.
В заключение отметим, что наш чат допускает одновременное общение любого количества человек с любого числа адресов, но сообщения всегда передаются от одного человека к другому. Широковещательных и групповых сообщений у нас нет. Отметим также, что отправлять сообщения можно и не запуская "сервер".
Для того чтобы протестировать работу чата, не обязательно иметь два компьютера, соединенных в сеть. Два или более экземпляра чата можно запустить и на одном компьютере, главное, чтобы у них у всех были разные порты для принимающего сокета. В качестве IP-адреса для отправки сообщений можно задавать адрес локального компьютера вида 127.0.0.N. Это же верно и для всех остальных примеров работы с сокетами.
2.1.11. Передача данных при использовании TCP
При программировании TCP и UDP применяются одни и те же функции, но их поведение при этом различно. Для передачи данных с помощью TCP необходимо сначала установить соединение, и после этого возможен обмен данными только с тем адресом, с которым это соединение установлено. Функция sendto
может использоваться для TCP-сокетов, но ее параметры, задающие адрес получателя, игнорируются, а данные отправляются на тот адрес, с которым соединен сокет. Поэтому при отправке данных через TCP обычно прибегают к функции send
, которая дает тот же результат. По тем же причинам обычно используется recv
, а не recvfrom
.
В TCP существует разделение ролей взаимодействующих сторон на клиент и сервер. Мы начнем изучение передачи данных в TCP с изучения действий клиента.
Для начала взаимодействия клиент должен соединиться с сервером с помощью функции connect
. Мы уже знакомы с этой функцией, но в случае TCP она выполняет несколько иные действия. В данном случае она устанавливает реальное соединение, поэтому ее действия начинаются с проверки того, существует ли по указанному адресу серверный сокет, находящийся в режиме ожидания подключения. Функция connect
завершается успешно только тогда, когда соединение установлено, и серверная сторона выполнила все необходимые для этого действия. При вызове connect
в TCP предварительный явный вызов функции bind
также не обязателен.
В отличие от UDP, сокет в TCP нельзя отсоединить или соединить с другим адресом, если он уже соединен. Для нового соединения необходим новый сокет.
Мы уже говорили, что TCP является надежным протоколом, т.е. в том случае, если пакет не доставлен, отправляющая сторона уведомляется об этом.
Тем не менее успешное завершение send
, как и в случае UDP, не является гарантией того, что пакет был отослан и дошел до получателя, а говорит только о том, что данные скопированы в выходной буфер сокета, и на момент копирования сокет был соединён. Если в дальнейшем библиотека сокетов не сможет отправить эти данные или не получит подтверждения об их доставке, соединение будет закрыто, и следующая операция с этим сокетом завершится с ошибкой.
Если выходной буфер сокета равен нулю, данные сразу копируются в сеть, но успешное завершение функции и в этом случае не гарантирует успешную доставку. Использовать нулевой выходной буфер для TCP-сокетов не рекомендуется, т.к. это снижает производительность при последовательной отправке данных небольшими порциями. При буферизации эти порции накапливаются в буфере, а потом отправляются одним большим пакетом, требующим одного подтверждения от клиента. Если же буферизация не осуществляется, то будет отправлено несколько мелких пакетов, каждый со своим заголовком и своим подтверждением от клиента, что приведет к снижению производительности.