异步选择模型类似的是,它也允许应用程序在一个或多个套接字上,接收以事件为基础的网络事件通知。对于异步选择模型采用的网络事件来说,它们均可原封不动地移植到事件选择模型。事件选择模型和异步选择模型最主要的差别在于网络事件会投递至一个事件对象,而非投递至一个窗口。

首先,初始化网络环境,创建一个监听的socket,然后进行bind,listen操作。接下来我们会创建一个网络事件对象,它和我们讲内核态下线程同步里边事件对象很类似,我们可以调用WSACreateEvent来创建它,其声明如下:

WSAEVENT  WSACreateEvent (void);

然后我们再调用WSAEventSelect,来将监听的socket与该事件进行一个关联,其声明如下:

int WSAEventSelect(
SOCKET s, //套接字
WSAEVENT hEventObject, //网络事件对象
long lNetworkEvents //需要关注的事件
);

  因为我们这里是将监听的socket与事件对象进行关联,因此我们只需要关注两个事件,一个是客户端的连接,一个是socket关闭这两个事件。

由于是一个阻塞的过程,所以要创建一个线程在线程内调用它。而如果要同时处理多个客户端,我们可以用线程池的思想。(示例代码可以用线程池来理解)

最后将我们监听的socket和我们创建的那个网络事件对象保存到各自的全局数组里边去。这时候我们的主线程就结束了。

然后我们来看下我们的工作者线程做了哪些工作。在工作者线程里边,会有一个死循环,在循环刚开始的时候,会调用WSAWaitForMultipleEvents函数,来查看我们那个全局事件对象数组里边是否至少有一个有信号到来,其声明如下:

如果该函数执行成功,就返回一个索引值,这个值代表了那个有信号的事件对象的索引值。
我们在主线程里边关注了两个事件,一个是客户端的连接,一个是关闭事件,那我们如何知道出现了哪个网络事件呢,我需要调用WSAEnumNetworkEvents,来检测指定的socket上的网络事件。其声明如下:

int WSAEnumNetworkEvents
(
SOCKET s, //指定的socket
WSAEVENT hEventObject, //事件对象
LPWSANETWORKEVENTS lpNetworkEvents //WSANETWORKEVENTS<span style="font-family:Arial, Helvetica, sans-serif;">结构地址</span>
);

当我们调用这个函数成功后,它会将我们指定的socket和事件对象所关联的网络事件的信息保存到WSANETWORKEVENTS这个结构体里边去,我们来看下这个结构体的声明:

  根据这个结构体我们就可以判断是否是我们所关注的网络事件已经发生了。如果是我们那个客户端连接的事件发生了,我们就调用accept函数将客户端和服务端进行连接。连接完成后,我们再次创建一个网络事件对象,然后继续调用WSAEventSelect函数,将这个事件对象和客户端的那个socket进行一个关联,此时我们需要关注的网络事件,数据的读和写,还有关闭这三个事件。然后我们就把客户端的socket和新建的事件对象保存到各自的全局数组里边去。

  如果是我们的读的网络事件发生了,那么我们就调用recv函数进行操作。如果是写的网络事件发生了,我们就可以做一些日志等操作。若是关闭的事件发生了,就调用closesocket将socket关掉,在数组里将其置零等操作。

  最后,在提一下另一个网络模型---异步选择模型,这个模型呢它和我们的事件选择模型很像,不过它是基于windows消息的,这就说明了我们只能在窗口程序里边来使用,而事件选择是以事件对象为基础,它不管是控制台还是窗口程序都可以使用,因此用的较多的也是我们事件选择模型。有兴趣的可以去了解一下异步选择模型。

以下是EventSelect网络模型的示例代码:

