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.

 int WSAEventSelect(
__in SOCKET s,
__in WSAEVENT hEventObject,
__in long lNetworkEvents
);

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,这将取消socket与事件的关联。MSDN给出解释,第三个参数为0,事件句柄hEventObject将被忽略。

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

 rc = WSAEventSelect(s, hEventObject1, FD_READ);
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完成例程开始执行

 DWORD WSAWaitForMultipleEvents(
__in DWORD cEvents,
__in const WSAEVENT* lphEvents,
__in BOOL fWaitAll,
__in DWORD dwTimeout,
__in BOOL fAlertable
);

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(可选)。

 int WSAEnumNetworkEvents(
__in SOCKET s,
__in WSAEVENT hEventObject,
__out LPWSANETWORKEVENTS lpNetworkEvents
);

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 的结构如下:

 typedef struct _WSANETWORKEVENTS {
long lNetworkEvents;
int iErrorCode[FD_MAX_EVENTS];
} 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取出网络事件状态操作为如下:

 WSANETWORKEVENTS  nwevents;
WSAEnumNetworkEvents(m_socks[i],m_events[i],&nwevents);
if(nwevents.lNetworkEvents & FD_ACCEPT)
{...
}

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

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

5:例子

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

  EventSelect.h

 #pragma once
#include "TaskSvc.h"
class CEventSelect : public CTaskSvc
{
public:
CEventSelect(void);
~CEventSelect(void); private:
void svc(); WSAData m_wsa;
bool m_bRes;
SOCKET m_listensocket; private:
WSAEVENT m_events[WSA_MAXIMUM_WAIT_EVENTS];
SOCKET m_socks[WSA_MAXIMUM_WAIT_EVENTS];
int m_eventsNum;
};

  EventSelect.cpp

 #include "StdAfx.h"
#include "EventSelect.h" CEventSelect::CEventSelect(void)
{
m_bRes = true;
WSAStartup(MAKEWORD(,),&m_wsa);
m_listensocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (m_listensocket == INVALID_SOCKET )
{
m_bRes = false;
}
sockaddr_in m_server;
m_server.sin_family = AF_INET;
m_server.sin_port = htons();
m_server.sin_addr.s_addr = inet_addr("127.0.0.1");
if (m_bRes && (bind(m_listensocket,(sockaddr*)&m_server,sizeof(sockaddr_in)) == SOCKET_ERROR))
{
DWORD dw = WSAGetLastError();
m_bRes = false;
}
if (m_bRes && (listen(m_listensocket,SOMAXCONN) == SOCKET_ERROR))
{
m_bRes = false;
}
WSAEVENT e = WSACreateEvent();
if (m_bRes && WSAEventSelect(m_listensocket,e,FD_ACCEPT|FD_CLOSE))
{
m_bRes = false;
}
m_eventsNum = ;
m_events[m_eventsNum] = e;
m_socks[m_eventsNum] = m_listensocket;
m_eventsNum++;
Activate();
} CEventSelect::~CEventSelect(void)
{
for(int i = ; i < m_eventsNum; i++)
{
//关闭event和socket,首先应当使用WSAEventSelect设置事件0,接触socket和event的关联
WSAEventSelect(m_socks[i], m_events[i], );
//关闭socket
closesocket(m_socks[i]);
//关机event
WSACloseEvent(m_events[i]);
}
} void CEventSelect::svc()
{
while (true)
{
DWORD dw = WSAWaitForMultipleEvents(m_eventsNum,m_events,FALSE,WSA_INFINITE,FALSE);
if (dw == WSA_WAIT_TIMEOUT)
{
continue;
}
//如果WSAWaitForMultipleEvents第三项是true,则返回值标识所有的事件都已授信
//如果WSAWaitForMultipleEvents第三项是false,则返回值标识已授信事件的最小索引
DWORD index = dw - WSA_WAIT_EVENT_0;
//获取授信事件的最小索引,后面所有事件的状态都不知道,所以要从最小索引开始,轮询一遍
//否则,下次进入svc调用WSAWaitForMultipleEvents又返回最小索引
for (int i=index;i<m_eventsNum;i++)
{
//循环调用WSAWaitForMultipleEvents
//注意:此次调用参数不同,
//1:需要查询的事件个数为1,第一个参数为1,第二个参数为待查询的事件地址
//2:由于事件个数只有一个,第3个参数可改成true
//3:不能设置等待时间为WSA_INFINITE,如果当前事件无限期等待,程序将阻塞在此
DWORD dw = WSAWaitForMultipleEvents(,&m_events[i],TRUE,,FALSE);
if (dw == WSA_WAIT_TIMEOUT || dw == WSA_WAIT_FAILED)
{
//超时,下一个事件查询
continue;
}
//使用WSAEnumNetworkEvents 获取授信事件
WSANETWORKEVENTS nwevents;
//获取socket[i]的事件集合,并将重置m_events[i]
WSAEnumNetworkEvents(m_socks[i],m_events[i],&nwevents);
//accept请求到达
if(nwevents.lNetworkEvents & FD_ACCEPT)
{
//验证当前网络状态,非0对应错误码
if (nwevents.iErrorCode[FD_ACCEPT_BIT] == )
{
sockaddr_in m_client;
int sz = sizeof(sockaddr_in);
SOCKET acp = accept(m_socks[i],(sockaddr*)&m_client,&sz);
if (acp == INVALID_SOCKET)
{
continue;
}
//为新连接的socket关联事件
WSAEVENT e = WSACreateEvent();
WSAEventSelect(acp,e,FD_READ|FD_WRITE|FD_CLOSE);
m_events[m_eventsNum] = e;
m_socks[m_eventsNum] = acp;
m_eventsNum++;
}
}
else if (nwevents.lNetworkEvents & FD_READ)
{
if (nwevents.iErrorCode[FD_READ_BIT] == )
{
char buf[];
int res = recv(m_socks[i],buf,,);
if (res == )
{
closesocket(m_socks[i]);
break;
}
buf[res] = ;
cout<<buf<<endl;
}
}
else if (nwevents.lNetworkEvents & FD_WRITE)
{
if (nwevents.iErrorCode[FD_WRITE_BIT] == )
{
std::string str = "send data from service";
int sz = send(m_socks[i],str.c_str(),str.length(),);
if (sz == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
continue;
}
}
}
}
else if (nwevents.lNetworkEvents & FD_CLOSE)
{
//此处不应再判断结束状态,非正常终止的连接将返回错误码10053或10054,而非0
//if (nwevents.iErrorCode[FD_CLOSE_BIT] == 0)
{
closesocket(m_socks[i]);
//从socket数组中移除当前socket
//此过程省略
}
}
}
}
}

