• 见到的一篇IOCP流程 自己用demo实现了一下, 简单照抄,改动了一点点


    要分析的实例分为两个线程:


    分别是主线程(MAIN),还有一个是创建的线程(ServerThread)


    1.主函数完成初始化工作:
      1.1: (主线程)HANDLE hCompletion = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);    创建完成端口对象
      1.2: (主线程)::CreateThread(NULL, 0, ServerThread, (LPVOID)hCompletion, 0, 0);  创建线程用于接收等
      1.3:(主线程)调用socket(),SOCKADDR_IN,bind(),listen(),初始化套接字
      1.4: (线程函数)HANDLE hCompletion = (HANDLE)lpParam;   通过线程参数得到完成端口对象
      1.5: (线程函数) while(TRUE)     定义循环  循环等待套接字上发生事件
      1.6:(线程函数) ::GetQueuedCompletionStatus() 在关联到此完成端口的所有套节字上等待I/O完成
      1.7: (主线程) SOCKADDR_IN saRemote; SOCKET sNew = ::accept(sListen, (sockaddr*)&saRemote, &nRemoteLen); 为新连接建立结构并等待连接请求


    2.有连接发生:
      2.1: (主线程while循环中) PPER_HANDLE_DATA pPerHandle = (PPER_HANDLE_DATA)::GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));
                               创建PPER_HANDLE_DATA结构 
      2.2: (主线程while循环中) ::CreateIoCompletionPort((HANDLE)pPerHandle->s, hCompletion, (DWORD)pPerHandle, 0);
        完成 完成端口 与 套接字关联
      2.3: (主线程while循环中)    给pPerIO结构   添加类型
    PPER_IO_DATA pPerIO = (PPER_IO_DATA)::GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
    pPerIO->nOperationType = OP_READ;
    WSABUF buf;
    buf.buf = pPerIO->buf;
    buf.len = BUFFER_SIZE;
    DWORD dwRecv;
    DWORD dwFlags = 0;
      2.4: (主线程while循环中) ::WSARecv(pPerHandle->s, &buf, 1, &dwRecv, &dwFlags, &pPerIO->ol, NULL);     发送异步接收请求
      2.5: (主线程while循环中)  回到WHILE循环accept()函数处  等待新连接
      2.6: (线程函数while循环中)GetQueuedCompletionStatus() 此时I/O完成 开始处理消息
      2.7: (线程函数while循环中)pPerIO->nOperationType  通过类型判断  消息类型
      2.7:(线程函数while循环中)打印接收的消息  并在此投递  一个完成端口
    pPerIO->buf[dwTrans] = '\0';
    printf(pPerIO -> buf);

    WSABUF buf;
    buf.buf = pPerIO->buf ;
    buf.len = BUFFER_SIZE;
    pPerIO->nOperationType = OP_READ;
    DWORD nFlags = 0;
    printf("12\n");
    ::WSARecv(pPerHandle->s, &buf, 1, &dwTrans, &nFlags, &pPerIO->ol, NULL);
      2.8:(线程函数while循环中)  由于又有端口I/O完成 ::GetQueuedCompletionStatus()函数  继续判断并处理
      2.9:(线程函数while循环中)  由于没有数据 GetQueuedCompletionStatus()返回值为错误  调用一下函数关闭
    ::closesocket(pPerHandle->s);
    ::GlobalFree(pPerHandle);
    ::GlobalFree(pPerIO);

      2.10:(线程函数while循环中)  在此回到while循环 调用::GetQueuedCompletionStatus()函数  继续等待


    // iocpTP.cpp : Defines the entry point for the console application.
    //


    #include "stdafx.h"


    #include <stdio.h>
    #include <windows.h>


    // 初始化Winsock库
    #include <winsock2.h>
    #pragma comment(lib,"WS2_32.lib") // 






    #define BUFFER_SIZE 1024


    typedef struct _PER_HANDLE_DATA // per-handle数据
    {
    SOCKET s; // 对应的套节字句柄
    sockaddr_in addr;// 客户方地址
    } PER_HANDLE_DATA, *PPER_HANDLE_DATA;




    typedef struct _PER_IO_DATA // per-I/O数据
    {
    OVERLAPPED ol;// 重叠结构
    char buf[BUFFER_SIZE];// 数据缓冲区
    int nOperationType;// 操作类型
    #define OP_READ   1
    #define OP_WRITE  2
    #define OP_ACCEPT 3
    } PER_IO_DATA, *PPER_IO_DATA;




    DWORD WINAPI ServerThread(LPVOID lpParam)
    {
    // 得到完成端口对象句柄
    HANDLE hCompletion = (HANDLE)lpParam;
    printf("ServerThread Processor\n");
    DWORD dwTrans;
    PPER_HANDLE_DATA pPerHandle;
    PPER_IO_DATA pPerIO;
    while(TRUE)
    {
    // 在关联到此完成端口的所有套节字上等待I/O完成
    printf("wait GetQueuedCompletionStatus  Done \n");
    BOOL bOK = ::GetQueuedCompletionStatus(hCompletion, 
    &dwTrans, (LPDWORD)&pPerHandle, (LPOVERLAPPED*)&pPerIO, WSA_INFINITE);
    if(!bOK) // 在此套节字上有错误发生
    {
    printf("9\n");
    ::closesocket(pPerHandle->s);
    ::GlobalFree(pPerHandle);
    ::GlobalFree(pPerIO);
    continue;
    }

    if(dwTrans == 0 &&// 套节字被对方关闭
    (pPerIO->nOperationType == OP_READ || pPerIO->nOperationType == OP_WRITE))

    {
    printf("10\n");
    ::closesocket(pPerHandle->s);
    ::GlobalFree(pPerHandle);
    ::GlobalFree(pPerIO);
    continue;
    }
    printf("11\n");
    switch(pPerIO->nOperationType)// 通过per-I/O数据中的nOperationType域查看什么I/O请求完成了
    {
    case OP_READ: // 完成一个接收请求
    {
    pPerIO->buf[dwTrans] = '\0';
    printf(pPerIO -> buf);

    // 继续投递接收I/O请求
    WSABUF buf;
    buf.buf = pPerIO->buf ;
    buf.len = BUFFER_SIZE;
    pPerIO->nOperationType = OP_READ;


    DWORD nFlags = 0;
    printf("12\n");
    ::WSARecv(pPerHandle->s, &buf, 1, &dwTrans, &nFlags, &pPerIO->ol, NULL);

    }
    break;
    case OP_WRITE: // 本例中没有投递这些类型的I/O请求
    {
    WSABUF buf;
    buf.buf = pPerIO->buf ;
    buf.len = BUFFER_SIZE;
    pPerIO->nOperationType = OP_READ;
    DWORD nFlags = 0;
    ::WSASend(pPerHandle->s, &buf, 1, &dwTrans, nFlags, &pPerIO->ol, NULL);
    }
    break;
    case OP_ACCEPT:
    {
    }
    break;
    }
    }
    return 0;
    }




    void main()
    {
    // 初始化WS2_32.dll
    BYTE minorVer = 0x02;
    BYTE majorVer = 0x02;
    WSADATA wsaData;
    WORD sockVersion = MAKEWORD(minorVer, majorVer);
    if(::WSAStartup(sockVersion, &wsaData) != 0)
    {
    }
    int nPort = 5000;
    // 创建完成端口对象,创建工作线程处理完成端口对象中事件
    printf("Init NetWork\n");
    HANDLE hCompletion = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
    printf("CreateIoCompletionPort Done\n");
    ::CreateThread(NULL, 0, ServerThread, (LPVOID)hCompletion, 0, 0);
    printf("Create Server Thread \n");
    // 创建监听套节字,绑定到本地地址,开始监听
    SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, 0);
    SOCKADDR_IN si;
    si.sin_family = AF_INET;
    si.sin_port = ::ntohs(nPort);
    si.sin_addr.S_un.S_addr = INADDR_ANY;
    ::bind(sListen, (sockaddr*)&si, sizeof(si));
    ::listen(sListen, 5);


    printf("Start Listen \n");
    // 循环处理到来的连接
    while(TRUE)
    {
    // 等待接受未决的连接请求
    printf("Wait for Connect...\n");
    SOCKADDR_IN saRemote;
    int nRemoteLen = sizeof(saRemote);
    SOCKET sNew = ::accept(sListen, (sockaddr*)&saRemote, &nRemoteLen);


    // 接受到新连接之后,为它创建一个per-handle数据,并将它们关联到完成端口对象。
    PPER_HANDLE_DATA pPerHandle = 
    (PPER_HANDLE_DATA)::GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));
    pPerHandle->s = sNew;
    memcpy(&pPerHandle->addr, &saRemote, nRemoteLen);
    ::CreateIoCompletionPort((HANDLE)pPerHandle->s, hCompletion, (DWORD)pPerHandle, 0);
    printf("Accept connect , assigned a CreateIoCompletionPort \n");
    // 投递一个接收请求
    PPER_IO_DATA pPerIO = (PPER_IO_DATA)::GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
    pPerIO->nOperationType = OP_READ;
    WSABUF buf;
    buf.buf = pPerIO->buf;
    buf.len = BUFFER_SIZE;
    DWORD dwRecv;
    DWORD dwFlags = 0;
    printf(" Mail a request!!! \n");
    ::WSARecv(pPerHandle->s, &buf, 1, &dwRecv, &dwFlags, &pPerIO->ol, NULL);
    }


    ::WSACleanup();
    }


    测试程序:

    //////////////////////////////////////////////////////////
    // TCPClient.cppÎļþ
    #include <stdio.h>
    #include <winsock2.h>
    #pragma comment(lib, "ws2_32.lib")


    int main()
    {
    BYTE minorVer = 0x02;
    BYTE majorVer = 0x02;
    //WS2_32.dll
    WSADATA wsaData;
    WORD sockVersion = MAKEWORD(minorVer, majorVer);
    if(::WSAStartup(sockVersion, &wsaData) != 0)
    {
    exit(0);
    }


    // ´´½¨Ì×½Ú×Ö
    SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(s == INVALID_SOCKET)
    {
    printf(" Failed socket() \n");
    return 0;
    }

    // Ò²¿ÉÒÔÔÚÕâÀïµ÷ÓÃbindº¯Êý°ó¶¨Ò»¸ö±¾µØµØÖ·
    // ·ñÔòϵͳ½«»á×Ô¶¯°²ÅÅ

    // ÌîдԶ³ÌµØÖ·ÐÅÏ¢
    sockaddr_in servAddr; 
    servAddr.sin_family = AF_INET;
    servAddr.sin_port = htons(5000);
    // ×¢Ò⣬ÕâÀïÒªÌîд·þÎñÆ÷³ÌÐò£¨TCPServer³ÌÐò£©ËùÔÚ»úÆ÷µÄIPµØÖ·
    // Èç¹ûÄãµÄ¼ÆËã»úûÓÐÁªÍø£¬Ö±½ÓʹÓÃ127.0.0.1¼´¿É
    servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

    if(::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1)
    {
    printf(" Failed connect() \n");
    return 0;
    }

    // ·¢ËÍÊý¾Ý
    send(s,"dddd",sizeof("dddd"),0);
    // ½ÓÊÕÊý¾Ý
    char buff[256];
    int nRecv = ::recv(s, buff, 256, 0);
    if(nRecv > 0)
    {
    buff[nRecv] = '\0';
    printf(" ½ÓÊÕµ½Êý¾Ý£º%s", buff);
    }

    // ¹Ø±ÕÌ×½Ú×Ö
    ::closesocket(s);


    ::WSACleanup();
    return 0;
    }

  • 相关阅读:
    Oracle之sqlplus显示中文出现乱码
    如何让谷歌取消自动重定向
    装饰器模式
    代理模式
    适配器模式
    protobuf接口调用报错:java.nio.charset.MalformedInputException: Input length = 1
    本地tomcat调用远程接口报错:java.lang.reflect.InvocationTargetException
    windows下安装weblogic
    windows下使用linux命令搜文件
    单例模式
  • 原文地址:https://www.cnblogs.com/eaglezzb/p/4176558.html
Copyright © 2020-2023  润新知