содержит адрес интерфейса, принявшего соединение (поля структуры при этом используются так же, как у параметра
lpCallerId
). Ранее уже обсуждалось, что сокет, привязанный к адресу
INADDR_ANY
, прослушивает все сетевые интерфейсы, имеющиеся на компьютере, но каждое подключение, созданное с его помощью, использует конкретный интерфейс. Параметр
lpCalleeId
содержит адрес, привязанный к конкретному соединению. Параметр
lpCalleeData
указывает на буфер, в который сервер может поместить данные для отправки клиенту. Этот параметр также не имеет смысла для протокола TCP, не поддерживающего отправку данных при соединении.
Параметр
g
выходной, он позволяет управлять присоединением создаваемого функцией
WSAAccept
сокета к группе. Параметр, как и все, связанное с группами, зарезервирован для использования в будущем.
Примечание
Если вы пользуетесь старой версией MSDN, то можете не обнаружить там описания параметра
g
— оно там отсутствует. Видимо, просто по ошибке.
И наконец, через параметр
dwCallbackData
в функцию обратного вызова передается значение параметра
dwCallbackData
, переданное в функцию
WSAAccept
. Программист должен сам решить, как ему интерпретировать это значение.
Функция должна вернуть
CF_ACCEPT
(0), если соединение принимается,
CF_REJECT
(1), если оно отклоняется, и
CF_DEFER
(2), если решение о разрешении или запрете соединения откладывается. Если функция обратного вызова вернула
CF_REJECT
, to
WSAAccept
завершается с ошибкой
WSAECONNREFUSED
, если
CF_DEFER
— то с ошибкой
WSATRY_AGAIN
(в последнем случае соединение остаётся в очереди, и информация о нем вновь будет передана в функцию обратного вызова при следующем вызове
WSAAccept
). Обе эти ошибки не фатальные, сокет остается в режиме ожидания соединения и может принимать подключения от новых клиентов.
Ранее уже обсуждалось, что функция
connect
на стороне клиента считается успешно завершенной тогда, когда соединение встало в очередь, а не тогда, когда оно реально принято сервером через функцию
accept
. По умолчанию для клиента, соединение с которым сервер отклонил, нет разницы, вызвал ли сервер функцию
WSAAccept
и сразу отклонил соединение, или установил его с помощью
accept
, а потом разорвал. В обоих случаях клиент сначала получит информацию об успешном соединении с сервером, а потом это соединение будет разорвано. Но при использовании
WSAAccept
можно установить такой режим работы, когда сначала выполняется функция. заданная параметром
lpCondition
, и лишь потом клиенту отправляется разрешение или запрет на подключение. Включается этот режим установкой параметра слушающего сокета
SO_CONDITIONAL_ACCEPT
, что иллюстрирует листинг 2.42.
Листинг 2.42. Включение режима ожидания реального подключения
Этот режим снижает нагрузку на сеть и повышает устойчивость сервера против DoS-атак, заключающихся в многократном подключении-отключении посторонних клиентов, поэтому в серьезных серверах рекомендуется использовать эту возможность.
Из сказанного следует, что при использовании протокола TCP функция
WSAAccept
по сравнению с accept даёт два принципиальных преимущества: позволяет управлять качеством обслуживания и запрещать подключение нежелательных клиентов.
Некоторые протоколы поддерживают передачу информации не только при установлении связи, но и при её завершении. Для таких протоколов в WinSock2 предусмотрены функции
WSASendDisconnect
и
WSARecvDisconnect
. Так
как протокол TCP не поддерживает передачу данных при закрытии соединения, для него эти функции не дают никаких преимуществ по сравнению с вызовом функции
shutdown
, поэтому мы не будем их здесь рассматривать.
Далее мы рассмотрим несколько новых функций, унифицирующих работу с различными протоколами.
Функция
inet_addr
, как это уже упоминалось, жестко связана с протоколом IP и не имеет смысла для других протоколов. WinSock 2 предлагает вместо нее функцию
WSAStringToAddress
, имеющую следующий прототип (листинг 2.43).
Листинг 2.43. Функция
WSAStringToAddress
// ***** Описание на C++ *****
INT WSAStringToAddress(LPTSTR AddressString, INT AddressFamily, LPWSAPROTOCOL_INFO lpProtocolInfo, LPSOCKADDR lpAddress, LPINT lpAddressLength);
// ***** Описание на Delphi *****
function WSAStringToAddress(AddresString: PChar; AddressFamily: Integer; lpProtocolInfo: PWSAProtocolInfo; var Address: TSockAddr; var AddressLength: Integer): Integer;
Данная функция преобразует строку, задающую адрес сокета, в адрес, хранящийся в структуре
TSockAddr
. Параметр
AddressString
указывает на строку, хранящую адрес, параметр
AddressFamily
— на семейство адресов, для которого осуществляется трансляция. Если есть необходимость выбрать конкретный провайдер для протокола, в функцию может быть передан параметр
lpProtocolInfo
, в котором указан идентификатор провайдера. Если же программу устраивает провайдер по умолчанию, параметр
lpProtocolInfo
должен быть равен
nil
. Адрес возвращается через параметр
Address
. Параметр
AddressLength
при вызове функции должен содержать размер буфера, переданного через
Address
, а на выходе содержит реально использованное число байтов в буфере.
Функция возвращает 0 в случае успешного выполнения и
SOCKET_ERROR
— при ошибке.
Допустимый формат строки определяется протоколом (некоторые протоколы вообще не поддерживают текстовую запись адреса, и для них функция
WSAStringToAddress
неприменима). Для семейства
AF_INET
, к которому относятся TCP и UDP, адрес может задаваться в виде "IP1.IP2.IP3.IР4:Port" или "IP1.IP2.IP3.IP4", где IРn — n– й компонент IP-адреса, записанною в виде 4-байтных полей,
Port
— номер порта. Если порт явно не указан, устанавливается нулевой номер порта.
Таким образом, чтобы в структуре
TSockAddr
оказался, например, адрес 192.168.100.217 и порт с номером 5000, необходимо выполнить следующий код (листинг 2.44).