Отметим, что функция WSADuplicateSocket
может быть полезна только для копирования дескрипторов между разными процессами. Разные нити одного процесса не нуждаются в этой функции, т.к., находясь в одном адресном пространстве, они могут работать с одним и тем же дескриптором.
2.2.5. Асинхронный режим, основанный на сообщениях
Все операции с сокетами, которые мы рассматривали раньше, являлись синхронными. Программа, использующая такие сокеты, должна сама время от времени проверять тем или иным способом, пришли ли данные, установлена ли связь и т.п. Асинхронные сокеты позволяют программе получать уведомления о событиях, происходящих с сокетом: поступлении данных, освобождении места в буфере, закрытии и т.п. Такой способ работы лучше подходит для событийно-ориентированных программ, типичных для Windows. Поддержка асинхронных сокетов впервые появилась в WinSock 1 и была основана на сообщениях, которые обрабатывались оконными процедурами. В WinSock 2 этот асинхронный режим остался без изменений. Программист указывает, какое сообщение какому окну должно приходить при возникновении события на интересующем его сокете.
Асинхронный режим с уведомлением через сообщения устанавливается функцией WSAAsyncSelect
, имеющей следующий прототип:
function WSAAsyncSelect(S: TSocket; HWindow: HWND; wMsg: u_int; lEvent: LongInt): Integer;
Параметр S
определяет сокет, для которого устанавливается асинхронный режим работы. Параметр HWindow
— дескриптор окна, которому будут приходить сообщения, wMsg
— сообщение, a lEvent
задает события, которые вызывают отправку сообщения. Для этого параметра определены константы, комбинация которых задает интересующие программу события. Мы не будем рассматривать здесь все возможные события, остановимся только на самых главных (табл. 2.2).
Таблица 2.2. Асинхронные события сокета
Событие |
Комментарий |
FD_READ |
Сокет готов к чтению |
FD_WRITE |
Сокет готов к записи |
FD_ACCEPT |
В очереди сокета есть подключения (применимо только для сокетов, находящихся в режиме ожидания подключения) |
FD_CONNECT |
Соединение установлено (применимо только для сокетов, для которых вызвана функция connect или аналогичная ей) |
FD_CLOSE |
Соединение закрыто |
Каждый последующий вызов WSAAsyncSelect
для одного и того же сокета отменяет предыдущий вызов. Таким образом, в результате выполнения следующего кода форма будет получать только сообщения, показывающие готовность сокета к чтению, а готовность к записи не приведет к отправке сообщения (листинг 2.48).
Листинг 2.48. Последовательный вызов функции WSAAsyncSelect
WSAAsyncSelect(S, Form1.Handle, WM_USER, FD_WRITE);
// Второй вызов отменит результаты первого
WSAAsyncSelect(S, Form1.Handle, WM_USER, FD_READ);
// Теперь окно не будет получать уведомления о возможности записи
WSAAsyncSelect
связывает с сообщением именно сокет, а не его дескриптор. Это означает, что если две программы используют один сокет (копия дескриптора которого была создана с помощью функции WSADuplicateSocket
), и первая программа вызывает WSAAsyncSelect
со своим дескриптором, а затем вторая — со своим, то вызов WSAAsyncSelect
, сделанный во второй программе, отменит вызов, сделанный в первой.
Для того, чтобы получать сообщения при готовности сокета как к чтению, так и к записи, нужно выполнить следующий код.
WSAAsyncSelect(S, Form1.Handle, WM_USER, FD_READ or FD_WRITE);
При необходимости с помощью or
можно комбинировать и большее число констант.
Из сказанного следует, что нельзя связать с разными событиями одного и того же сокета разные сообщения (или отправлять сообщения разным окнам), т.к. при одном вызове WSAAsyncSelect
можно передать только один дескриптор окна и один номер сообщения, а следующий вызов этой функции, с другим дескриптором и/или номером, отменит предыдущий. Функция WSAAsyncSelect
переводит сокет в неблокирующий режим. Если необходимо использовать асинхронный сокет в блокирующем режиме, после вызова WSAAsyncSelect
требуется перевести его в этот режим вручную.
Сообщение, которое связывается с асинхронным сокетом, может быть любым. Обычно его номер выбирают от WM_USER
и выше, чтобы исключить путаницу со стандартными сообщениями.
При получении сообщения его параметр wParam
содержит дескриптор сокета, на котором произошло событие. Младшее слово lParam
содержит произошедшее событие (одну из констант FD_XXX
), а старшее слово — код ошибки если она произошла. Для выделения кода события и кода ошибки из lParam
в библиотеке WinSock предусмотрены макросы WSAGETSELECTEVENT
и WSAGETSELECTERROR
соответственно. В модуле WinSock они заменены функциями WSAGetSelectEvent
и WSAGetSelectError
. Одно сообщение может информировать только об одном событии на сокете. Если произошло несколько событий, в очередь окна будет добавлено несколько сообщений.
Читать дальше
Конец ознакомительного отрывка
Купить книгу