windows下的IO模型之事件选择(WSAEventSelect)模型
异步选择模型类似的是,它也允许应用程序在一个或多个套接字上,接收以事件为基础的网络事件通知。对于异步选择模型采用的网络事件来说,它们均可原封不动地移植到事件选择模型。事件选择模型和异步选择模型最主要的差别在于网络事件会投递至一个事件对象,而非投递至一个窗口。
首先,初始化网络环境,创建一个监听的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)模型的更多相关文章
- 三.Windows I/O模型之事件选择(WSAEventSelect )模型
1.事件选择模型:和异步选择模型类似的是,它也允许应用程序在一个或多个套接字上,接收以事件为基础的网络事件通知.对于异步选择模型采用的网络事件来说,它们均可原封不动地移植到事件选择模型.事件选择模型和 ...
- windows下的IO模型之异步选择(WSAAsyncSelect)模型
异步选择(WSAAsyncSelect)模型是一个有用的异步I/O 模型.其核心函数是WSAAsyncSelect,该函数是非阻塞的 (关于异步io的理解详情可以看:http://www.cnblog ...
- windows下的IO模型之选择(select)模型
1.选择(select)模型:选择模型:通过一个fd_set集合管理套接字,在满足套接字需求后,通知套接字.让套接字进行工作. 选择模型的核心是FD_SET集合和select函数.通过该函数,我们可以 ...
- 机器学习模型从windows下 spring上传到预发布会导致模型不可加载
1.通过上传到redis,程序通过redis拉取模型,解决问题. 2.问题原因初步思考为windows下模型文件上传到 linux导致,待继续跟进查找.
- windows下的IO模型之完成端口
本文整理于:http://blog.csdn.net/piggyxp/article/details/6922277 一. 完成端口的优点 完成端口会充分利用Windows内核来进行I/O的调度,是用 ...
- 二.Windows I/O模型之异步选择(WSAAsyncSelect)模型
1.基于windows消息为基础的网络事件io模型.因此我们必须要在窗口程序中使用该模型.该模型中的核心是调用WSAAsyncSelect函数实现异步I/O. 2.WSAAsyncSelect函数:注 ...
- WinSock WSAEventSelect 模型总结
前言 本文配套代码:https://github.com/TTGuoying/WSAEventSelect-model 由于篇幅原因,本文假设你已经熟悉了利用Socket进行TCP/IP编程的基本原理 ...
- windows下C语言调用系统文件选择对话框
代码片段,在windows下用C语言调用文件选择对话框,以备忘 #define DEFAULT_DIR "" char extraction_path[MAX_PATH] = DE ...
- windows下几种I/O端口(了解)
如果你想在Windows平台上构建服务器应用,那么I/O模型是你必须考虑的.Windows操作系统提供了选择(Select).异步选择(WSAAsyncSelect).事件选择(WSAEventSel ...
随机推荐
- MegaCli 监控raid状态 限戴尔服务器
MegaCli 监控raid状态 MegaCli是一款管理维护硬件RAID软件,可以通过它来了解当前raid卡的所有信息,包括 raid卡的型号,raid的阵列类型,raid 上各磁盘状态,等等.通常 ...
- linux内存管理之vmalloc函数分析
2017-07-09 今天周末,闲来无事聊聊linux内核内存分配那点事……重点在于分析vmalloc的执行 流程 以传统x86架构为例,内核空间内存(3G-4G)主要分为三大部分:DMA映射区,一致 ...
- 纯css3加载动画
<!DOCTYPE html><html> <head> <meta charset="utf-8"> <meta name= ...
- EdrawSoft Edraw Max 9.1安装破解
1,安装软件[不要运行软件] 2,断网 3,打开Crack文件夹,复制”BaseCore.dll”,”ObjectModule.dll”到软件安装目录下替换原文件 默认的安装路径C:\Program ...
- hadoop中map和reduce的数量设置
hadoop中map和reduce的数量设置,有以下几种方式来设置 一.mapred-default.xml 这个文件包含主要的你的站点定制的Hadoop.尽管文件名以mapred开头,通过它可以控制 ...
- Android初体验之Monkey和MonkeyRunner
原文地址https://blog.csdn.net/mad1989/article/details/38087737 Monkey 什么是Monkey Monkey是Android中的一个命令行工具, ...
- Java多态 父类引用指向子类对象
Java多态的三个必要条件: 1. 继承 2. 子类重写父类方法 3. 父类引用指向子类对象 然后看一个例子 输出结果为: 给出结论:Father c = new Child() 在c的 ...
- cocos-lua基础学习(七)Scene类学习笔记
local scene = cc.Scene:create() cc.Director:getInstance():replaceScene( scene ) cc.Director:getInsta ...
- python uwsgi报错epoll_ctl(): Bad file descriptor
今天安装了uwsgi+supervisord+nginx,一直访问不了 直接启动uwsgi使用nginx访问,查看有大量报错:epoll_ctl(): Bad file descriptor [cor ...
- javascript模式(1)--私有成员
javascript是基于对象的一门语言,没有想java等语言那样子拥有封装的特性.但是javascript可以通过闭包来进行模拟. 1.构造函数与私有成员 可以用构造函数形成一个闭包,实现内部成员的 ...