О чём не пишут в книгах по Delphi
Шрифт:
Если при вызове функции
WSARecvEx
флаг MSG_PARTIAL
установлен программой, но дейтаграмма поместилась в буфер целиком, функция сбрасывает этот флаг. В описании функции
WSARecvEx
в MSDN можно прочитать, что если дейтаграмма прочитана частично, то следующий вызов функции позволит прочитать оставшуюся часть дейтаграммы. Это не относится к протоколу UDP и справедливо только по отношению к протоколам типа SPX, в которых одна дейтаграмма может разбиваться на несколько сетевых пакетов и потому возможна ситуация, когда в буфере сокета окажется только часть дейтаграммы. В UDP, напомним, дейтаграмма всегда посылается одним IP-пакетом и помещается в буфер сразу целиком. Функция
WSARecvEx
не позволяет программе
recvfrom
с такими же возможностями в WinSock нет. Мы уже упоминали о том, что в WinSock 1 существует перекрытый ввод-вывод, но только для систем линии NT. Также в WinSock 1 определена функция
AcceptEx
, которая является более мощным эквивалентом функции accept
, и позволяет принимать входящие соединения в режиме перекрытого ввода-вывода. В WinSock 1 эта функция не поддерживается в Windows 95, в WinSock 2 она доступна во всех системах. Листинг 2.81 содержит ее прототип. Листинг 2.81. Функция
AcceptEx
function AcceptEx(sListenSocket, sAcceptSocket: TSocket; lpOutputBuffer: Pointer; dwReceiveDataLength: DWORD; dwLocalAddressLength: DWORD; dwRemoteAddressLength: DWORD; var lpdwBytesReceived: DWORD; lpOverlapped: POverlapped): BOOL;
Функция
AcceptEx
позволяет принять новое подключение со стороны клиента и сразу же получить от него первую порцию данных. Функция работает только в режиме перекрытого ввода-вывода. Параметр
sListenSocket
определяет сокет, который должен находиться в режиме ожидания подключения. Параметр sAcceptSocket
— сокет, через который будет осуществляться связь с подключившимся клиентом. Напомним, что функции accept
и WSAAccept
сами создают новый сокет. При использовании же AcceptEx
программа должна заранее создать сокет и, не привязывая его к адресу, передать в качестве параметра sAcceptSocket
. Параметр lpOutputBufer
задает указатель на буфер, в который будут помещены, во-первых, данные, присланные клиентом, а во-вторых, адреса подключившегося клиента и адрес, к которому привязывается сокет sAcceptSocket
. Параметр dwReceiveDataLength
задает число байтов в буфере, зарезервированных для данных, присланных клиентом, dwLocalAddressLength
— для адреса привязки сокета sAcceptSocket
, dwRemoteAddressLength
— адреса подключившегося клиента. Если параметр dwReceiveDataLength
равен нулю, функция не ждет, пока клиент пришлет данные, и считает операцию завершившейся сразу после подключения клиента, как функция accept. Для адресов нужно резервировать как минимум на 16 байтов больше места, чем реально требуется. Так как размер структуры TSockAddr
составляет 16 байтов, на каждый из адресов требуется зарезервировать как минимум 32 байта. Параметр lpdwBytesReceived
используется функцией, чтобы вернуть количество байтов, присланных клиентом. Параметр
lpOverlapped
указывает на запись TOverlapped
, определенную в модуле Windows следующим образом (листинг 2.82). Листинг 2.82. Тип
TOverlapped
POverlapped = TOverlapped;
_OVERLAPPED = record
Internal: DWORD;
InternalHigh: DWORD;
Offset: DWORD;
OffsetHigh: DWORD;
hEvent: THandle;
end;
TOverlapped = _OVERLAPPED;
Структура
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
.
Поделиться с друзьями: