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

首先,初始化网络环境,创建一个监听的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. Asp.net底层机制

    Asp.net底层就是用户通过输入网址,然后请求IIs服务器的流程,在这个过程中有一个重要的部件就是ISAPI,这是一个底层的win32API,在扩展方面比较困难,多用于接口之间的桥接,.net和II ...

  2. sql 中如何将返回的记录某一条置顶

    将table1中id 为2的记录置顶select * from table1order by case when id='2' then 0 else 1 end 例子:将已发布的置顶,status  ...

  3. 表空间Tablespace

    SQL Fundamentals: 表的创建和管理(表的基本操作,闪回技术flashback,表结构修改) Oracle Schema Objects——Tables——TableStorage 数据 ...

  4. Fatal error in launcher: Unable to create process using '"c:\python37\python3.exe" "C:\Python37\Scripts\pip3.exe" install opencv-python'

    pip3.exe install opencv-python 报错: Fatal error in launcher: Unable to create process using '"c: ...

  5. Bean Life Cycle

    Bean生命周期 Spring Bean Life Cycle https://www.tutorialspoint.com/spring/spring_bean_life_cycle.htm The ...

  6. 【转】sql server 订阅发布、快照发布(一)

    原文链接:https://blog.csdn.net/tiandi_5000/article/details/11646023 SQL SERVER 2012 使用订阅发布同步数据库(一) 2013年 ...

  7. centos LB负载均衡集群 三种模式区别 LVS/NAT 配置 LVS/DR 配置 LVS/DR + keepalived配置 nginx ip_hash 实现长连接 LVS是四层LB 注意down掉网卡的方法 nginx效率没有LVS高 ipvsadm命令集 测试LVS方法 第三十三节课

    centos   LB负载均衡集群 三种模式区别 LVS/NAT 配置  LVS/DR 配置  LVS/DR + keepalived配置  nginx ip_hash 实现长连接  LVS是四层LB ...

  8. (转)帮你深入理解OAuth2.0协议

    1. 引言 如果你开车去酒店赴宴,你经常会苦于找不到停车位而耽误很多时间.是否有好办法可以避免这个问题呢?有的,听说有一些豪车的车主就不担心这个问题.豪车一般配备两种钥匙:主钥匙和泊车钥匙.当你到酒店 ...

  9. 模块讲解----subprocess模块

    历史 #输出结果到屏幕上,并不返回执行状态os.system('dir')#保存命令的执行结果输出ret = os.popen('dir').read() 问题:上面2条是把命令结果保存下来了,但是返 ...

  10. soapUI-DataSource

    1.1.1.1 概述 - 数据源   Option Description   Properties DataSource属性表   Toolbar DataSource工具栏   Configura ...