winsock编程WSAEventSelect模型

winsock编程WSAEventSelect模型

  WSAEventSelect模型和WSAAsyncSelec模型类似,都是用调用WSAXXXXXSelec函数将socket和事件关联并注册到系统,并将socket设置成非阻塞模式。二者不同之处在于socket事件的通知方法:WSAAsyncSelec模型利用窗口句柄和消息映射函数通知网络事件,而WSAEventSelect模型利用WSAEVENT通知网络事件。完成WSAEventSelect模型需要涉及以下函数或结构:

1:WSAEventSelect

Description:The WSAEventSelect function specifies an event object to be associated with the specified set of FD_XXX network events.

1 int WSAEventSelect(
2   __in          SOCKET s,
3   __in          WSAEVENT hEventObject,
4   __in          long lNetworkEvents
5 );

Parameters

s

Descriptor identifying the socket.

hEventObject

Handle identifying the event object to be associated with the specified set of FD_XXX network events.与socket关联的事件对象,同时需要指定事件对象的类型,lNetworkEvents

lNetworkEvents

Bitmask that specifies the combination of FD_XXX network events in which the application has interest.

事件对象的事件集合,FD_ACCEPT等,用|符号指定多个类型。在WSAAsyncSelec相关文章http://www.cnblogs.com/hgwang/p/6093976.html内有介绍。

Return Value

  The return value is zero if the application‘s specification of the network events and the associated event object was successful. Otherwise, the value SOCKET_ERROR is returned, and a specific error number can be retrieved by calling WSAGetLastError.

Remarks

  The WSAEventSelect function automatically sets socket s to nonblocking mode, regardless of the value of lNetworkEvents. To set socket s back to blocking mode, it is first necessary to clear the event record associated with socket s via a call to WSAEventSelect with lNetworkEvents set to zero and the hEventObject parameter set to NULL. You can then call ioctlsocket or WSAIoctl to set the socket back to blocking mode.

  不管第3个参数是什么,WSAEventSelect 都会设置socket为非阻塞模式。设置socket为阻塞模式,受限需要清除socket的事件和事件类型关联,即再次调用WSAEventSelect ,将hEventObject 赋值为NULL,将lNetworkEvents 赋值为0。然后,调用ioctlsocket设置socket为阻塞模式。

rc = WSAEventSelect(s, hEventObject, 0);

  上例中,事件集合被设置为0,这将取消socket与事件的关联。MSDN给出解释,第三个参数为0,事件句柄hEventObject将被忽略。

  对同一个socket调用两次WSAEventSelect ,第二次传入的参数将覆盖第一次传入参数的效果。

1 rc = WSAEventSelect(s, hEventObject1, FD_READ);
2 rc = WSAEventSelect(s, hEventObject2, FD_WRITE); //bad

  上例中,s将只接收FD_WRITE事件消息。如果想要FD_WRITE和FD_READ都生效,需要改成FD_WRITE|FD_READ。

  Issuing a WSAAsyncSelect for a socket cancels any previous WSAAsyncSelect or WSAEventSelect for the same socket。

  Issuing a WSAEventSelect for a socket cancels any previous WSAAsyncSelect or WSAEventSelect for the same socket and clears the internal network event record.

  WSAAsyncSelect 和WSAEventSelect 能够互相取消对方的网络事件状态,所以,一个网络事件event,不可能同时被WSAAsyncSelect 和WSAEventSelect 触发,必有一个永远无法的到消息。也就是说,最好不要同时使用WSAAsyncSelect 和WSAEventSelect ,以免线程无限期等待。

2:WSAEVENT、WSACreateEvent、WSACloseEvent、WSASetEvent、WSAResetEvent

   和windows的event对象一样,WSAEVENT实际类型是HANDLE。WSACreateEvent和WSACloseEvent分别用了创建WSAEVENT和销毁WSAEVENT。函数定义如:

2.1 WSACreateEvent

WSAEVENT WSACreateEvent(void);

Return Value

  If no error occurs, WSACreateEvent returns the handle of the event object. Otherwise, the return value is WSA_INVALID_EVENT. To get extended error information, call WSAGetLastError.

Remarks

  The WSACreateEvent function creates an event object that is manually reset with an initial state of nonsignaled. Windows Sockets 2 event objects are system objects in Windows environments. Therefore, if a Windows application desires auto reset events, it can call the native CreateEvent Windows function directly. The scope of an event object is limited to the process in which it is created.

  需要注意的是,WSACreateEvent 创建的event是需要人工重置的事件对象,即需要手动reset释放event。想要创建一个自动重置的event,则可以直接调用CreateEvent 。也就是说,在windows网络编程内,WSACreateEvent 和CreateEvent 创建的对象都是可以识别的。

2.2 WSACloseEvent

BOOL WSACloseEvent(  __in          WSAEVENT hEvent);

Return Value

  If the function succeeds, the return value is TRUE.

  If the function fails, the return value is FALSE. To get extended error information, call WSAGetLastError.

  用WSACloseEvent关闭event对象后,所有对event的引用都将返回WSA_INVALID_HANDLE

2.3 WSASetEvent

BOOL WSASetEvent(  __in    WSAEVENT hEvent);

  设置event为授信状态。

Return Value

  If the function succeeds, the return value is TRUE.If the function fails, the return value is FALSE. To get extended error information, call WSAGetLastError.

2.4 WSAResetEvent

BOOL WSAResetEvent(  __in     WSAEVENT hEvent);

  设置event为未授信状态

Return Value

  If the WSAResetEvent function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call WSAGetLastError.

  WSASetEvent、WSAResetEvent都是需要在创建event的时候,将event设置成manually人工操作类型,WSACreateEvent 创建的event是需要人工重置的事件对象,即需要手动reset释放event。

WSAWaitForMultipleEvents

Description:The WSAWaitForMultipleEvents function returns when one or all of the specified event objects are in the signaled state, when the time-out interval expires, or when an I/O completion routine has executed

  WSAWaitForMultipleEvents 返回由以下几种情况造成:1,一个或多个event编程授信状态 ;2,时间到;3,IO完成例程开始执行

1 DWORD WSAWaitForMultipleEvents(
2   __in          DWORD cEvents,
3   __in          const WSAEVENT* lphEvents,
4   __in          BOOL fWaitAll,
5   __in          DWORD dwTimeout,
6   __in          BOOL fAlertable
7 );

Parameters

cEvents

The number of event object handles in the array pointed to by lphEvents. The maximum number of event object handles is WSA_MAXIMUM_WAIT_EVENTS. One or more events must be specified.

指定lphEvents内events的数量,传递给lphEvents的事件不能超过WSA_MAXIMUM_WAIT_EVENTS,也就是64个。

lphEvents

A pointer to an array of event object handles. The array can contain handles of objects of different types. It may not contain multiple copies of the same handle if the fWaitAll parameter is set to TRUE. If one of these handles is closed while the wait is still pending, the behavior of WSAWaitForMultipleEvents is undefined. The handles must have the SYNCHRONIZE access right. For more information, see Standard Access Rights.

event对象数组,能够包含多个不同类型的对象句柄。如果在等待期间有事件对象被close,那WSAWaitForMultipleEvents 的行为将无法预期。也就是说,不要在WSAWaitForMultipleEvents 期间close。

fWaitAll

A value that specifies the wait type. If TRUE, the function returns when the state of all objects in the lphEvents array is signaled. If FALSE, the function returns when any of the event objects is signaled. In the latter case, the return value minus WSA_WAIT_EVENT_0 indicates the index of the event object whose state caused the function to return. If more than one event object became signaled during the call, this is the array index to the signaled event object with the smallest index value of all the signaled event objects.

TRUE:在lphEvents 所有的event都授信后返回。

FALSE:用返回值减去WSA_WAIT_EVENT_0 等于事件对象的索引值。如果lphEvents存在多个event,则索引为下标最小的索引。

dwTimeout

The time-out interval, in milliseconds. WSAWaitForMultipleEvents returns if the time-out interval expires, even if conditions specified by the fWaitAll parameter are not satisfied. If the dwTimeout parameter is zero, WSAWaitForMultipleEvents tests the state of the specified event objects and returns immediately. If dwTimeout is WSA_INFINITE, WSAWaitForMultipleEvents waits forever; that is, the time-out interval never expires.

如果设置了dwTimeout,不管event是否授信,在时间到达时,WSAWaitForMultipleEvents 都将返回。如果dwTimeout 设置为0,WSAWaitForMultipleEvents 将检测lphEvents的状态并立即返回,如果设置成WSA_INFINITE,WSAWaitForMultipleEvents 将无限等待直到有event编程授信状态。

fAlertable

A value that specifies whether the thread is placed in an alertable wait state so the system can execute I/O completion routines. If TRUE, the thread is placed in an altertable wait state and WSAWaitForMultipleEvents can return when the system executes an I/O completion routine. In this case, WSA_WAIT_IO_COMPLETION is returned and the event that was being waited on is not signaled yet. The application must call the WSAWaitForMultipleEvents function again. If FALSE, the thread is not placed in an altertable wait state and I/O completion routines are not executed.

Return Value

  如果WSAWaitForMultipleEvents 调用成功,将返回以下数值之一:

  1:WSA_WAIT_EVENT_0 to (WSA_WAIT_EVENT_0 + cEvents - 1)。如果fWaitAll是true,指示所有的事件都已授信。如果是false,则返回值减去WSA_WAIT_EVENT_0表示lphEvents里面最小授信事件的下标。

  2:WSA_WAIT_IO_COMPLETION。

  The wait was ended by one or more I/O completion routines that were executed. The event that was being waited on is not signaled yet. The application must call the WSAWaitForMultipleEvents function again. This return value can only be returned if the fAlertable parameter is TRUE.

  3:WSA_WAIT_TIMEOUT。超出dwTimeout设置的时间,且没有事件授信。或者没有IO完成例程执行。

  If the WSAWaitForMultipleEvents function fails, the return value is WSA_WAIT_FAILED.

4:WSAEnumNetworkEvents

Description:The WSAEnumNetworkEvents function discovers occurrences of network events for the indicated socket, clear internal network event records, and reset event objects (optional).

WSAEnumNetworkEvents 函数查找出给定socket的已授信事件,清楚网络事件记录,并且重置网络事件对象event(可选)。

1 int WSAEnumNetworkEvents(
2   __in          SOCKET s,
3   __in          WSAEVENT hEventObject,
4   __out         LPWSANETWORKEVENTS lpNetworkEvents
5 );

Parameters

s

A descriptor identifying the socket.

hEventObject

An optional handle identifying an associated event object to be reset.

可选,待重置的网络授信事件(必须是和s关联的网络事件)

lpNetworkEvents

A pointer to a WSANETWORKEVENTS structure that is filled with a record of network events that occurred and any associated error codes.

指向WSANETWORKEVENTS 的指针,WSANETWORKEVENTS 里面存储了当前socket的授信事件标识和错误代码。

Return Value

  The return value is zero if the operation was successful. Otherwise, the value SOCKET_ERROR is returned, and a specific error number can be retrieved by calling WSAGetLastError.

4.1 WSANETWORKEVENTS 的结构如下:

1 typedef struct _WSANETWORKEVENTS {
2 long lNetworkEvents;
3 int iErrorCode[FD_MAX_EVENTS];
4 } WSANETWORKEVENTS,  *LPWSANETWORKEVENTS;

Members

lNetworkEvents

Indicates which of the FD_XXX network events have occurred.

iErrorCode

Array that contains any associated error codes, with an array index that corresponds to the position of event bits in lNetworkEvents. The identifiers FD_READ_BIT, FD_WRITE_BIT and others can be used to index the iErrorCode array.

4.2 常见网络FD_XX的宏定义

/* WinSock 2 extension -- bit values and indices for FD_XXX network events*/
#define FD_READ_BIT      0
#define FD_READ          (1 << FD_READ_BIT)

#define FD_WRITE_BIT     1
#define FD_WRITE         (1 << FD_WRITE_BIT)

#define FD_OOB_BIT       2
#define FD_OOB           (1 << FD_OOB_BIT)

#define FD_ACCEPT_BIT    3
#define FD_ACCEPT        (1 << FD_ACCEPT_BIT)

#define FD_CONNECT_BIT   4
#define FD_CONNECT       (1 << FD_CONNECT_BIT)

#define FD_CLOSE_BIT     5
#define FD_CLOSE         (1 << FD_CLOSE_BIT)

#define FD_QOS_BIT       6
#define FD_QOS           (1 << FD_QOS_BIT)

#define FD_GROUP_QOS_BIT 7
#define FD_GROUP_QOS     (1 << FD_GROUP_QOS_BIT)

  从4.2可以看出,FD_XXX事件位偏移各不相同,这也是FD_AAA|FD_BBB生效的原因。WSANETWORKEVENTS内的lNetworkEvents取出网络事件状态操作为如下:

1 WSANETWORKEVENTS  nwevents;
2 WSAEnumNetworkEvents(m_socks[i],m_events[i],&nwevents);
3 if(nwevents.lNetworkEvents & FD_ACCEPT)
4 {...
5 }

  而WSANETWORKEVENTS内的iErrorCode是socket状态数组,0表示当前socket状态正常,非0指示socket错误代码,取出来对应授信事件的操作是:

1 if (nwevents.iErrorCode[FD_ACCEPT_BIT] == 0)
2 {...}

5:例子

  封装类CEventSelect,基类CTaskSvc提供线程函数,见http://www.cnblogs.com/hgwang/p/6094444.html。

  EventSelect.h

 1 #pragma once
 2 #include "TaskSvc.h"
 3 class CEventSelect : public CTaskSvc
 4 {
 5 public:
 6     CEventSelect(void);
 7     ~CEventSelect(void);
 8
 9 private:
10     void svc();
11
12     WSAData        m_wsa;
13     bool        m_bRes;
14     SOCKET        m_listensocket;
15
16 private:
17     WSAEVENT    m_events[WSA_MAXIMUM_WAIT_EVENTS];
18     SOCKET        m_socks[WSA_MAXIMUM_WAIT_EVENTS];
19     int            m_eventsNum;
20 };

  EventSelect.cpp

  1 #include "StdAfx.h"
  2 #include "EventSelect.h"
  3
  4 CEventSelect::CEventSelect(void)
  5 {
  6     m_bRes = true;
  7     WSAStartup(MAKEWORD(2,3),&m_wsa);
  8     m_listensocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  9     if (m_listensocket == INVALID_SOCKET )
 10     {
 11         m_bRes = false;
 12     }
 13     sockaddr_in    m_server;
 14     m_server.sin_family = AF_INET;
 15     m_server.sin_port = htons(8828);
 16     m_server.sin_addr.s_addr = inet_addr("127.0.0.1");
 17     if (m_bRes && (bind(m_listensocket,(sockaddr*)&m_server,sizeof(sockaddr_in)) == SOCKET_ERROR))
 18     {
 19         DWORD dw = WSAGetLastError();
 20         m_bRes = false;
 21     }
 22     if (m_bRes && (listen(m_listensocket,SOMAXCONN) == SOCKET_ERROR))
 23     {
 24         m_bRes = false;
 25     }
 26     WSAEVENT e = WSACreateEvent();
 27     if (m_bRes && WSAEventSelect(m_listensocket,e,FD_ACCEPT|FD_CLOSE))
 28     {
 29         m_bRes = false;
 30     }
 31     m_eventsNum = 0;
 32     m_events[m_eventsNum] = e;
 33     m_socks[m_eventsNum] = m_listensocket;
 34     m_eventsNum++;
 35     Activate();
 36 }
 37
 38 CEventSelect::~CEventSelect(void)
 39 {
 40     for(int i = 0; i < m_eventsNum; i++)
 41     {
 42         //关闭event和socket,首先应当使用WSAEventSelect设置事件0,接触socket和event的关联
 43         WSAEventSelect(m_socks[i], m_events[i], 0);
 44         //关闭socket
 45         closesocket(m_socks[i]);
 46         //关机event
 47         WSACloseEvent(m_events[i]);
 48     }
 49 }
 50
 51
 52 void CEventSelect::svc()
 53 {
 54     while (true)
 55     {
 56         DWORD dw = WSAWaitForMultipleEvents(m_eventsNum,m_events,FALSE,WSA_INFINITE,FALSE);
 57         if (dw == WSA_WAIT_TIMEOUT)
 58         {
 59             continue;
 60         }
 61         //如果WSAWaitForMultipleEvents第三项是true,则返回值标识所有的事件都已授信
 62         //如果WSAWaitForMultipleEvents第三项是false,则返回值标识已授信事件的最小索引
 63         DWORD index = dw - WSA_WAIT_EVENT_0;
 64         //获取授信事件的最小索引,后面所有事件的状态都不知道,所以要从最小索引开始,轮询一遍
 65         //否则,下次进入svc调用WSAWaitForMultipleEvents又返回最小索引
 66         for (int i=index;i<m_eventsNum;i++)
 67         {
 68             //循环调用WSAWaitForMultipleEvents
 69             //注意:此次调用参数不同,
 70             //1:需要查询的事件个数为1,第一个参数为1,第二个参数为待查询的事件地址
 71             //2:由于事件个数只有一个,第3个参数可改成true
 72             //3:不能设置等待时间为WSA_INFINITE,如果当前事件无限期等待,程序将阻塞在此
 73             DWORD dw = WSAWaitForMultipleEvents(1,&m_events[i],TRUE,1000,FALSE);
 74             if (dw == WSA_WAIT_TIMEOUT || dw == WSA_WAIT_FAILED)
 75             {
 76                 //超时,下一个事件查询
 77                 continue;
 78             }
 79             //使用WSAEnumNetworkEvents 获取授信事件
 80             WSANETWORKEVENTS  nwevents;
 81             //获取socket[i]的事件集合,并将重置m_events[i]
 82             WSAEnumNetworkEvents(m_socks[i],m_events[i],&nwevents);
 83             //accept请求到达
 84             if(nwevents.lNetworkEvents & FD_ACCEPT)
 85             {
 86                 //验证当前网络状态,非0对应错误码
 87                 if (nwevents.iErrorCode[FD_ACCEPT_BIT] == 0)
 88                 {
 89                     sockaddr_in    m_client;
 90                     int sz = sizeof(sockaddr_in);
 91                     SOCKET acp = accept(m_socks[i],(sockaddr*)&m_client,&sz);
 92                     if (acp == INVALID_SOCKET)
 93                     {
 94                         continue;
 95                     }
 96                     //为新连接的socket关联事件
 97                     WSAEVENT e = WSACreateEvent();
 98                     WSAEventSelect(acp,e,FD_READ|FD_WRITE|FD_CLOSE);
 99                     m_events[m_eventsNum] = e;
100                     m_socks[m_eventsNum] = acp;
101                     m_eventsNum++;
102                 }
103             }
104             else if (nwevents.lNetworkEvents & FD_READ)
105             {
106                 if (nwevents.iErrorCode[FD_READ_BIT] == 0)
107                 {
108                     char buf[1024];
109                     int res = recv(m_socks[i],buf,1024,0);
110                     if (res == 0)
111                     {
112                         closesocket(m_socks[i]);
113                         break;
114                     }
115                     buf[res] = 0;
116                     cout<<buf<<endl;
117                 }
118             }
119             else if (nwevents.lNetworkEvents & FD_WRITE)
120             {
121                 if (nwevents.iErrorCode[FD_WRITE_BIT] == 0)
122                 {
123                     std::string str = "send data from service";
124                     int sz = send(m_socks[i],str.c_str(),str.length(),0);
125                     if (sz == SOCKET_ERROR)
126                     {
127                         if (WSAGetLastError() == WSAEWOULDBLOCK)
128                         {
129                             continue;
130                         }
131                     }
132                 }
133             }
134             else if (nwevents.lNetworkEvents & FD_CLOSE)
135             {
136                 //此处不应再判断结束状态,非正常终止的连接将返回错误码10053或10054,而非0
137                 //if (nwevents.iErrorCode[FD_CLOSE_BIT] == 0)
138                 {
139                     closesocket(m_socks[i]);
140                     //从socket数组中移除当前socket
141                     //此过程省略
142                 }
143             }
144         }
145     }
146 }

