【网络编程】事件选择模型

简介: 【网络编程】事件选择模型

事件选择模型

windows处理用户行为的两种方式

消息机制

核心:消息队列

处理过程:所有的用户操作,比如点鼠标,按键盘,对软件进行的各种操作......等等,所有操作均依次按顺序被记录,装进一个队列。不同的操作对应着不同的编号。

特点:消息队列由操作系统维护,用户进行操作,然后把消息读取出来,分类处理。有先后顺序。动态的。

异步选择模型就是基于这个消息的

事件机制

核心:事件集合

处理过程:根据需求我们为用户的特定操作绑定一个事件,事件由我们自己调用API创建,需要多少创建多少。

将时间投递给系统,系统就帮我们监视着,所以不能无限创建,太多了系统运行就卡了。

如果操作发生了,比如用户按鼠标了,那么对应的事件就会被置成有信号,也就是类似1变2,用个数来标记。

直接获取到有信号的时间进行处理。

特点:所有时间都是咱们自己定义的,系统只是帮咱们标记有无信号。无序

事件选择模型,就是应用这个。

事件选择

整体类似于select

  1. 创建一个事件对象(变量),WSACreateEvent
  2. 为每一个事件对象绑定socket以及操作accept,read,close.....

    投递给系统让其监管,WSAEventSelect

  3. 查看事件是否有信号,WSAWaitForMultipleEvents
  4. 有信号就分类处理,WSAEnumNetworkEvents
网路头文件 网络库
    打开网络库
    校验版本
    创建SOCKET
    绑定地址与端口
    开始监听 
    
    事件选择

创建一个事件对象

WSAEVENT WSAAPI WSACreateEvent();

WSAEVENT eventServer = WSACreateEvent();
成功-返回一个事件
失败-返回WSA_INVALID_EVENT(无效的事件对象) 
    if (eventServer == WSA_INVALID_EVENT)
    {
        int a = WSAGetLastError();
        closesocket(socketServer);
        WSACleanup();
        return 0;
    }

就是定义了一个事件类型。

HANDLE(void *通用类型指针),句柄,句柄的本质是ID,内核对象,唯一的标识符。

内核对象内核对象是系统提供的用户模式下代码与内核模式下代码进行交互的基本接口(百度百科)。

当调用一个用于创建内核对象的函数时,该函数就返回一个用于标识该对象的句柄

内核对象
    由系统在内核申请
    由操作系统访问
    我们不能定位其内容,也不能修改 
        void* 通用类型指针
        对内核的保护,对规则的保护,从而使操作系统有序的平稳的,
        有效的运行,而不会随便出问题
    调用函数创建,调用函数释放
        如果我们没有调用释放,那么他可能就一直存在于内核,
        造成内核内存泄漏, 这种只能重启电脑
    内核对象有哪些 socket Kernel Objects

关闭/释放事件句柄

不用就要释放

BOOL WSAAPI WSACloseEvent
(
  WSAEVENT hEvent
);

WSACloseEvent(eventServer);

指定事件主动置成无信号的

BOOL WSAAPI WSAResetEvent
(
  WSAEVENT hEvent
);

指定事件主动置成有信号的

BOOL WSAAPI WSASetEvent
(
  WSAEVENT hEvent
);

绑定并投递

int WSAAPI WSAEventSelect(
    SOCKET s,
    WSAEVENT hEventObject,
    long INetworkEvents
);

功能

给事件绑上socket与操作码,并投递给操作系统。

参数1

被绑定的socket,最终每个socket都会被绑定一个事件

参数2

事件对象,逻辑,就是将参数1和参数2绑定在一起

参数3

具体事件

FD_ACCEPT
   有客户端链接,与服务器socket绑定
FD_READ
   有客户端发来消息,与客户端socket绑定,可多个属性并列使用。
FD_CLOSE
   客户端下线,与客户端绑定,包含强制下线
FD_WRITE
   可以给客户端发信,与客户端socket绑定,会在accept之后立即主动产生该信号。可以说明,客户端连接成功。即可随时send
FD_CONNECT
   用在客户端上,给服务器绑定这个。
0
   取消事件绑定。
FD_OOB 
   带外数据,一般不使用。
FD_QOS 
      套接字服务质量状态发生变化。动态变化。
FD_GROUP_QOS
   保留——还没有对其赋值具体意义。    
重叠I/O模型中
    FD_ROUTING_ INTERFACE_CHANGE
        
      FD_ADDRESS_ LIST_CHANGE

返回值

成功——返回0
失败——返回SOCKET_ERROR
     if (SOCKET_ERROR == WSAEventSelect(socketServer, SetEvent, FD_ACCEPT);)
    {
        int a = WSAGetLastError();
        //释放事件句柄
        WSACloseEvent(eventServer);
        //释放所有socket
        closesocket(socketServer);
        //关闭网络库
        WSACleanup();
        return 0;
    }   

询问事件

DWORD WSAAPI WSAWaitForMultipleEvents(
  DWORD          cEvents,
  const WSAEVENT *lphEvents,
  BOOL           fWaitAll,
  DWORD          dwTimeout,
  BOOL           fAlertable
);

功能

获取发生信号的事件。

参数1

事件个数,定义事件列表(数组)个数。

最大64  WSA_MAXIMUM_WAIT_EVENTS

可以变大,不像select模型,直接就能变大,因为select模型本身就是个数组,直接遍历即可, 比较直接,而事件选择是按照异步来投放,由系统进行管理,咱们就不能随便修改了,要按照规则来。

参数2

事件列表。

参数3

事件等待方式。

TRUE
    所有事件都产生信号,才返回。
FALSE
    任何一个事件产生信号,立即返回。
       返回值减去WSA_WAIT_EVENT_0表示事件对象的索引,其状态导致函数返回。
     如果在调用期间发出多个事件对象的信号,则这是信号事件对象的数组索引,其中所有信号事件对象的索引值最小。

参数4

超时间隔,以毫秒为单位。与select参数5意义相同。

123 等待123秒,超时返回WSA_WAIT_TIMEOUT
0    检查事件对象的状态并立即返回。不管有没有信号 
WSA_INFINITE 等待,直到事件发生。

参数5

TRUE 重叠I/O模型使用
FALSE 

返回值

数组下标的运算值,参数3为TRUE 所有时间均有信号
                参数3位FALSE 返回值减去WSA_WAIT_EVENT_0==数                                                组中事件的下标
WSA_WAIT_IO_COMPLETION 参数5为TRUE,才会返回这个值            WSA_WAIT_TIMEOUT 超时了,continue即可。    
    
    
    
    while (1)
    {
        //询问
        DWORD nRes = WSAWaitForMultipleEvents(esSet.count, esSet.eventall, FALSE,WSA_INFINITE, FALSE);
        if (nRes == WSA_WAIT_FAILED)
        {
            printf("错误码%d\n", WSAGetLastError());
            break;
        }


        //超时使用
        /*if (WSA_WAIT_TIMEOUT == nRes)
        {
            continue;
        }*/


        DWORD nIndex = nRes - WSA_WAIT_EVENT_0;

    }

列举事件

int WSAAPI WSAEnumNetworkEvents(
  SOCKET             s,
  WSAEVENT           hEventObject,
  LPWSANETWORKEVENTS lpNetworkEvents
);

获取事件类型,并将事件上的信号重置,accept,recv,close等

WSANETWORKEVENTS NetworkEvents;
if(SOCKET_ERROR==WSAEnumNetworkEvents(esSet.sockall[nIndex], esSet.eventall[nIndex], &NetworkEvents))
{
            int a = WSAGetLastError();
            printf("错误码:%d\n", a);
            break;
}

参数1

对应的socket

参数2

对应的事件

参数3

触发的事件类型在这里装着。是一个结构体指针。

struct _WSANETWORKEVENTS
 {
  long lNetworkEvents;
    //具体操作,一个信号可能包含两个信息,以按位或的形式存在
  int  iErrorCode[FD_MAX_EVENTS];
    //错误码数组,FD_ACCEPT事件错误码在FD_ACCEPT_BIT下标里
    //没有错误,对应的就是0
} 

返回值

成功——返回0
失败——返回SOCKET_ERROR            

事件分类处理逻辑

if (lpNetworkEvents->lNetworkEvents & FD_ACCEPT)
{
    if (lpNetworkEvents->iErrorCode[FD_ACCEPT_BIT] == 0)
    {
        //接受链接
       //创建事件
       //投放事件
       //元素增加
    }
}
switch不行有大bug
else if 不太行,有小bug

有序处理

优化

for (i = Index; i < EventTotal; i++)
 {
        Index = WSAWaitForMultipleEvents(1, &EventArray[i], TRUE, 1000, FALSE);
        if ((Index != WSA_WAIT_FAILED) && (Index != WSA_WAIT_TIMEOUT)) 
        {
            WSAEnumNetworkEvents(SocketArray[i], EventArray[i], &NetworkEvents);
            //分类处理
        }
}

增加事件数量

当前代码是一组一组投递,一组是64个,由WSAWaitForMultipleEvents这个函数决定。

增加事件数量————一个一个投,一个大数组就行了,
           ————一组一组投,单线程,一组一组顺序处理就好了。
                创建多线程,每个线程处理一个事件表,最大是64

完整代码

#define _CRT_SECURE_NO_WARNINGS
//#define FD_SETSIZE 128
#define _WINSOCK_DEPRECATED_NO_WARNINGS 
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <Winsock2.h>
#include <string.h>
#pragma comment(lib, "Ws2_32.lib")

struct fd_es_set
{
    unsigned short count;
    SOCKET sockall[WSA_MAXIMUM_WAIT_EVENTS];
    WSAEVENT evnetall[WSA_MAXIMUM_WAIT_EVENTS];
};

struct fd_es_set esSet;

BOOL WINAPI fun(DWORD dwCtrlType)
{
    switch (dwCtrlType)
    {
    case CTRL_CLOSE_EVENT:
        //释放所有socket
        for (int i = 0; i < esSet.count; i++)
        {
            closesocket(esSet.sockall[i]);
            WSACloseEvent(esSet.evnetall[i]);
        }

        break;
    }

    return TRUE;
}

int main(void)
{
    SetConsoleCtrlHandler(fun, TRUE);

    WORD wdVersion = MAKEWORD(2, 2); //2.1  //22
    //int a = *((char*)&wdVersion);
    //int b = *((char*)&wdVersion+1);
    WSADATA wdScokMsg;
    //LPWSADATA lpw = malloc(sizeof(WSADATA));// WSADATA*
    int nRes = WSAStartup(wdVersion, &wdScokMsg);

    if (0 != nRes)
    {
        switch (nRes)
        {
        case WSASYSNOTREADY:
            printf("重启下电脑试试,或者检查网络库");
            break;
        case WSAVERNOTSUPPORTED:
            printf("请更新网络库");
            break;
        case WSAEINPROGRESS:
            printf("请重新启动");
            break;
        case WSAEPROCLIM:
            printf("请尝试关掉不必要的软件,以为当前网络运行提供充足资源");
            break;
        }
        return  0;
    }

    //校验版本
    if (2 != HIBYTE(wdScokMsg.wVersion) || 2 != LOBYTE(wdScokMsg.wVersion))
    {
        //说明版本不对
        //清理网络库
        WSACleanup();
        return 0;
    }

    SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    //int a = WSAGetLastError();
    if (INVALID_SOCKET == socketServer)
    {
        int a = WSAGetLastError();
        //清理网络库
        WSACleanup();
        return 0;
    }

    struct sockaddr_in si;
    si.sin_family = AF_INET;
    si.sin_port = htons(12345);
    si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    //int a = ~0;
    if (SOCKET_ERROR == bind(socketServer, (const struct sockaddr*)&si, sizeof(si)))
    {
        //出错了
        int a = WSAGetLastError();
        //释放
        closesocket(socketServer);
        //清理网络库
        WSACleanup();
        return 0;
    }

    if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
    {
        //出错了
        int a = WSAGetLastError();
        //释放
        closesocket(socketServer);
        //清理网络库
        WSACleanup();
        return 0;
    }



    //创建事件
    WSAEVENT eventServer = WSACreateEvent();
    if (WSA_INVALID_EVENT == eventServer)
    {
        //出错了
        int a = WSAGetLastError();
        //释放
        closesocket(socketServer);
        //清理网络库
        WSACleanup();
        return 0;
    }

    if (SOCKET_ERROR == WSAEventSelect(socketServer, eventServer, FD_ACCEPT))
    {
        //出错了
        int a = WSAGetLastError();
        //释放事件句柄
        WSACloseEvent(eventServer);
        //释放所有socket
        closesocket(socketServer);
        //清理网络库
        WSACleanup();
        return 0;
    }

    //装进去
    esSet.evnetall[esSet.count] = eventServer;
    esSet.sockall[esSet.count] = socketServer;
    esSet.count++;

    while (1)
    {

        //询问
        DWORD nRes = WSAWaitForMultipleEvents(esSet.count, esSet.evnetall, FALSE, WSA_INFINITE, FALSE);
        if (WSA_WAIT_FAILED == nRes)
        {
            int a = WSAGetLastError();
            //出错了
            printf("错误码:%d\n", a);
            break;
        }
        //超时使用
        //if (WSA_WAIT_TIMEOUT == nRes)
        //{
        //    continue;
        //}
        //
        DWORD nIndex = nRes - WSA_WAIT_EVENT_0;

        //得到下标对应的具体操作
        WSANETWORKEVENTS NetworkEvents;
        if (SOCKET_ERROR == WSAEnumNetworkEvents(esSet.sockall[nIndex], esSet.evnetall[nIndex], &NetworkEvents))
        {
            int a = WSAGetLastError();
            //出错了
            printf("错误码:%d\n", a);
            break;
        }

        if (NetworkEvents.lNetworkEvents & FD_ACCEPT)
        {
            if (0 == NetworkEvents.iErrorCode[FD_ACCEPT_BIT])
            {
                //正常处理
                SOCKET socketClient = accept(esSet.sockall[nIndex], NULL, NULL);
                if (INVALID_SOCKET == socketClient)
                {
                    continue;
                }

                //创建事件对象
                WSAEVENT wsaClientEvent = WSACreateEvent();
                if (WSA_INVALID_EVENT == wsaClientEvent)
                {
                    closesocket(socketClient);
                    continue;
                }

                //投递给系统
                if (SOCKET_ERROR == WSAEventSelect(socketClient, wsaClientEvent, FD_READ | FD_CLOSE | FD_WRITE))
                {
                    closesocket(socketClient);
                    WSACloseEvent(wsaClientEvent);
                    continue;
                }

                //装进结构体
                esSet.sockall[esSet.count] = socketClient;
                esSet.evnetall[esSet.count] = wsaClientEvent;
                esSet.count++;

                printf("accept event\n");
            }
            else
            {
                continue;
            }
        }

        if (NetworkEvents.lNetworkEvents & FD_WRITE)
        {
            if (0 == NetworkEvents.iErrorCode[FD_WRITE_BIT])
            {
                //初始化

                if (SOCKET_ERROR == send(esSet.sockall[nIndex], "connect success", strlen("connect success"), 0))
                {
                    int a = WSAGetLastError();
                    printf("send faild, error code:%d\n", a);
                    continue;
                }
                printf("write event\n");
            }
            else
            {
                printf("socket error code:%d\n", NetworkEvents.iErrorCode[FD_WRITE_BIT]);
                continue;
            }
        }

        if (NetworkEvents.lNetworkEvents & FD_READ)
        {
            if (0 == NetworkEvents.iErrorCode[FD_READ_BIT])
            {
                char strRecv[1500] = { 0 };
                if (SOCKET_ERROR == recv(esSet.sockall[nIndex], strRecv, 1499, 0))
                {
                    int a = WSAGetLastError();
                    printf("recv faild, error code:%d\n", a);
                    continue;
                }
                printf("recv data: %s\n", strRecv);
            }
            else
            {
                printf("socket error code:%d\n", NetworkEvents.iErrorCode[FD_READ_BIT]);
                continue;
            }
        }

        if (NetworkEvents.lNetworkEvents & FD_CLOSE)
        {
            /*if (0 == NetworkEvents.iErrorCode[FD_CLOSE_BIT])
            {

            }
            else
            {

            }
            WSAECONNABORTED;*/

            //打印
            printf("client close\n");
            printf("client force out: %d\n", NetworkEvents.iErrorCode[FD_CLOSE_BIT]);
            //清理下线的客户端 套接字  事件
            //套接字
            closesocket(esSet.sockall[nIndex]);
            esSet.sockall[nIndex] = esSet.sockall[esSet.count - 1];
            //事件
            WSACloseEvent(esSet.evnetall[nIndex]);
            esSet.evnetall[nIndex] = esSet.evnetall[esSet.count - 1];

            //数量减一
            esSet.count--;
        }


    }

    for (int i = 0; i < esSet.count; i++)
    {
        closesocket(esSet.sockall[i]);
        WSACloseEvent(esSet.evnetall[i]);
    }

    //清理网络库
    WSACleanup();

    system("pause");
    return 0;
}

对比select模型

事件选择模型——异步

select模型——同步

相关文章
|
1月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于BP神经网络的苦瓜生长含水量预测模型matlab仿真
本项目展示了基于BP神经网络的苦瓜生长含水量预测模型,通过温度(T)、风速(v)、模型厚度(h)等输入特征,预测苦瓜的含水量。采用Matlab2022a开发,核心代码附带中文注释及操作视频。模型利用BP神经网络的非线性映射能力,对试验数据进行训练,实现对未知样本含水量变化规律的预测,为干燥过程的理论研究提供支持。
|
9天前
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
28 2
|
9天前
|
运维 网络协议 算法
7 层 OSI 参考模型:详解网络通信的层次结构
7 层 OSI 参考模型:详解网络通信的层次结构
31 1
|
1月前
|
网络协议 前端开发 Java
网络协议与IO模型
网络协议与IO模型
网络协议与IO模型
|
1月前
|
机器学习/深度学习 网络架构 计算机视觉
目标检测笔记(一):不同模型的网络架构介绍和代码
这篇文章介绍了ShuffleNetV2网络架构及其代码实现,包括模型结构、代码细节和不同版本的模型。ShuffleNetV2是一个高效的卷积神经网络,适用于深度学习中的目标检测任务。
73 1
目标检测笔记(一):不同模型的网络架构介绍和代码
|
20天前
|
网络协议 算法 网络性能优化
计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议
计算机网络常见面试题(一):TCP/IP五层模型、应用层常见的协议、TCP与UDP的区别,TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议、ARP协议
|
1月前
|
安全 NoSQL Java
一文搞懂网络通信的基石✅IO模型与零拷贝
【10月更文挑战第1天】本文深入探讨了网络通信中的IO模型及其优化方法——零拷贝技术。首先介绍了IO模型的概念及五种常见类型:同步阻塞、同步非阻塞、多路复用、信号驱动和异步IO模型。文章详细分析了每种模型的特点和适用场景,特别是多路复用和异步IO在高并发场景中的优势。接着介绍了零拷贝技术,通过DMA直接进行数据传输,避免了多次CPU拷贝,进一步提升了效率。最后总结了各种模型的优缺点,并提供了相关的代码示例和资源链接。
一文搞懂网络通信的基石✅IO模型与零拷贝
|
26天前
|
机器学习/深度学习 人工智能 算法
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
车辆车型识别,使用Python作为主要编程语言,通过收集多种车辆车型图像数据集,然后基于TensorFlow搭建卷积网络算法模型,并对数据集进行训练,最后得到一个识别精度较高的模型文件。再基于Django搭建web网页端操作界面,实现用户上传一张车辆图片识别其类型。
72 0
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
|
1月前
|
监控 安全 Linux
网络安全事件应急响应
应急响应是针对网络安全事件的快速处理流程,包括信息收集、事件判断、深入分析、清理处置、报告产出等环节。具体步骤涵盖准备、检测、抑制、根除、恢复和总结。
|
1月前
|
开发者
什么是面向网络的IO模型?
【10月更文挑战第6天】什么是面向网络的IO模型?
21 3

热门文章

最新文章