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.
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。
3 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模型的更多相关文章
- winsock编程IOCP模型实现代码
winsock编程IOCP模型实现代码 话不多说,上代码.借鉴<windows核心编程>部分源码和CSDN小猪部分代码. stdafx.h依赖头文件: #include <iostr ...
- winsock编程WSAAsyncSelect模型
winsock编程WSAAsyncSelect模型 WSAAsyncSelect模型也称异步选择模型,其核心函数是WSAAsyncSelect.它可以用来在一个socket上接收以windows消息为 ...
- winsock编程select模型
winsock编程select模型 网络服务端连接数量过多时,为每一个连接申请一个线程会让机器性能急剧下降(大多说是因为线程在用户态和内核态之间切换会占用大量的CPU时间片).为了解决多线程带来的性能 ...
- WSAEventSelect模型编程 详解
转自:http://blog.csdn.net/wangjieest/article/details/7042108 WSAEventSelect模型编程 WSAEventSelect模型编程这个模型 ...
- WinSock WSAEventSelect 模型总结
前言 本文配套代码:https://github.com/TTGuoying/WSAEventSelect-model 由于篇幅原因,本文假设你已经熟悉了利用Socket进行TCP/IP编程的基本原理 ...
- WinSock WSAEventSelect 模型
在前面我们说了WSAAsyncSelect 模型,它相比于select模型来说提供了这样一种机制:当发生对应的IO通知时会立即通知操作系统,并调用对应的处理函数,它解决了调用send和 recv的时机 ...
- Winsock编程基础介绍 .
相信很多人都对网络编程感兴趣,下面我们就来介绍,在网络编程中应用最广泛的编程接口Winsock API. 使用Winsock API的编程,应该了解一些TCP/IP的基础知识.虽然你可以直接使用Win ...
- WinSock 重叠IO模型
title: WinSock 重叠IO模型 tags: [WinSock 模型, 网络编程, 重叠IO模型] date: 2018-06-29 20:26:13 categories: Windows ...
- WSAEventSelect模型详解
WSAEventSelect 是 WinSock 提供的一种异步事件通知I/O模型,与 WSAAsyncSelect模型有些类似. 该模型同样是接收 FD_XXX 之类的网络事件,但是是通 ...
随机推荐
- C# 语言规范_版本5.0 (第8章 语句)
1. 语句 C# 提供各种语句.使用过 C 和 C++ 编程的开发人员熟悉其中大多数语句. statement: labeled-statement declaration-statement emb ...
- 自动化辅助工具Firebug和Firepath的安装
1.安装firefox浏览器,点击主菜单,选择“附加组件” 2.搜索Firebug和firepath点击安装 3.安装后点击浏览器的主菜单-web开发者-firebug即可打开 4.或者在页面右键选择 ...
- Android Studio Gradle更新的解决办法
环境: Win7_64旗舰版.Android Studio2.1.1 问题描述: 在打开Android Studio项目的时候,会下载项目对应版本的gradle,由于是国外网站导致更新较慢,该版本是在 ...
- php 编程效率(3)
提高php编程效率的53个小知识点:用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中 搜寻变量,单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符串当 ...
- input 和raw_input
---恢复内容开始--- 因为看python2 和 3 混了,所以两者里面的东西有点乱input 和raw_input 今天终于搞明白了,在这里记录一下: 1.python 2 中raw_input ...
- 利用StringBuffer向字符串特定的重复子字符串插入数据
public class InsertDetail { public void insertInvoiceDetail(StringBuffer sb, String Label, String ...
- KVM guest caching modes
kvm中host和guest各自维护自己的page caches,使得内存中有两份缓存数据.host的缓存为page cache可以理解为读缓存,guest的缓存为disk write cache,可 ...
- 【ORACLE】“System.Exception: System.Data.OracleClient 需要 Oracle 客户端软件 version 8.1.7 或更高版本。”解决办法
我的电脑是win10.64位.问题如题,在网上找了很多办法都没搞好,如下: 1.给oracle目录设置“Authenticated Users”用户的“读取/写入”权限 2.IIS网站物理路径凭据添加 ...
- 初学者必知的HTML规范
一.整体结构 用div代替table布局 结构.表现.行为三者分离,避免内联 良好的树形结构四个空格代替一个tab 能并列就不嵌套<div></div><div>& ...
- Swift逃逸闭包之见解
Swift 逃匿闭包顾名思义,就是闭包想要逃跑.当闭包作为参数传给一个方法时,在这个方法被调用完后闭包却还没有被执行,而是等到方法执行完后才调用 基本都是跨线程的时候才会有逃逸闭包这个说法.因为异步 ...