Использование событий (events) в WinSock2

Рано или поздно у большинства кодеров появляется желание написать какую-нибудь сетевую программу. И у многих начинающих появляется куча вопросов – как, что и чем.… Вот так и я, решив написать что-то простенькое, задумался, а как? Выбор пал на изучение WinSock, так как он предоставляет все возможности и обеспечивает максимальное быстродействие для работы с сетью по сравнению с компонентами-обертками типа Indy, с которым даже ни разу не работа О_о.
Фиг с ними индейцами вернемся к нашим событиям. Events – вещь в какой-то степени удобная и простая. Как обуздать это детище – да в принципе не сложно, но обо всем по порядку. Для начала все как обычно – подготовим библиотеку winsock2:

WSAStartup(MakeWord(2, 2), wsaData);

Создадим сокет для TCP\IP:

hSocket:= WSASocket(AF_INET, SOCK_STREAM, 0, nil, 0, 0);

Заполним структуру SockAddr_In :

SockAddrIn.sin_family:= AF_INET;
SockAddrIn.sin_port:= htons(Port);
SockAddrIn.sin_addr.s_addr:= inet_addr(‘127.0.0.1’);

А далее перейдем к созданию события с помощью WSACreateEvent :

hEvent:= WSACreateEvent;

После того, как событие создано, его необходимо связать с сокетом, события которого мы хотим обрабатывать. Это делается функцией WSAEventSelect(). В качестве параметров функция принимает указатель на сокет (hSocket), указатель на созданное событие (hEvent) и события, которые будут обрабатываться FD_CONNECT, FD_WRITE, FD_READ и т.д.
Небольшое замечание по поводу привязки обрабатываемых событий, вот так делать нельзя:

hEvent1:= WSACreateEvent;
hEvent2:= WSACreateEvent;
WSAEventSelect(hSocket,  hEvent1,  FD_READ);
WSAEventSelect(hSocket,  hEvent2,  FD_WRITE);

Правильно сделать так:

WSAEventSelect(hSocket,  hEvent, FD_READ or FD_WRITE);

Теперь, когда обрабатываемые события заданы для сокета, нам необходимо их как-то обрабатывать. Для ожидания событий используется функция WSAWaitForMultipleEvents(). Она будет работать как поток в спящем режиме до тех пор, пока не произойдёт событие, заданное нами для сокета.

function WSAWaitForMultipleEvents(cEvents: DWORD; lphEvents: PWSAEVENT; fWaitAll: LongBool; dwTimeout: DWORD;  fAlertable: LongBool): DWORD; stdcall;

cEvents – количество событий для ожидания;
lphEvents – указатель на массив событий для ожидания;
fWaitAll – определяет будет ли функция оставаться в спящем режиме до тех пор пока не сработают все события;
dwTimeout – определяет как долго ожидать наступления события.
fAlertable – значение, которое определяет, будет ли поток помещается в alertable.

Пример вызова этой функции только одного события:

WSAWaitForMultipleEvents(1, @hEvent, False, WSA_INFINITE, False);

Для того чтобы определить какое событие произошло необходимо вызвать функцию WSAEnumNetworkEvents :

function WSAEnumNetworkEvents(const s: TSocket; const hEventObject: WSAEVENT; lpNetworkEvent: LPWSANETWORKEVENTS): Integer; stdcall;

В нашем случае она будет выглядеть так:

WSAEnumNetworkEvents(hSocket, hEvent, @NetworkEvents);

Осталось только проверить что за событие и не произошла ли ошибка, в обобщенном виде код примерно такой:

        if ((NetworkEvents.lNetworkEvents and FD_xxx)<>0) then begin
          if (NetworkEvents.iErrorCode[FD_xxx_BIT]<>0) then begin
            // FD_xxх  failed with error: 
            // NetworkEvents.iErrorCode[FD_xxx_BIT]
            Break;
          end else begin
            // FD_xxx is Ok
          end;
        end;


Где «xxx» одно из ожидаемых событий READ, WRITE, CONNECT и так далее.
Вот в общем-то и все, ко всему вышесказанному прилагаю небольшой, и не очень красиво сделанный, но работающий пример “программулины”.


Files:

Links:


Комментарии

  1. =Zeus= · 2 Май 2011, 14:21 · #

    Спасибо за пример, очень пригодился.