4.3. Функция connect
4.3. Функция connect
Функция connect используется клиентом TCP для установления соединения с сервером TCP.
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *servaddr,
socklen_t addrlen);
Возвращает: 0 в случае успешного выполнения функции, -1 в случае ошибки
Аргумент sockfd — это дескриптор сокета, возвращенный функцией socket. Второй и третий аргументы — это указатель на структуру адреса сокета и ее размер (см. раздел 3.3). Структура адреса сокета должна содержать IP-адрес и номер порта сервера. Пример применения этой функции был представлен в листинге 1.1.
Клиенту нет необходимости вызывать функцию bind (которую мы описываем в следующем разделе) до вызова функции connect: при необходимости ядро само выберет и динамически назначаемый порт, и IP-адрес отправителя.
В случае сокета TCP функция connect инициирует трехэтапное рукопожатие TCP (см. раздел 2.6). Функция возвращает значение, только если установлено соединение или произошла ошибка. Возможно несколько ошибок:
1. Если клиент TCP не получает ответа на свой сегмент SYN, возвращается сообщение ETIMEDOUT. 4.4BSD, например, отправляет один сегмент SYN, когда вызывается функция connect, второй — 6 с спустя, и еще один — через 24 с [128, с. 828]. Если ответ не получен в течение 75 с, возвращается ошибка.
Некоторые системы позволяют администратору устанавливать значение времени ожидания; см. приложение Е [111].
2. Если на сегмент SYN сервер отвечает сегментом RST, это означает, что ни один процесс на узле сервера не находится в ожидании подключения к указанному нами порту (например, нужный процесс может быть не запущен). Это устойчивая неисправность (hard error), и клиенту возвращается сообщение ECONNREFUSED сразу же по получении им сегмента RST.
RST (от «reset» — сброс) — это сегмент TCP, отправляемый собеседнику при возникновении ошибок. Вот три условия, при которых генерируется RST: сегмент SYN приходит для порта, не имеющего прослушивающего сервера (что мы только что описали); TCP хочет разорвать существующее соединение; TCP получает сегмент для несуществующего соединения (дополнительная информация содержится на с. 246–250 [111]).
3. Если сегмент SYN клиента приводит к получению сообщения ICMP о недоступности получателя от какого-либо промежуточного маршрутизатора, это считается случайным сбоем (soft error). Клиентское ядро сохраняет сообщение об ошибке, но продолжает отправлять сегменты SYN с теми же временными интервалами, что и в первом сценарии. Если же но истечении определенного фиксированного времени (75 с для 4.4BSD) ответ не получен, сохраненная ошибка ICMP возвращается процессу либо как EHOSTUNREACH, либо как ENETUNREACH. Может случиться, что удаленная система будет недоступна по любому маршруту из таблицы маршрутизации локального узла, или что возврат из connect произойдет без всякого ожидания.
ПРИМЕЧАНИЕ
Многие более ранние системы, такие как 4.2BSD, некорректно прерывали попытки установления соединения при получении сообщения ICMP о недоступности получателя. Это было неверно, поскольку данная ошибка ICMP может указывать на временную неисправность. Например, может быть так, что эта ошибка вызвана проблемой маршрутизации, которая исправляется в течение 15 с.
Обратите внимание, что мы не включили ENETUNREACH в табл. А.5 несмотря на то, что сеть получателя действительно может быть недоступна. Недоступность сети считается устаревшей ошибкой, и даже если 4.4BSD получает такое сообщение, приложению возвращается EHOSTUNREACH.
Эти ошибки мы можем наблюдать на примере нашего простого клиента, созданного в листинге 1.1. Сначала мы указываем адрес нашего собственного узла (127.0.0.1), на котором работает сервер времени и даты, и видим обычный вывод:
solaris % daytimetcpcli 127.0.0.1
Sun Jul 27 22:01:51 2003
Укажем IP-адрес другого компьютера (HP-UX):
solaris % daytimecpcli 192.6.38.100
Sun Jul 27 22:04:59 PDT 2003
Затем мы задаем IP-адрес в локальной подсети (192.168.1/24) с несуществующим адресом узла (100). Когда клиент посылает запросы ARP (запрашивая аппаратный адрес узла), он не получает никакого ответа:
solaris % daytimetcpcli 192.168.1.100
connect error: Connection timed out
Мы получаем сообщение об ошибке только по истечении времени выполнения функции connect (которое, как мы говорили, для Solaris 9 составляет 3 мин). Обратите внимание, что наша функция err_sys выдает текстовое сообщение, соответствующее коду ошибки ETIMEDOUT.
В следующем примере мы пытаемся обратиться к локальному маршрутизатору, на котором не запущен сервер времени и даты:
solaris % daytimetcpcli 192.168.1.5
connect error: Connection refused
Сервер отвечает немедленно, отправляя сегмент RST.
В последнем примере мы пытаемся обратиться к недоступному адресу из сети Интернет. Просмотрев пакеты с помощью программы tcpdump, мы увидим, что маршрутизатор, находящийся на расстоянии шести прыжков от нас, возвращает сообщение ICMP о недоступности узла:
solaris % daytimetcpcli 192.3.4.5
connect error: No route to host
Как и в случае ошибки ETIMEDOUT, в этом примере функция connect возвращает ошибку EHOSTUNREACH только после ожидания в течение определенного времени.
В терминах диаграммы перехода состояний TCP (см. рис. 2.4) функция connect переходит из состояния CLOSED (состояния, в котором сокет начинает работать при создании с помощью функции socket) в состояние SYN_SENT, а затем, при успешном выполнении, в состояние ESTABLISHED. Если выполнение функции connect окажется неудачным, сокет больше не используется и должен быть закрыт. Мы не можем снова вызвать функцию connect для сокета. В листинге 11.4 вы увидите, что если функция connect выполняется в цикле, проверяя каждый IP-адрес данного узла, пока он не заработает, то каждый раз, когда выполнение функции оказывается неудачным, мы должны закрыть дескриптор сокета с помощью функции close и снова вызвать функцию socket.
Данный текст является ознакомительным фрагментом.