测试结果:

winsock编程WSAEventSelect模型的更多相关文章

  1. winsock编程IOCP模型实现代码

    winsock编程IOCP模型实现代码 话不多说,上代码.借鉴<windows核心编程>部分源码和CSDN小猪部分代码. stdafx.h依赖头文件: #include <iostr ...

  2. winsock编程WSAAsyncSelect模型

    winsock编程WSAAsyncSelect模型 WSAAsyncSelect模型也称异步选择模型,其核心函数是WSAAsyncSelect.它可以用来在一个socket上接收以windows消息为 ...

  3. winsock编程select模型

    winsock编程select模型 网络服务端连接数量过多时,为每一个连接申请一个线程会让机器性能急剧下降(大多说是因为线程在用户态和内核态之间切换会占用大量的CPU时间片).为了解决多线程带来的性能 ...

  4. WSAEventSelect模型编程 详解

    转自:http://blog.csdn.net/wangjieest/article/details/7042108 WSAEventSelect模型编程 WSAEventSelect模型编程这个模型 ...

  5. WinSock WSAEventSelect 模型总结

    前言 本文配套代码:https://github.com/TTGuoying/WSAEventSelect-model 由于篇幅原因,本文假设你已经熟悉了利用Socket进行TCP/IP编程的基本原理 ...

  6. WinSock WSAEventSelect 模型

    在前面我们说了WSAAsyncSelect 模型,它相比于select模型来说提供了这样一种机制:当发生对应的IO通知时会立即通知操作系统,并调用对应的处理函数,它解决了调用send和 recv的时机 ...

  7. Winsock编程基础介绍 .

    相信很多人都对网络编程感兴趣,下面我们就来介绍,在网络编程中应用最广泛的编程接口Winsock API. 使用Winsock API的编程,应该了解一些TCP/IP的基础知识.虽然你可以直接使用Win ...

  8. WinSock 重叠IO模型

    title: WinSock 重叠IO模型 tags: [WinSock 模型, 网络编程, 重叠IO模型] date: 2018-06-29 20:26:13 categories: Windows ...

  9. WSAEventSelect模型详解

    WSAEventSelect 是 WinSock 提供的一种异步事件通知I/O模型,与 WSAAsyncSelect模型有些类似.       该模型同样是接收 FD_XXX 之类的网络事件,但是是通 ...

随机推荐

  1. LeetCode 319. Bulb Switcher

    There are n bulbs that are initially off. You first turn on all the bulbs. Then, you turn off every ...

  2. java 用hmac-sha1进行签名

    public static String getSignature(String s) throws NoSuchAlgorithmException, UnsupportedEncodingExce ...

  3. java练习 - 字符串反转

    思路: 1. 首先将字符串转换成数组,一个数组元素放一个字符. 2. 循环遍历字符串,将所有字符串前后字符调换位置,比如:第一个和最后一个调换,第二个和倒数第三调换,第三个和倒数第三调换,直到所有字符 ...

  4. python2到python3的转换以及f.write在python3 中的用法

    .利用Python内置(Python脚本)工具,帮你自动转换 Python 2.x版本,比如我安装的Python 2.7.2,其在windows下载安装好之后,就自带了相关的一些有用的工具. 其中一个 ...

  5. java变量初始化

    java全局变量会自动初始化,但局部变量不会自动初始化.当我们新建一个对象的时候,java会申请一个区域存放类的数据,而成员变量就是类的数据,也是放在这个内存区域中,jvm申请内存时初始化.而方法中变 ...

  6. 如何安装使用Impala

      一.Impala简介 Cloudera Impala对你存储在Apache Hadoop在HDFS,HBase的数据提供直接查询互动的SQL.除了像Hive使用相同的统一存储平台,Impala也使 ...

  7. pigcms 微外卖下单加数量bug

    bug:加数量的时候结算金钱出现NAN 先给一个简单粗暴的解决办法. 找到/tpl/static/dishout/js/main.js 把  65行 disPrice = parseFloat(sig ...

  8. 通过this获取当前点击选项相关数据

    很多时候jquery只能或者应该回去一个集合.然后通过this获取触发时间的对象及相关属性 this.jq('#needsType').on("click", ".sel ...

  9. s=a+aa+aaa+aaaa+aa...aaaa

    main(){ int a,n,count=1; long int sn=0,tn=0; cout<<"input a and n:"; cin>>a> ...

  10. iptables允许FTP

    在iptables里设置允许访问ftp(建立连接,数据传输) 由于ftp服务在建立连接和传输数据时,使用的时不同的端口,建立连接的端口20.21,数据传输的端口可以自定义: 修改ftp配置文件,指定用 ...