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

首先,初始化网络环境,创建一个监听的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. 沈阳网络赛F-Fantastic Graph【贪心】or【网络流】

    "Oh, There is a bipartite graph.""Make it Fantastic." X wants to check whether a ...

  2. cocos2d动作

    1.动作的基类是CCAction,通过继承它可以实现很多不同的动作,主要分为三大类: (1)CCFiniteTimeAction(有限次动作执行类) (2)CCSpeed(节点执行速度类) (3)CC ...

  3. centos shell脚本编程2 if 判断 case判断 shell脚本中的循环 for while shell中的函数 break continue test 命令 第三十六节课

    centos  shell脚本编程2 if 判断  case判断   shell脚本中的循环  for   while   shell中的函数  break  continue  test 命令   ...

  4. centos Linux下磁盘管理 parted,df ,du,fdisk,partprobe,mkfs.ext4,mount,/etc/fstab,fsck,e2fsck,mk2efs,tmpfs ,nr_inodes, LVM,传统方式扩容文件系统 第七节课

    centos Linux下磁盘管理   parted,df ,du,fdisk,partprobe,mkfs.ext4,mount,/etc/fstab,fsck,e2fsck,mk2efs,tmpf ...

  5. java-基础-【三】try/catch/finally

    原文地址: https://my.oschina.net/bieber/blog/703251 一.单层的try/catch public int test(int a,int b){ try{ re ...

  6. 解决下载ftp文件过程中,浏览器直接解析文件(txt,png等)的问题

    搭建了一个ftp服务器,供用户进行上传下载,在下载过程中发现,一些文件,例如txt,jpg,png,pdf等直接被浏览器解析了.在浏览器中显示其内容,没有下载. 下面通过网上查询得到一些解决方法: 最 ...

  7. Jenkins的持续集成

    持续集成:不需要人工干预,持久化.重复的运行一个任务.将代码自动的更新到最新,然后自动运行. 新建项目之前要再Jenkins的全局工具配置里面把git的路径设置好.[全局工具配置]-->[Git ...

  8. gcc报错 can not be used when making a shared object; recompile with -fPIC

    使用google protobuf时,出现错误 /usr/bin/ld: /usr/local/lib/libprotobuf.a(message_lite.o): relocation R_X86_ ...

  9. PHP实现返回JSON和XML的类分享

    PHP实现返回JSON和XML的类分享 <?php     class Reponse{         //private $result = array('code'=null,'messa ...

  10. addslashes — 使用反斜线引用字符串

    返回字符串,该字符串为了数据库查询语句等的需要在某些字符前加上了反斜线.这些字符是单引号(').双引号(").反斜线(\)与 NUL( NULL 字符). 一个使用 addslashes() ...