测试结果:

时间: 2024-10-10 17:57:53

winsock编程WSAEventSelect模型的相关文章

winsock编程IOCP模型实现代码

winsock编程IOCP模型实现代码 话不多说,上代码.借鉴<windows核心编程>部分源码和CSDN小猪部分代码. stdafx.h依赖头文件: 1 #include <iostream> 2 #include <WinSock2.h> 3 #include <MSWSock.h> 4 #include <vector> 5 #include "Singleton.h" 6 #include "IOCPWrap

WSAEventSelect模型编程 详解

转自:http://blog.csdn.net/wangjieest/article/details/7042108 WSAEventSelect模型编程 WSAEventSelect模型编程这个模型是一个简单的异步事件模型,使用起来比较方便,现在说一下其的具体的用法和需要注意的地方.一,模型的例程(服务端):先举一个王艳平网络通信上的例子: [cpp] view plaincopyprint? //////////////////////////////////////////////////

Winsock I/O 模型详解

Winsock共有五种类型的套接字I/O模型,可让Winsock应用程序对I/O进行管理,它们包括: select(选择).WSAAsyncSelect(异步选择).WSAEventSelect(事件选择).overlapped(重叠).以及completion port(完成端口). 1.Select(选择)模型 利用select函数,判断套接字上是否存在数据,或者能否向一个套接字写入数据.目的是防止应用程序在套接字处于锁定模式时,调用recv(或send)从没有数据的套接字上接收数据,被迫进