#include <winsock2.h>
#include <stdio.h>
#define PORT 6000
#pragma comment (lib, "Ws2_32.lib")
SOCKET ArrSocket[64] = { 0 };
WSAEVENT ArrEvent[64] = { 0 };
DWORD dwTotal = 0;
DWORD dwIndex = 0;
BOOL WinSockInit()
{
WSADATA data = { 0 };
if (WSAStartup(MAKEWORD(2, 2), &data))
return FALSE;
if (LOBYTE(data.wVersion) != 2 || HIBYTE(data.wVersion) != 2){
WSACleanup();
return FALSE;
}
return TRUE;
} DWORD WINAPI WorkThreadProc(LPARAM lparam)
{
char buf[1024] = { 0 };
//用于ACCEPT临时使用的SOCKET
SOCKET sockClient = INVALID_SOCKET;
WSANETWORKEVENTS NetWorkEvent = { 0 };
while (TRUE)
{
//数组内任意一个WSAEVENT有信号了,返回对应的索引值
dwIndex = WSAWaitForMultipleEvents(dwTotal, ArrEvent, FALSE, 100, FALSE);
if (dwIndex == WSA_WAIT_TIMEOUT) {
continue;
}
//检测指定的socket的网络事件的发生
WSAEnumNetworkEvents(ArrSocket[dwIndex - WSA_WAIT_EVENT_0], ArrEvent[dwIndex - WSA_WAIT_EVENT_0], &NetWorkEvent);//调用完成后NetWorkEvent保存了网络事件及一些标志位
//如果第3位数据是1,代表有客户端进行连接
if (NetWorkEvent.lNetworkEvents & FD_ACCEPT)
{
//如果出错了,就跳过
if (NetWorkEvent.iErrorCode[FD_ACCEPT_BIT] != 0)
{
continue;
}
sockClient = accept(ArrSocket[dwIndex - WSA_WAIT_EVENT_0], NULL, NULL);
if (sockClient == INVALID_SOCKET)
continue;
//连接完成后,将客户端的SOCKET保存到数据,同时新建EVENT与SOCKET建立关系
WSAEVENT newEvent = WSACreateEvent();
WSAEventSelect(sockClient, newEvent, FD_READ | FD_WRITE | FD_CLOSE); ArrSocket[dwTotal] = sockClient;
ArrEvent[dwTotal] = newEvent;
++dwTotal;
} if (NetWorkEvent.lNetworkEvents & FD_READ)
{
if (NetWorkEvent.iErrorCode[FD_READ_BIT] != 0)
{
continue;
}
int len = recv(ArrSocket[dwIndex - WSA_WAIT_EVENT_0], buf, sizeof(buf), 0);
printf("Recv: %s\n", buf);
send(ArrSocket[dwIndex - WSA_WAIT_EVENT_0], buf, strlen(buf), 0);
} if (NetWorkEvent.lNetworkEvents & FD_WRITE)
{
if (NetWorkEvent.iErrorCode[FD_WRITE_BIT] != 0)
{
continue;
}
printf("Send something\n");
} if (NetWorkEvent.lNetworkEvents & FD_CLOSE)
{
if (NetWorkEvent.iErrorCode[FD_CLOSE_BIT] != 0)
{
continue;
}
closesocket(ArrSocket[dwIndex - WSA_WAIT_EVENT_0]);
ArrSocket[dwIndex - WSA_WAIT_EVENT_0] = 0;
}
}
}
int main()
{
//初始化环境
WinSockInit();
SOCKET sockListen = INVALID_SOCKET;
sockListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockListen == INVALID_SOCKET)
{
printf("Create socket error");
return -1;
}
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.S_un.S_addr = INADDR_ANY;
service.sin_port = htons(PORT);
//绑定
if (bind(sockListen, (sockaddr*)&service, sizeof(service)) == SOCKET_ERROR)
{
printf("bind failed\n");
return -1;
}
//监听
if (listen(sockListen, SOMAXCONN) == SOCKET_ERROR) {
printf("listen error\n");
return -1;
} //创建一个网络事件对象
WSAEVENT ListenEvent = WSACreateEvent();
// 把WSAEVENT与一个SOCKET进行关联,告诉关联的对象需要关注的事件有哪些
WSAEventSelect(sockListen, ListenEvent, FD_ACCEPT | FD_CLOSE);
//创建一个子进程,用子进程来处理所有的SOCKET上面的事件
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)WorkThreadProc, NULL, NULL, NULL);
//保存到全局数组
ArrSocket[dwTotal] = sockListen;
ArrEvent[dwTotal] = ListenEvent;
++dwTotal; system("pause");
if (sockListen != INVALID_SOCKET)
{
closesocket(sockListen);
}
WSACleanup();
return 0;
}

  本文转载于:http://blog.csdn.net/timmiy/article/details/52167022

