Механизм обработки сетевых событий в Winsock2Автор: Joseph Dempsey Обычно, при программировании сокетов под Winsock 1.1, используются стандартные и, надеюсь известные большинству программистов операторы. При этом оповещение о событии на сокете проходит через сообщения windows. Думаю, не секрет, что такой способ порождает массу проблем при разработке приложений. Однако можно воспользоваться другим методом - в обход сообщений Windows, а именно через события WSA (WSA Events). Как это работает?При работе с сокетом большинство программистов используют стандартный набор событий: для отправки данных, приёма данных, соединения с другим сокетом, для установления канала передачи данных при входящем запросе и для закрытия сокета. Возможно есть и другие события для сокетов, но в этой статье мы сосредоточимся на основных. Когда одно из этих событий, ассоциированных с сокетом, происходит, то мы получаем сигнал и производим необходимые действия для обработки данного события. Вот основные константы, описывающие сетевые события (те, которые будут фигурировать в данной статье):
Итак, давайте рассмотрим весь процесс создания, отслеживания и обработки сетевых событий. Во-первых нам прийдётся инициализировать библиотеку winsock2. Впринципе существует нескольколько способов сделать это, например так: WSADATA wsd; LPFN_WSASTARTUP lpf = (LPFN_WSA_STARTUP)::GetProcAddress( ::LoadLibrary("WS2_32.DLL"), "WSAStartup"); lpf(0x0202, &wsd); Инициализировать можно в любом месте программы,
но обязательно до вызова каких-либо функций winsock.
Следующий важный момент - это создание события,
которое мы хотим отслеживать на данном сокете.
Для этого будем использовать вызов Winsock2 API WSAEVENT hEvent = WSA_INVALID_EVENT; hEvent = WSACreateEvent(); ::WSAEventSelect(m_listen, m_hEvent, FD_ACCEPT); Если мы хотим, чтобы сокет (в данном случае это WSAEVENT hDataEvent = WSA_INVALID_EVENT; hDataEvent = WSACreateEvent(); ::WSAEventSelect(m_socket, hDataEvent, FD_WRITE | FD_READ | FD_CLOSE); Необходимо заметить, что для одного и того же сокета невозможно создать два объекта событий, то есть следующий код неверен: WSAEVENT hEvent1 = WSA_INVALID_EVENT; WSAEVENT hEvent2 = WSA_INVALID_EVENT; hEvent1 = WSACreateEvent(); hEvent2 = WSACreateEvent(); ::WSAEventSelect(m_socket, hEvent1, FD_READ); ::WSAEventSelect(m_socket, hEvent2, FD_WRITE);
Обработка уведомлений о событияхТеперь, когда события заданы, нам необходимо
ожидать их и, соответственно, обрабатывать. Для ожидания
событий можно использовать функцию // Если же мы хотим ожидать только одного события, то эту функцию можно вызвать следующим образом: int nReturnCode = ::WSAWaitForMultipleEvents(1, &hEvent1, FALSE, INFINITE, FALSE); Первый параметр - это количество событий, которые мы хотим ожидать. Второй параметр - это указатель на массив событий, которые мы хотим ожидать. Третий параметр имеет значение BOOL, которое определяет - будет ли функция оставаться в спящем режиме до тех пор пока не сработают все события. Обычно этот параметр задаётся как false, но возможно Вам может понадобиться ожидать наступления всех событий. Четвёртый параметр определяет - как долго ожидать наступления события. Обычно я запускаю отдельный поток и оставляю его как infinite. Но, если Вы будете запускать функцию в основном потоке, то может понадобиться поставить ограничение в 5 (или больше) секунд, чтобы дать возможность приложению обрабатывать другие события. Пятый параметр указывает на то, хотим мы или нет получать алерты. Теперь необходимо позаботиться об
обработчиках каждого события. Перво-наперво нам
необходимо получить достоверную информацию о
том, какое событие возникло. Для этого существует
функция WSANETWORKEVENTS hConnectEvent; WSANETWORKEVENTS hProcessEvent; ::WSAEnumNetworkEvents(m_listen, hConnectEvent, &wsaConnectEvents); ::WSAEnumNetworkEvents(m_data, hProcessEvent, &wsaProcessEvents); В заключении мы получаем событие, которое возникает на одном из сокетов. Давайте посмотрим, как выглядит процесс выборки нужного события и его обработки: if( (wsaConnectEvents.lNetworkEvents & FD_ACCEPT) && //Нужное ли нам событие ? (wsaConnectEvents.iErrorCode[FD_ACCEPT_BIT] == 0) ) //если нет ошибок, { //то обрабатываем /* .... Здесь находится обработчик .... */ } Эта проверка может быть сделана для каждого WSAEVENT, который Вы установили, и для каждого сетевого события, для которого WSAEVENT будет сигнализировать. Для обработки другого события, в последнем примере достаточно изменить FD_ACCEPT на необходимую константу и, соотвественно изменить константу проверки бита ошибки.
|