API-функции клиента

Клиентская часть значительно проще и для установления соединения требуется всего три шага: создать сокет функцией socket или WSASocket; разрешить имя сервера (зависит от используемого протокола); инициировать соединение функцией connect или WSAConnect.

Из материалов второй главы известно, как создать сокет и разрешить имя IP-узла, так что единственным оставшимся шагом является установление соединения. Во второй главе также рассматривались способы разрешения имен и для других семейств протоколов.

^ Состояния TCP

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

Начальное состояние любого сокета — CLOSED. Как только клиент инициирует соединение, серверу отправляется пакет SYN и клиентский сокет переходит в состояние SYN_SENT. Получив пакет SYN, сервер отправляет пакет SYN-and-ACK, а клиент отвечает на него пакетом АСК. С этого момента клиентский сокет переходит в состояние ESTABLISHED. Если сервер не отправляет пакет SYN-ACK, клиент по истечении времени ожидания возвращается в состояние CLOSED.

Если сокет сервера связан и прослушивает локальный интерфейс и порт, то он находится в состоянии LISTEN. При попытке клиента установить соединение сервер получает пакет SYN и отвечает пакетом SYN-ACK. Состояние сокета сервера меняется на SYN_RCVD. Наконец, после отправки клиентом пакета АСК сокет сервера переводится в состояние ESTABLISHED.

Существует два способа закрыть соединение. Если этот процесс начинает приложение, то закрытие называется активным, иначе — пассивным. На рис. 3.2 изображены оба вида закрытия. При активном закрытии соединения приложение отправляет пакет FIN. Если приложение вызывает closesocket или shutdown (со вторым аргументом SD_SEND), оно отправляет узлу пакет FIN, и состояние сокета меняется на FIN_WAIT_1. Обычно узел отвечает пакетом АСК и сокет переходит в состояние FIN_WAIT_2. Если узел тоже закрывает соединение, он отправляет пакет FIN, а компьютер отвечает пакетом АСК и переводит сокет в состояние TIME_WAIT.

Состояние TIME_WAIT также называется состоянием ожидания 2*MSL MSL — максимальное время жизни сегмента (Maximum Segment Lifetime), иными словами, время существования пакета в сети перед его отбрасыванием. У каждого IP-пакета есть поле времени жизни (time-to-live, TTL). Если оно равно 0, значит, пакет можно отбросить. Каждый маршрутизатор, обслуживающий пакет, уменьшает значение TTL на 1 и передает пакет дальше. Перейдя в состояние TIME_WAIT, приложение остается в нем на протяжении двух периодов времени, равных MSL. Это позволяет TCP в случае потери заключительного пакета АСК послать его заново, с последующей отправкой FIN. По истечении 2*MSL сокет переходит в состояние CLOSED.

Результат двух других способов активного закрытия — состояние TIME_WAIT. В предыдущем случае только одна сторона отправляла FIN и получала ответ АСК, а узел оставался свободным для передачи данных до момента своего закрытия. Здесь возможны и два других способа. В первом случае, при одновременном закрытии, компьютер и узел одновременно запрашивают закрытие: компьютер отправляет узлу пакет FIN и получает от него пакет FIN.

Затем в ответ на пакет FIN компьютер отправляет пакет АСК и изменяет состояние сокета на CLOSING. После получения компьютером пакета АСК от узла сокет переходит в состояние TIME_WAIT.

Второй случай активного закрытия является вариацией одновременного закрытия: сокет из состояния FIN_WAIT_1 сразу переходит в состояние TIME_WAIT. Это происходит, если приложение отправляет пакет FIN и тут же после этого получает от узла пакет FIN-ACK. В таком случае узел подтверждает пакет FIN приложения отправкой своего, на которое приложение отвечает пакетом АСК.

Основной смысл состояния TIME_WAIT заключается в том, что пока соединение ожидает истечения 2*MSL, сокетная пара, участвующая в соединении, не может быть использована повторно. Сокетная пара — это комбинация локального и удаленного IP-портов. Некоторые реализации TCP не позволяют повторно использовать любой из портов сокетной пары, находящейся в состоянии TIME_WAIT. В реализации Microsoft этого дефекта нет. При попытке соединения с сокетной парой, находящейся в состоянии TIME_WAIT, произойдет ошибка WSAEADDRINUSE. Одно из решений проблемы (кроме ожидания окончания состояния TIME_WAIT пары сокетов, использующей локальный порт) состоит в использовании параметра сокета SO_REUSEADDR. Более подробно SO_REUSEADDR рассматривается в последующих главах.

Наконец, целесообразно рассмотреть пассивное закрытие. По этому сценарию приложение получает от узла пакет FIN и отвечает пакетом АСК. В этом случае сокет приложения переходит в состояние CLOSE_WAIT. Так как узел закрыл свою сторону, он больше не может отправлять данные, но приложение вправе это делать, пока не закроет свою сторону соединения. Для закрытия своей стороны приложение отправляет пакет FIN, после чего ТСР-сокет приложения переводится в состояние LAST_ACK. После получения от узла пакета АСК сокет приложения возвращается в состояние CLOSED.

^ Функции connect и WSAConnect

Осталось обсудить собственно установление соединения. Оно осуществляется вызовом connect или WSAConnect. Сначала можно рассмотреть версию Winsock 1 этой функции:

// Code 3.11

int connect(

SOCKET s,

const struct sockaddr FAR * name,

int namelen

);

Параметры практически не требуют пояснений: s — действительный ТСР-сокет для установления соединения, name — структура адреса сокета (SOCKADDR_IN) для TCP, описывающая сервер к которому подключаются, namelen — длина переменной пате. Версия Winsock 2 этой функции определена так:

// Code 3.12

int WSAConnect(

SOCKET s,

const struct sockaddr FAR * name,

int namelen,

LPWSABUF lpCallerData

LPWSABUF lpCalleeData,

LPQOS lpSQOS,

LPQOS lpGQOS

);

Первые три параметра такие же, как и в функции connect. Следующие два: lpCallerData и lpCalleeData, — это строковые буферы, используемые для приема и отправки данных в момент установления соединения. Параметр lpCallerData указывает на буфер, содержащий данные, отправляемые клиентом серверу вместе с запросом на соединение; lpCallerData — на буфер с данными, возвращаемыми сервером в ходе установления соединения. Обе переменные являются структурами WSABUF, и для lpCallerData поле len должно указывать длину данных передаваемого буфера buf. В случае lpCalleeData поле len определяет размер буфера buf, куда принимаются данные от сервера. Два последних параметра: lpSQOS и lpGQOS, — ссылаются на структуры QoS, определяющие требования пропускной способности отправки и приема данных устанавливаемого соединения. Параметр lpSQOS указывает требования к сокету s, a lpGQOS — к группе сокетов. На данный момент группы сокетов в полной мере не поддерживаются. Нулевое значение lpSQOS означает, что приложение не предъявляет требований к качеству обслуживания.

Если на компьютере, к которому выполнено подключение, не запущен процесс, прослушивающий данный порт, функция connect вернет ошибку WSAECONNREFUSED. Другая ошибка — WSAETIMEDOUT, происходит, когда вызываемый адресат недоступен, например, из-за отказа коммуникационного оборудования на пути к узлу или отсутствия узла в сети.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: