Windows socket之WSAEventSelect模型_wsaeventselect模型的socket编程和客户端应用的功能

devtools/2025/2/3 12:22:21/
   WSAEVENT hEvent,LPWSANETWORKEVENTS lpNetworkEvents);
该函数可以查找发生在套接字上的网络事件,并清除系统内部的网络事件记录,重置事件对象。s为发生网络事件的套接字句柄。hEvent为被重置的事件对象句柄(可选)。lpNetworkEvents为指向WSANETWORKEVENTS结构指针。如果hEvent不为NULL,则该事件被重置。如果为NULL,需要调用WSAResetEvent函数设置事件为非触发状态。该结构中包含发生网络事件的记录和相关错误代码。调用成功返回0,否则为SOCKETS\_ERROR。WSANETWORKEVENTS结构如下:

typedef struct _WSANETWORKEVENTS

{

long lNetworkEvents,

int iErrorCode[FD_MAX_EVENTS];

}WSANETWORKEVENTS,*LPWSANETWORKEVENTS;

lNetworkEvents指示发生的网络事件。一个对象再变为触发态时,可能在套接字上发生了多个网络事件。iErrorCode为包含网络事件错误代码的数组。错误代码与lNetworkEvents字段中的网络事件对应。在应用程序中,使用网络事件事件错误标识符对iErrorCode数组进行索引,检查是否发生了网络错误。这些标识符的命名规则是对应的网络事件后面添加\_BIT.例如,对于FD\_READ事件的网络事件错误标识符为FD\_READ\_BIT。下面的代码演示了,如何判断FD\_READ网络事件的发生:

SOCKET s;

WSAEVENT hNetworkEvent;

WSANETWORKEVENT networkEvents;

if(0==WSAEnumNetworkEvents(h,hNetworkEvent,&networkEvents);

{

 //发生FD_WRITE网络事件。if(networkEvents.lNetworkEvents&FD_READ)

{

    if(0==networkEvent.iErrorCode[FD_READ_BIT]){//接收数据。}else{//获取错误代码。int nErrorCode=networkEvents.iErrorCode[FD_READ_BIT];//处理错误。}}

}

本例演示利用WSAEventSelect模型开发一个服务器应用程序的步骤。主要步骤:程序开始时会创建监听套接字,利用WSAEventSelect函数为套接字注册FD\_ACCEPT和FD\_CLOSE事件,然后套接字进入监听状态。在while循环内,循环调用WSAWaitForMultipleEvents函数等待网络事件的发生,当网络事件发生时函数返回,并通过该函数的返回值得到发生网络事件的套接字。调用WSAEnumNetworkEvents函数检查在该套接字上到底发生什么网络事件。如果发生FD\_ACCEPT网络事件,则调用accept函数接受客户端连接。将该套接字加入套接字数组。创建事件对象并加入事件数组。事件对象数量加一。然后调用WSAEventSelect函数为该套接字关联事件对象,注册FD\_READ,FD\_WRITE和FD\_CLOSE网络事件。如果发生FD\_READ网络事件,则调用recv函数接收数据。如果发生FD\_WRITE网络事件,则调用send函数发送数据。如果发生FD\_CLOSE网络事件,将该套接字从套接字数组清除,同时将对应事件从事件数组删除。事件对象数量减一,并关闭该套接字。在应用程序中,对发生的每种网络事件,都首先判断是否发生了网络错误。如果发生错误,则服务器退出。步骤一:定义事件对象数组和套接字数组。这两个数组的最大程度为WSA\_MAXIMUM\_WAIT\_EVENTS。这两个数组的成员存在一一对应关系。DWORD totalEvent;//事件对象数量。WSAEVENT eventArray[WSA\_MAXIMUM\_WAIT\_EVENTS];SOCKETS socketArray[WSA\_MAXIMUM\_WAIT\_EVENTS];步骤二:创建套接字:

WSADATA wsaData;

WSAStartup(MAKEWORD(2,2),&wsaData);

if((sListen==socket(AF_INET,SOCK_STREAM,0))

   {//创建失败。

}

步骤三:为监听套接字注册网络事件:

if(eventArray[totalEvent]=WSACreateEvent()==WSA_INVALID_EVENT)

{

 //调用失败。}

//为监听套接字注册FD_READ,和FD_CLOSE网络事件。

if(WSAEventSelect(sListen,eventArray[totalEvent],FD_ACCEPT|FD_CLOSE)==SOCKETS_ERROR)

{

 //调用失败。

}

步骤四:开始监听:

sockaddr_in addr;

addr.sin_family=AF_INET;

addr.sin_addr.S_addr=htons(INADDR_ANY);

addr.sin_port=htons(4000);

if(bind(sListen,(SOCKADDR*)&addr,sizeof(addr))==SOCKETS_ERROR)

{

//绑定失败。

}

if(!listen(sListen,10))

{

//监听失败。

}

步骤五:等待网络事件。

while(true)

{

if(dwIndex=WSAWaitForMultipleEvents(totalEvent,eventArray,

          false,WSA_INFINITE,false)==WSA_WAIT_FAILED)

{

   //等待失败。

}

步骤六:获取发生的网络事件。当网络事件发生时WSAWaitForMultipleEvents函数返回。调用WSAEnumNetworkEvents函数获取发生在套接字上的网络事件。socketArray[dwIndex-WSA\_WAIT\_EVENT\_0]为当前发生网络事件的套接字。eventArray[dwIndex-WSA\_WAIT\_EVENT\_0]为当前被投递网络事件的事件对象。当函数返回时networkEvents变量中保存了网络事件的记录。同时事件对象的工作状态,由未触发态变为触发态。WSANETWORKEVENTS networkEvents;if(WSAEnumNetworkEvents(socketArray[dwIndex-WSA\_WAIT\_EVENT\_0],eventArray[dwIndex-WSA\_WAIT\_EVENT\_0],&networkEvents)==SOCKETS\_ERROR){//调用失败。}步骤七:判断是否是各网络事件发生。WSAEnumNetworkEvents函数返回时,首先检查是否发生了FD\_ACCEPT网络事件。如果该网络事件发生,则说明此时客户端的连接请求被等待。检查是否发生了网络错误,如果没有错误发生,则执行下面的步骤:1:调用accept接受客户端请求。2:判断当前套接字数量是否超过了最大值。如果超过则关闭该套接字。3:将客户端套接字介入套接字数组。4:创建套接字事件对象,并将该事件对象加入事件对象数组。5:为该套接字注册FD\_READ,FD\_WRITE和FD\_CLOSE网络事件。6:事件对象数量加一,将该套接字加入管理客户端套接字链表中。使用WSAEventSelect应该注意的问题。1:如果在一个套接字上多次调用WSAEventSelect函数,那么最后一次函数调用将会取消前一次的调用效果。2:一个套接字不要关联多个事件对象 。在一个套接字上为不同网络事件注册不同的事件对象是不可能的。一个套接字关联一个对象,当该对象被触发时,获得对应的套接字,然后调用WSAEnumNetworkEvents来获得发生在此套节字上的事件。3:如果要取消事件对象与网络事件的关联,以及为套接字注册的网络事件。应用程序可以在调用WSAEventSelect时将lNetworkEvent设置为0。另外,调用closesocket关闭套接字时,也会取消这种关联和为套接字注册的网络事件。4:调用accept接受的套接字与监听套接字具有同样的属性。如:在创建监听套接字时为其设置感兴趣的网络事件为FD\_ACCEPT和FD\_CLOSE。那么accept返回的套接字同样具有这些属性。它与监听套接字感兴趣的网络事件相同且使用同一个事件对象。一般情况下,我们都会为新套接字重新调用WSAEventSelect。后面的代码中在accept后会新套接字调用WSAEventSelect函数就不足为奇了!!5:接收FD\_CLOSE网络事件时,错误代码指出套接字是从容关闭还是硬关闭。如果错误代码为0,则为从容关闭;若错误代码为WSAECONNRESET错误,则是硬关闭。当应用程序接收到该网络事件时,说明对方在该套接字上执行了shutdown或者是closesocket函数调用。WSAEventSelect模型的优势和不足。WSAEventSelect模型的优势是可以应用在一个非窗口的Windows sockets程序中,实现对多个套接字的管理。不足是:每个WSAEventSelect模型最多只能管理64个套接字。当应用程序需要管理多于64个套接字时,就需要额外创建线程。由于该模型需要调用多个函数,这增加了开发的难度。以下为详细代码:

#include

#include<windows.h>

#include"winsock2.h"

SOCKET sListen;

#pragma comment(lib,“WS2_32.lib”)

#define MAX_NUM_SOCKET 20

u_int totalEvent=0;

//构造事件对象数组和套接字数组。

WSAEVENT eventArray[MAX_NUM_SOCKET];

SOCKET socketArray[MAX_NUM_SOCKET];

bool InitSocket()

{

WSAData wsa;WSAStartup(MAKEWORD(2,2),&wsa);sListen=socket(AF_INET,SOCK_STREAM,0);if(sListen==INVALID_SOCKET){return false;}WSAEVENT hEvent=WSACreateEvent();eventArray[totalEvent]=hEvent;//可用事件加一。totalEvent++;int ret=WSAEventSelect(sListen,hEvent,FD_CLOSE|FD_ACCEPT);//监听套接字只能收到这两种消息。if(!ret){return false;}sockaddr_in addr;addr.sin_addr.S_un=inet_addr("192.168.1.100");addr.sin_family=AF_INET;addr.sin_port=htons(4000);ret=bind(sListen,(SOCKADDR*)&addr,sizeof(addr));if(ret==SOCKET_ERROR){return false;}ret=listen(sListen,10);if(SOCKET_ERROR==ret){return false;}return true;

}

int main(int argc,char**argv)

{

InitSocket();while(true){//有一个事件被触发等待函数即返回。int dwIndex=WSAWaitForMultipleEvents(totalEvent,eventArray,false,WSA_INFINITE,false);if(dwIndex==WSA_WAIT_FAILED){break;}else {//有网络事件发生。WSANETWORKEVENTS wsanetwork;SOCKET s=socketArray[dwIndex-WSA_WAIT_EVENT_0];//传hEventObject为被触发的套接字,WSAEnumNetworkEvents函数,会将其设置为非触发态。无需手工设置。int ret=WSAEnumNetworkEvents(s,eventArray[dwIndex-WSA_WAIT_EVENT_0],&wsanetwork);if(ret==SOCKET_ERROR)//函数调用失败。{break;}//发生FD_ACCEPT网络事件。else if(wsanetwork.lNetworkEvents&FD_ACCEPT){if(wsanetwork.iErrorCode[FD_ACCEPT_BIT]!=0)//发生网络错误。{break;}else //接受连接请求。{SOCKET sAccept;if((sAccept=accept(socketArray[dwIndex-WSA_WAIT_EVENT_0],NULL,NULL))==INVALID_SOCKET){break;}//超过最大值。if(totalEvent>WSA_MAXIMUM_WAIT_EVENTS){closesocket(sAccept);break;}//将新接受的套接字加入套接字数组。socketArray[totalEvent]=sAccept;//创建套接字事件对象。if((eventArray[totalEvent]=WSACreateEvent())==WSA_INVALID_EVENT){break;}//为新接受的套接字重新注册网络事件,重新关联事件对象。不使用与监听套接字同样的属性,这点要注意!!!!。if(WSAEventSelect(sAccept,eventArray[totalEvent],FD_READ|FD_WRITE|FD_CLOSE)==SOCKET_ERROR)//接受的套接字,用于收发数据。{break;}totalEvent++;//总数加一。//将套接字加入链表。}}//发生FD_CLOSE网络事件。else if(wsanetwork.lNetworkEvents&FD_CLOSE){if(wsanetwork.iErrorCode[FD_CLOSE_BIT]!=0)//发生网络错误。{break;}else //连接关闭。{//删除链表中的该套接字。//关闭网络事件对象。WSACloseEvent(eventArray[dwIndex-WSA_WAIT_EVENT_0]);//将此套节字和事件对象从数组中清除。for(int i=dwIndex-WSA_WAIT_EVENT_0;i<totalEvent-1;i++){eventArray[i]=eventArray[i+1];socketArray[i]=socketArray[i+1];}totalEvent--;//总数减一。}}//发生FD_READ网络事件。else if(wsanetwork.lNetworkEvents&FD_READ){if(wsanetwork.iErrorCode[FD_READ_BIT]!=0)//发生网络错误。{break;}else //套接字可读。{//接收数据。}}//发生FD_WRITE网络事件。else if(wsanetwork.lNetworkEvents&FD_WRITE){if(wsanetwork.iErrorCode[FD_WRITE_BIT]!=0)//发生网络错误。{break;}else //套接字可写。{//发送数据。}

http://www.ppmy.cn/devtools/155714.html

相关文章

【回溯+剪枝】回溯算法的概念 全排列问题

文章目录 46. 全排列Ⅰ. 什么是回溯算法❓❓❓Ⅱ. 回溯算法的应用1、组合问题2、排列问题3、子集问题 Ⅲ. 解题思路&#xff1a;回溯 剪枝 46. 全排列 46. 全排列 ​ 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 …

LeetCode 344: 反转字符串

LeetCode 344: 反转字符串 - C语言题解 这道题的目标是反转一个字符数组&#xff08;字符串&#xff09;。我们将通过双指针法来实现这一功能。 代码实现 #include <stdio.h>void reverseString(char* s, int sSize) {int left 0, right sSize - 1; // 定义左右指针…

一元函数微积分的几何应用:二维平面光滑曲线的曲率公式

文章目录 前言曲率和曲率半径的定义曲率计算公式参数方程形式直角坐标显式方程形式极坐标形式向量形式 前言 本文将介绍二维平面光滑曲线的曲率定义以及不同形式的曲率及曲率半径公式的推导。 曲率和曲率半径的定义 &#xff08;关于二维平面光滑曲线的定义以及弧长公式请参…

使用 Tauri 2 + Next.js 开发跨平台桌面应用实践:Singbox GUI 实践

Singbox GUI 实践 最近用 Tauri Next.js 做了个项目 - Singbox GUI&#xff0c;是个给 sing-box 用的图形界面工具。支持 Windows、Linux 和 macOS。作为第一次接触这两个框架的新手&#xff0c;感觉收获还蛮多的&#xff0c;今天来分享下开发过程中的一些经验~ 为啥要做这个…

流媒体娱乐服务平台在AWS上使用Presto作为大数据的交互式查询引擎的具体流程和代码

一家流媒体娱乐服务平台拥有庞大的用户群体和海量的数据。为了高效处理和分析这些数据&#xff0c;它选择了Presto作为其在AWS EMR上的大数据查询引擎。在AWS EMR上使用Presto取得了显著的成果和收获。这些成果不仅提升了数据查询效率&#xff0c;降低了运维成本&#xff0c;还…

【自学笔记】Java的重点知识点-持续更新

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Java知识点概览一、Java简介二、Java基本语法三、面向对象编程&#xff08;OOP&#xff09;四、异常处理五、常用类库六、多线程编程七、网络编程 注意事项 总结 Ja…

25.2.2学习内容

通过前序遍历和后序遍历求可能的二叉树的种数&#xff08;AI生成&#xff09;&#xff1a; #include<stdio.h> #include<string.h> #include<stdlib.h> #include<math.h>struct TreeNode {char val;struct TreeNode *left;struct TreeNode *right; };…

Hive详细讲解-概述与环境搭建

文章目录 1.Hive概述1.2.Hive架构原理1.3Driver 2.Hive最小化模式安装部署3.生产环境hive安装部署4.将hive的元数据存储到Mysql5.元数据库概述6.Hive服务的部署6.1HiveServer26.2Metastore 7.HiveServer2使用7.1Metastore嵌入模式配置7.2Metastore独立模式配置* 8.hive常用的参…