• AcceptEx函数与完成端口的结合使用例子


    这是在学《Windows网络编程(第二版)》第六章时制作的一个例子
    由于书中没有给出简洁的例子,本人在学这里时就费了很多时间。
    现在把完成的代码贴出来,供大家参考。
    下面包括了主程序部分,工作线程在(2)中贴出,由于代码太长。
    本程序在VS2003编译器编译运行。在6.0下可能需要稍加修改。
    
    #include <iostream>
    #include <winsock2.h>
    #include <ws2tcpip.h>
    #include <mswsock.h>    //微软扩展的类库
    
    using namespace std;
    #define SEND 0
    #define RECV 1
    #define ACCEPT 2
    
    #define DATA_LENGTH 1000
    //单句柄数据定义
    typedef struct _PER_HANDLE_DATA
    {
        SOCKET socket;    //相关的套接字
        SOCKADDR_STORAGE clientAddr;    //客户端的地址
    }PER_HANDLE_DATA,*LPPER_HANDLE_DATA;
    
    //但IO操作数据
    typedef struct{
        OVERLAPPED overlapped;
        WSABUF buffer;    //一个数据缓冲区,用于WSASend/WSARecv中的第二个参数
        char dataBuffer[DATA_LENGTH];    //实际的数据缓冲区
        int dataLength;                    //实际的数据缓冲区长度
        int operatorType;                //操作类型,可以为SEND/RECV两种
        SOCKET client;                    //分别表示发送的字节数和接收的字节数
    }PER_IO_DATA,*LPPER_IO_DATA;
    
    
    void main()
    {
        HANDLE CompletionPort;
        WSADATA data;
        SYSTEM_INFO info;
        SOCKADDR_IN addr;
        SOCKET Listen;
    
        unsigned int i;
        WSAStartup(MAKEWORD(2,2),&data);
    
        //创建一个IO完成端口
        CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
        //确定处理器的数量
        GetSystemInfo(&info);    //创建线城
        for(i=0;i<info.dwNumberOfProcessors * 2;i++)
        {
            //根据处理器的数量创建相应多的处理线程
            HANDLE thread = CreateThread(NULL,0,ServerThread,CompletionPort,0,NULL);
            CloseHandle(thread);
        }
        //创建一个监听套接字(进行重叠操作)
        Listen = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
        //将监听套接字与完成端口绑定
        LPPER_HANDLE_DATA perDandleData;
        perDandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR,sizeof(PER_HANDLE_DATA));
        perDandleData->socket = Listen;
        CreateIoCompletionPort((HANDLE)Listen,CompletionPort,(ULONG_PTR)perDandleData,0);
    
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        addr.sin_port = htons(5500);
    
        bind(Listen,(PSOCKADDR)&addr,sizeof(addr));
        listen(Listen,5);
    
        LPFN_ACCEPTEX lpfnAcceptEx = NULL;    //AcceptEx函数指针
        //Accept function GUID
        GUID guidAcceptEx = WSAID_ACCEPTEX;
        //get acceptex function pointer
        DWORD dwBytes = 0;
        if(WSAIoctl(Listen,SIO_GET_EXTENSION_FUNCTION_POINTER,
            &guidAcceptEx,sizeof(guidAcceptEx),&lpfnAcceptEx,sizeof(lpfnAcceptEx),
            &dwBytes,NULL,NULL)==0)
            cout<<"WSAIoctl success..."<<endl;
        else{
            cout<<"WSAIoctl failed..."<<endl;
            switch(WSAGetLastError())
            {
            case WSAENETDOWN:
                cout<<""<<endl;
                break;
            case WSAEFAULT:
                cout<<"WSAEFAULT"<<endl;
                break;
            case WSAEINVAL:
                cout<<"WSAEINVAL"<<endl;
                break;
            case WSAEINPROGRESS:
                cout<<"WSAEINPROGRESS"<<endl;
                break;
            case WSAENOTSOCK:
                cout<<"WSAENOTSOCK"<<endl;
                break;
            case WSAEOPNOTSUPP:
                cout<<"WSAEOPNOTSUPP"<<endl;
                break;
            case WSA_IO_PENDING:
                cout<<"WSA_IO_PENDING"<<endl;
                break;
            case WSAEWOULDBLOCK:
                cout<<"WSAEWOULDBLOCK"<<endl;
                break;
            case WSAENOPROTOOPT:
                cout<<"WSAENOPROTOOPT"<<endl;
                break;
            }
            return;
        }
    
        //while(true)
        //{
            //准备调用 AcceptEx 函数,该函数使用重叠结构并于完成端口连接
            LPPER_IO_DATA perIoData = (LPPER_IO_DATA)GlobalAlloc(GPTR,sizeof(PER_IO_DATA));
            memset(&(perIoData->overlapped),0,sizeof(OVERLAPPED));    
            perIoData->operatorType = ACCEPT;
            //在使用AcceptEx前需要事先重建一个套接字用于其第二个参数。这样目的是节省时间
            //通常可以创建一个套接字库
            perIoData->client = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,0,0,WSA_FLAG_OVERLAPPED);
    
            perIoData->dataLength = DATA_LENGTH;
            DWORD flags = 0;
            
            //调用AcceptEx函数,地址长度需要在原有的上面加上16个字节
            //注意这里使用了重叠模型,该函数的完成将在与完成端口关联的工作线程中处理
            cout<<"Process AcceptEx function wait for client connect..."<<endl;
            int rc = lpfnAcceptEx(Listen,perIoData->client,perIoData->dataBuffer,
                perIoData->dataLength-((sizeof(SOCKADDR_IN)+16)*2),
                sizeof(SOCKADDR_IN)+16,sizeof(SOCKADDR_IN)+16,&dwBytes,
                &(perIoData->overlapped));
            if(rc == FALSE)
            {
                if(WSAGetLastError()!=ERROR_IO_PENDING)
                    cout<<"lpfnAcceptEx failed.."<<endl;
            }
    
            cin>>i;
        closesocket(Listen);
        WSACleanup();
    }

  • 相关阅读:
    【北亚数据恢复】未加where子句的命令操作导致MySQL数据库数据丢失的数据恢复案例
    【北亚数据恢复】意外断电导致Dell服务器硬盘离线,raid5阵列数据丢失的数据恢复案例
    【北亚数据恢复】DELL POWEREDGE 2850服务器RAID5先后两块硬盘掉线后系统瘫痪的数据恢复
    log4j:WARN Please initialize the log4j system properly
    连接数据库 springboot环境下默认使用的是h2数据库,你没有对h2进行配置,这里我是mysql数据库,需要配置一下mysql数据库
    vue.config.js的proxy为什么不起作用
    连接数据库 driverclassname: com.mysql.cj.jdbc.Driver 报错的几种解决办法 三种办法
    <dependencyManagement>正确使用方法 多个子项目都引用同一样依赖,则可以避免在每个使用的子项目里都声明一个版本号。当想升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要逐个修改子项目
    vue.config.js 的完整配置(超详细) 个性化 设置ip和端口
    服务网关 Zuul 与 Redis 结合实现 Token 权限校验
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13318513.html
Copyright © 2020-2023  润新知