Структура TOverlapped
используется, в основном, для перекрытого ввода-вывода в файловых операциях. Видно, что она отличается от уже знакомой нам структуры TWSAOverlapped
(см. листинг 2.69) только типом параметра hEvent
— THandle
вместо TWSAEvent
. Впрочем, ранее мы уже обсуждали, что TWSAEvent
— это синоним THandle
, так что можно сказать, что эти структуры идентичны (но компилятор подходит к этому вопросу формально и считает их разными).
Параметр lpOverlapped
функции AcceptEx
не может быть равным -1, а его поле hEvent
должно указывать на корректное событие. Процедуры завершения не предусмотрены. Если на момент вызова функции клиент уже подключился и прислал первую порцию данных (или место для данных в буфере не зарезервировано), AcceptEx
возвращает True
. Если же клиент еще не подключился, или подключился, но не прислал данные, функция AcceptEx
возвращает False
, а WSAGetLastError
— ERROR_IO_PENDING
. Параметр lpBytesReceived
в этом случае остается без изменений.
Проконтролировать состояние операции можно с помощью функции GetOverlappedResult
, которая является аналогом известной нам функции WSAGetOverlappedResult
, за исключением того, что использует запись TOverlapped
вместо TWSAOverlapped
и не предусматривает передачу флагов. С ее помощью можно узнать, завершилась ли операция, а также дождаться ее завершения и узнать, сколько байтов прислано клиентом (функция AcceptEx
не ждет, пока клиент заполнит весь буфер, предназначенный для него — для завершения операции подключения достаточно первого пакета).
Если к серверу подключаются некорректно работающие клиенты, которые не присылают данные после подключения, операция может не завершаться очень долго, что будет мешать подключению новых клиентов. MSDN рекомендует при ожидании время от времени с помощью функции getsockopt
для сокета sAcceptSocket
узнавать значение целочисленного параметра SO_CONNECT_TIME
уровня SOL_SOCKET
. Этот параметр показывает время в секундах, прошедшее с момента подключения клиента к данному сокету (или -1, если подключения не было). Если подключившийся клиент слишком долго не присылает данных, сокет sAcceptSocket
следует закрыть, что приведет к завершению операции, начатой AcceptEx
, с ошибкой. После этого можно снова вызывать AcceptEx
для приема новых клиентов.
Функция AcceptEx
реализована таким образом, чтобы обеспечивать максимальную скорость подключения. Ранее мы говорили, что сокеты, созданные функциями accept
и WSAAccept
, наследуют параметры слушающего сокета (например, свойства асинхронного режима). Для повышения производительности сокет sAcceptSocket
по умолчанию не получает свойств сокета sListenSocket
. Но он может унаследовать их после завершения операции с помощью следующей установки параметра сокета SO_UPDATE_ACCEPT_CONTEXT
:
setsockopt(sAcceptSocket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, PChar(@sListenSocket), SizeOf(sListenSocket));
Ha сокет sAcceptedSocket
после его подключения к клиенту накладываются ограничения: он может использоваться не во всех функциях WinSock, а только в следующих: send
, WSASend
, recv
, WSARecv
, ReadFile
, WriteFile
, TransmitFile
, closesocket
и setsockopt
, причем в последней — только для установки параметра SO_UPDATE_ACCEPT_CONTEXT
.
В WinSock не документируется, в какую именно часть буфера помещаются адрес клиента и принявшего его сокета. Вместо этого предоставляется функция GetAcceptExSockAddrs
, прототип которой приведен в листинге 2.83.
GetAcceptExSockAddrs
procedure GetAcceptExSockAddrs(lpOutputBuffer: Pointer; dwReceiveDataLength: DWORD; dwLocalAddressLength: DWORD; dwRemoteAddressLength: DWORD; var LocalSockaddr: PSockAddr; var LocalSockaddrLength: Integer; var RemoteSockaddr: PSockAddr; var RemoteSockaddrLength: Integer);