学习笔记(08):C++网络编程进阶-IO模型之重叠Overlapped IO(基于完成例程)

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

完成例程(completion routines)

完成例程回调函数原型

void CALLBACK CompletionRoutineFunc(DWORD dwError,DWROD cbTransferred,LPWSAOVERLAPPED lpOverlapped,DWORD dwFlags)

参数:

第一个参数:表名一个重叠操作的完成的状态

第二个参数:指明了重叠操作期间,实际传输字节数

第三个参数:传输到最初的IO调用内的重叠结构

第四个参数:返回操作结束时可能用到的标志

SleepEx函数:

SleepEx(

DWORD dwMilliseconds,

BOOL bAlertable

);

参数:

第一个参数:等待的超时时间,如果设置为INFINITE就会一直等待下去。

第二个参数:是否置于警觉状态,如果为FALSE,则一定要等待超时时间完毕后才返回,我们这里希望重叠操作,一完成就能返回,要设置为TRUE ;

返回值:

如果指定的时间间隔过期,返回值为0

如果返回的函数是由于一个或多个IO完成回调函数而返回的函数,则返回值是WAIT_IO_COMPLETION,只有在bAlertable是正确的情况下,才会发生这种情况,如果调用SleepEx函数的线程与调用扩展IO函数的线程相同。

基于完成例程的重叠IO编程步骤

1、创建套接字在指定端口监听,并接受客户端连接请求。

2、为接受的套接字新建一个WSAOVERLAPPED结构

3、在套接字上投递一个异步WSARecv请求,指定参数为WSAOVERLAPPED结构,同时提供一个完成例程函数,注意函数通常会以失败告终,返回SOCKET_ERROR错误状态WSA_IO_PENDINIG(IO操作尚未完成)

4、使用伪事件数组,将调用WSAWaitForMultipleEvents函数,fAlertable设置为true,等待重叠IO请求完成,重叠请求完成以后,完成例程会自动执行,而且WSAWaitForMultipleEvent函数的返回WAIT_IO_COMPLETION说明一个或多个完成例程已经排队等待执行。

5、在完成例程内,可用一个完成例程投递另一个重叠WSARecv请求。

6、如果要想知道完成例程调用后的返回结果,调用WSAWaitForMultipleEvents函数(返回值WAIT_IO_COMPLETION)或者SleepEx函数等待重叠操作返回的结果。

7、重复5~6.

 

服务端代码

#include <iostream>
#include<WinSock2.h>
#include<cstdlib>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
void CALLBACK CompletionRoutineFunc(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags);
typedef struct _MY_WSAOVERLAPPED
{
    WSAOVERLAPPED overlap;
    WSABUF Buffer;
    DWORD NumberOfBytesRecvd;
    DWORD Flags;
    SOCKET socket;
    _MY_WSAOVERLAPPED()
    {
        Buffer.buf = new char[64]{ '\0' };
        Buffer.len = 64;
        Flags = 0;
    }
    ~_MY_WSAOVERLAPPED()
    {
        delete[]Buffer.buf;
        Buffer.buf = NULL;
        Buffer.len = 0;
    }
}MY_WSAOVERLAPPED, * PMY_WSAOVERLAPPED;
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);
    }
    while (true)
    {
        SOCKET sClient = accept(s, NULL, NULL);
        if (sClient == INVALID_SOCKET)
        {
            cout << "accept error" << WSAGetLastError() << endl;
            continue;
        }
        cout << sClient << "进入聊天室" << endl;
        char temp[64];
        sprintf_s(temp,"欢迎%d进入客户端",sClient);
        send(sClient,temp,strlen(temp),0);
        PMY_WSAOVERLAPPED pOver = new MY_WSAOVERLAPPED;
        pOver->socket = sClient;
        int ret = WSARecv(pOver->socket, &pOver->Buffer, 1, &pOver->NumberOfBytesRecvd, &pOver->Flags, &pOver->overlap, CompletionRoutineFunc);
        if (ret == SOCKET_ERROR)
        {
            int err = WSAGetLastError();
            if (err != WSA_IO_PENDING)
            {
                cout << "wsarecv error " << WSAGetLastError << endl;
                closesocket(sClient);
                continue;
            }
        }
    }
}
void CALLBACK CompletionRoutineFunc(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
    PMY_WSAOVERLAPPED pover = (PMY_WSAOVERLAPPED)lpOverlapped;
    if (dwError == 0 && cbTransferred > 0)
    {
        cout << pover->socket << " 说:" << pover->Buffer.buf << endl;
        ZeroMemory(pover->Buffer.buf, 64);
        int ret = WSARecv(pover->socket, &pover->Buffer, 1, &pover->NumberOfBytesRecvd, &pover->Flags, &pover->overlap, CompletionRoutineFunc);
        if (ret == SOCKET_ERROR)
        {
            int err = WSAGetLastError();
            if (err != WSA_IO_PENDING)
            {
                cout << "wsarecv error " << WSAGetLastError() << endl;
                closesocket(pover->socket);
                delete[]pover->Buffer.buf;
            }
        }
    }
    else
    {
        cout << pover->socket << "离开了" << endl;
        closesocket(pover->socket);
        delete[]pover->Buffer.buf;
    }
}

©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页