WSAEventSelect模型详解

WSAEventSelect 是 WinSock 提供的一种异步事件通知I/O模型,与 WSAAsyncSelect模型有些类似.       该模型同样是接收 FD_XXX 之类的网络事件,但是是通过事件对象句柄通知,而非像 WSAAsyncSelect一样依靠Windows的消息驱动机制. 与WSAAsyncSelect模型相同,WSAEventSelect将所有的SOCKET事件分为如下类型:(共十种)                FD_READ , FD_WRITE , FD_OOB

Winsock—I/O模型之选择模型(一)

Winsock中提供了一些I/O模型帮助应用程序以异步方式在一个或多个套接字上管理I/O. 这样的I/O模型有六种:阻塞(blocking)模型,选择(select)模型,WSAAsyncSelect模型,WSAEventSelect模型,重叠(overlapped)模型,完成端口(completion port)模型. 选择模型: 目的:允许想要避免在套接字调用上阻塞的应用程序有能力管理多个套接字. 一.select函数             select函数可以确定一个或者多个套接字的状态

winsock的io模型(终极篇)

最近在看服务器框架的搭建,看了不少,都是零零碎碎的,觉得看的差不多了,可以写点最后的总结了,然后,竟然发现了这篇文章,总结做的特别好,肯定比我总结写要好多了,所以我也就不写了,直接转吧...... 套接字模式:锁定.非锁定套接字I/O模型:       select(选择)WSAAsyncSelect(异步选择)WSAEventSelect(事件选择)Overlapped I/O(重叠式I / O)Completion port(完成端口) 一. 简介套接字模型的出现,是为了解决套接字模式存在的

基于UDP的Winsock编程(C++版)

基于UDP的Winsock编程与基于TCP的Winsock编程相比,只是缺少了一个步骤而已.对于Server,缺少了接受连接的过程(accept()函数调用):对于Client,缺少了请求连接的过程(connect()函数调用). 另外与TCP区别的还有,在UDP中,数据收发函数是:sendto(),和recvfrom()函数. 函数原型为: int sendto(SOCKET s,const char FAR *buf,int len,int flags,const struct sockad

Winsock 编程流程

最近看了<Window程序设计>感觉在网络方面讲的不错,讲的很通俗易懂,与大家一同交流 转载请注明出处:http://blog.csdn.net/u010484477谢谢^_^ 使用 Winsock 编程的一般步骤是比较固定的. 1.Winsock 库的装入.初始化和释放 所有的 WinSock 函数都是从 WS2_32.DLL 库导出的,VC++在默认情况下并没有连接到该库,如果想使用 Winsock API,就必须包含相应的库文件. #pragma commment(lib, "

0730------Linux网络编程----------服务器端模型(迭代,多进程,多线程,select,poll,epoll 等)

1.迭代服务器模型 1.1 迭代服务器是处理多个请求时一种最简单直接的思路,即使用while循环,它不具有并发能力,即必须一个一个的处理客户的请求. 1.2 程序示例. #include "def.h" int listenfd_init(); //返回一个处于监听状态的套接字描述符 void do_service(int peerfd); // 处理客户端的请求 int main(int argc, const char *argv[]) { if(signal(SIGPIPE, S