学习笔记(06):C++网络编程进阶-IO模型之WSAEventSelect

立即学习:https://edu.csdn.net/course/play/6082/113758?utm_source=blogtoedu

WSAEventSelect模型

WSAEventSelect模型是一个不主动去轮询所以客户端套接字是否有数据到来的模型,客户端有数据到来时,系统发送事件通知我们的程序,这就解决了WSAAysncSelect模型只能用在windows窗口程序的限制。

WSAEventSelect模型,流程如下

1、创建一个事件对象数组,用于存放所有的事件对象

2、创建一个事件对象(WSACreateEvent)

3、将一组感兴趣的SOCKET事件与事件对象关联(WSAEventSelect),然后加入事件对象数组。

4、等待事件对象发生有一个你感兴趣的网络事件

5、对发生事件的事件对象查询具体发生的事件类型(WSAEnumNetworkEvents)

6、针对不同的事件类型进行不同的处理。

WSACreateEvent函数:

WSAEVENT WSACreateEvent(void)

调用WSACreateEveent函数创建一个事件对象

返回值:成功返回事件对象的句柄,失败返回WSA_INVALID_EVENT ,通过WSAGetLastError(),获取错误信息

WSAEventSelect函数

int WSAEventSelect(SOCKET s,WSAEVENT hEvent,Long INetworkEvents);

参数:

第一个参数:套接字句柄

第二个参数:为事件对象句柄

第三个参数:应用程序感兴趣的网络事件集合,应用程序为套接字注册网络事件成功

返回值:

成功返回0 , 失败返回SOCKET_ERROR,通过WSAGetLastError获取错误信息

WSAWaitForMultipleEvents函数

DWORD WSAWatiForMultipleEvent(DWORD cEvent,const WSAEVENT *plhEvent,BOOL fWaitAll,DWORD dwTimeOut,BOOL fAltertable)

参数:

第一个参数:事件句柄个数,至少为1,最多64个。

第二个参数:为指向对象句柄数组指针

第三个参数:如果为TRUE,则该函数在所有事件对象都转为已触发时才返回,如为false,只要有一个对象呗触发,函数即返回。

第四个参数:单位为毫秒,在事件用完后函数会返回,如果为WSA_INFINITE,则函数会一直等待下去,如果超时返回,函数返回WSA_WAIT_TIMEOUT

第五个参数:参数说明当完成例程在系统队列中排队等待执行时,该函数是否返回,这主要应用于重叠IO模型,在完成例程中会用到这个参数,这里我们先设置为false

 

当fWaitAll为true时:

返回WSA_TIMEOUT 则表明等待超时

返回WSA_EVENT_0 表明所有对象都已变成触发态,等待成功

返回WAIT_IO_COMPLETION说明一个或多个完成例程已经排队等待执行

如果fWaitAll为false时;

WSA_WAIT_EVENT_0到WSA_WAIT_EVENT_0+cEvent-1范围的值,说明有一个对象变为触发态,他在数组为:返回值 WSA_WAIT_EVENT_0

如果函数调用失败:返回WSA_WAIT-FAILED

WSAEnumNetwordEvent函数:监测所指定的套接字上网络事件的发生

int WSAEnumNetworkEvents(_In_ SOCKET s , _In_ WSAEVENT hEventObject,_Out_ LPWSANETWORDEVENTS lpNetwordEvents);

参数:

第一个参数:发生事件的socket

第二个参数:发生事件的事件对象

第三个参数:发生的网络事件

返回值:

成功返回0 , 失败返回SOCKET_ERROR错误,通过WSAGetLastError()来获取错误代码

typedef struct _WSANETWORKEVENTS

{

long INetworkEvent;发生的网络事件类型

int iErrorCode[FD_MAX_EVENTS] 网络事件错误代码

} WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;

比如当发生FD_READ 事件时,那么INetworkEvent & FD_READ 将为真,同事 iErrorCode[FD_READ_BIT] 表明了此时的错误代码,为0则没有错误。

WSAResetEvent函数

bool WSAResetEvent(WSAEVENT hEvent)

当网络事件到来时,与套接字关联的事件对象由未触发变为出发状态,由于它是手工重置事件,应用程序需要手动将事件的状态设置为为触发态

返回值:

成功返回真TRUE

失败返回FALSE,可调用WSAGetLastError来获取错误信息。

WSACloseEvent函数:

bool WSACloseEvent(WSAEVENT hEvent)

不再使用对象时要将其关闭

返回值:

成功返回TRUE

失败返回FALSE ,可调用WSAGetLastError来获取错误信息。

 

服务端代码:

#include <iostream>
#include<WinSock2.h>
#include<cstdlib>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
    WSADATA wd;
    if (WSAStartup(MAKEWORD(2, 2), &wd) != 0)
    {
        cout << "wsastartup error " << WSAGetLastError() << endl;
        exit(EXIT_FAILURE);
    }
    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s == INVALID_SOCKET)
    {
        cout << "socket error " << WSAGetLastError() << endl;
        exit(EXIT_FAILURE);
    }
    sockaddr_in addr;
    addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8000);

    int bindRet = bind(s, (sockaddr*)&addr, sizeof sockaddr);
    if (bindRet == SOCKET_ERROR)
    {
        cout << "bind error " << WSAGetLastError() << endl;
        exit(EXIT_FAILURE);
    }
    int listenRet = listen(s, 0);
    if (listenRet == SOCKET_ERROR)
    {
        cout << "listen error " << WSAGetLastError() << endl;
        exit(EXIT_FAILURE);
    }
    //定义事件数组
    WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS];
    //定义套接字数组
    SOCKET socketArray[WSA_MAXIMUM_WAIT_EVENTS];
    int N = 0;
    //创建事件对象
    WSAEVENT event = WSACreateEvent();
    //将套接字和事件对象以及事件的类型通过WSAEventSelect方法“绑定”
    WSAEventSelect(s, event, FD_ACCEPT | FD_CLOSE);
    socketArray[N] = s;
    eventArray[N++] = event;
    while (true)
    {
        //线程将在这里阻塞,直到有一个socket触发事件
        int ret = WSAWaitForMultipleEvents(N, eventArray, false, WSA_INFINITE, false);
        if (ret == WSA_WAIT_FAILED)
        {
            cout << "WSAWaitFroMultipleEvents error " << WSAGetLastError() << endl;
            continue;
        }

        int index = ret - WSA_WAIT_EVENT_0;
        cout << "WSA_WAIT_EVENT_O" << WSA_WAIT_EVENT_0 << endl;
        WSANETWORKEVENTS netEvents;
        SOCKET sock = socketArray[index];
        //枚举出网络事件的具体类型
        WSAEnumNetworkEvents(sock, eventArray[index], &netEvents);
        if (netEvents.lNetworkEvents & FD_ACCEPT)
        {
            if (netEvents.iErrorCode[FD_ACCEPT_BIT] == 0)
            {
                if (N >= WSA_MAXIMUM_WAIT_EVENTS)
                {
                    cout << "事件对象数组已满" << endl;
                    continue;
                }
                SOCKET client = accept(sock, NULL, NULL);
                if (client != INVALID_SOCKET)
                {
                    cout << client << "进入聊天室" << endl;
                    char temp[64];
                    sprintf_s(temp, "欢迎%d进入聊天室", client);
                    send(client, temp, strlen(temp), 0);
                    WSAEVENT e = WSACreateEvent();
                    WSAEventSelect(client, e, FD_READ | FD_WRITE | FD_CLOSE);
                    socketArray[N] = client;
                    eventArray[N++] = e;
                }
            }
        }
        else if (netEvents.lNetworkEvents & FD_READ)
        {
            if (netEvents.iErrorCode[FD_READ_BIT] == 0)
            {
                char buf[64];
                ZeroMemory(buf, 64);
                int ret = recv(sock, buf, 64, 0);
                if (ret > 0)
                {
                    cout << sock << "说:" << buf << endl;
                }
            }
        }
        else if (netEvents.lNetworkEvents & FD_CLOSE)
        {
            WSACloseEvent(eventArray[index]);
            closesocket(sock);
            cout << sock << "离开了" << endl;
            for (int j = index; j < N - 1; j++)
            {
                eventArray[j] = eventArray[j + 1];
                socketArray[j] = socketArray[j + 1];
            }
            N--;
        }
    }
    WSACleanup();
}

 

客户端代码

#include <iostream>
#include<WinSock2.h>
#include<cstdlib>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
    WSADATA wd;
    if (WSAStartup(MAKEWORD(2, 2), &wd) != 0)
    {
        cout << "wsastartup error " << WSAGetLastError() << endl;
        exit(EXIT_FAILURE);
    }
    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s == INVALID_SOCKET)
    {
        cout << "socket error " << WSAGetLastError() << endl;
        exit(EXIT_FAILURE);
    }
    sockaddr_in addr;
    addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(8000);
    addr.sin_family = AF_INET;
    int connRet = connect(s, (sockaddr*)&addr, sizeof sockaddr_in);
    if (connRet == SOCKET_ERROR)
    {
        cout << "connect error " << WSAGetLastError() << endl;
        exit(EXIT_FAILURE);
    }
    int ret; 
    char str[100];
    while (true)
    {
        memset(str, 0, 100);
        cin >> str;
        ret = send(s, str, strlen(str), 0);
        if (ret == SOCKET_ERROR)
        {
            cout << "recv error " << WSAGetLastError() << endl;
            break;
        }
    }
    WSACleanup();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值