windows下的IO模型之事件选择(WSAEventSelect)模型的更多相关文章

  1. 三.Windows I/O模型之事件选择(WSAEventSelect )模型

    1.事件选择模型:和异步选择模型类似的是,它也允许应用程序在一个或多个套接字上,接收以事件为基础的网络事件通知.对于异步选择模型采用的网络事件来说,它们均可原封不动地移植到事件选择模型.事件选择模型和 ...

  2. windows下的IO模型之异步选择(WSAAsyncSelect)模型

    异步选择(WSAAsyncSelect)模型是一个有用的异步I/O 模型.其核心函数是WSAAsyncSelect,该函数是非阻塞的 (关于异步io的理解详情可以看:http://www.cnblog ...

  3. windows下的IO模型之选择(select)模型

    1.选择(select)模型:选择模型:通过一个fd_set集合管理套接字,在满足套接字需求后,通知套接字.让套接字进行工作. 选择模型的核心是FD_SET集合和select函数.通过该函数,我们可以 ...

  4. 机器学习模型从windows下 spring上传到预发布会导致模型不可加载

    1.通过上传到redis,程序通过redis拉取模型,解决问题. 2.问题原因初步思考为windows下模型文件上传到 linux导致,待继续跟进查找.

  5. windows下的IO模型之完成端口

    本文整理于:http://blog.csdn.net/piggyxp/article/details/6922277 一. 完成端口的优点 完成端口会充分利用Windows内核来进行I/O的调度,是用 ...

  6. 二.Windows I/O模型之异步选择(WSAAsyncSelect)模型

    1.基于windows消息为基础的网络事件io模型.因此我们必须要在窗口程序中使用该模型.该模型中的核心是调用WSAAsyncSelect函数实现异步I/O. 2.WSAAsyncSelect函数:注 ...

  7. WinSock WSAEventSelect 模型总结

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

  8. windows下C语言调用系统文件选择对话框

    代码片段,在windows下用C语言调用文件选择对话框,以备忘 #define DEFAULT_DIR "" char extraction_path[MAX_PATH] = DE ...

  9. windows下几种I/O端口(了解)

    如果你想在Windows平台上构建服务器应用,那么I/O模型是你必须考虑的.Windows操作系统提供了选择(Select).异步选择(WSAAsyncSelect).事件选择(WSAEventSel ...

随机推荐

  1. html lang="zh-cn"解决Mac版Firefox中文字体显示问题

    这两天在Mac下被Firefox的中文字体显示问题所困扰.在Firefox中将Sans-serif字体设置为SimSun-ExtB(新宋体)或英文字体(这时会用Mac默认中文字体),如下图: 浏览园子 ...

  2. Kafka集群部署 (守护进程启动)

    1.Kafka集群部署 1.1集群部署的基本流程 下载安装包.解压安装包.修改配置文件.分发安装包.启动集群 1.2集群部署的基础环境准备 安装前的准备工作(zk集群已经部署完毕)  关闭防火墙 c ...

  3. day10(java web之request&respone&访问路径&编码问题)

    day10 请求响应流程图 response response概述 response是Servlet.service方法的一个参数,类型为javax.servlet.http.HttpServletR ...

  4. C++ vector错误(1)

    在用C++的vector的时候,要保证访问的下标不能超过vector的size.否则出现msvcp60.dll 访问禁止.

  5. NPOI+反射+自定义特性实现上传excel转List及验证

    1.自定义特性 [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public ...

  6. 2017-2018-2 20165207实验二《Java面向对象程序设计》实验报告

    2017-2018-2 20165207实验二<Java面向对象程序设计>实验报告 课程:Java程序设计 班级:1652 姓名:李天林 学号:20165207 实验日期:2018年4月1 ...

  7. 解决[Xcodeproj] Unknown object version错误

    错误描述: RuntimeError - [Xcodeproj] Unknown object version. /Library/Ruby/Gems/2.0.0/gems/xcodeproj-0.2 ...

  8. Python 读取写入配置文件 ConfigParser

    https://blog.csdn.net/piaodexin/article/details/77371343 https://www.cnblogs.com/feeland/p/4502931.h ...

  9. Windows Server 2008 R2 web服务器发布在线系统时遇到的问题

    1  HTTP 错误 404.2 - Not Found,由于 Web  服务器上的“ISAPI 和 CGI 限制”列表设置,无法提供您请求的页面(如下图) 打开 Internet 信息服务(IIS) ...

  10. 20145325张梓靖 实验四 "Andoid开发基础"

    20145325张梓靖 实验四 "Andoid开发基础" 实验内容 使用 Android Studio 设计"Hello" 设计过程 创建项目 选择.xml中的 ...