本文是我在学习IOCP的时候,第一次写一个完整的例子出来,当然了,参考了CSDN上一些朋友的博客,大部分都是按照他们的思路写的,毕竟我是初学者,参考现成的学起来比较快。当然了,真正用到项目中的IOCP肯定不止这么简单的,还有内存池,环形缓冲区,socket连接池等高端内容,后面我会参考一些例子,写出一个完整的给大家看。

/************************************************************************

        FileName:iocp.h
        Author  :eliteYang
        http://www.cppfans.org

************************************************************************/
#ifndef __IOCP_H__
#define __IOCP_H__

#include
#include 

#define DefaultPort 20000
#define DataBuffSize 8 * 1024

typedef struct
{
    OVERLAPPED overlapped;
    WSABUF databuff;
    CHAR buffer[ DataBuffSize ];
    DWORD bytesSend;
    DWORD bytesRecv;
}PER_IO_OPERATEION_DATA, *LPPER_IO_OPERATION_DATA;

typedef struct
{
    SOCKET socket;
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;

#endif

前面讲过IOCP里面一个很重要的东西就是IO重叠了,所以结构体里有一个OVERLAPPED结构。

/************************************************************************

        FileName:iocp.cpp
        Author  :eliteYang
        http://www.cppfans.org

************************************************************************/

#include "iocp.h"
#include 

using namespace std;

#pragma comment( lib, "Ws2_32.lib" )

DWORD WINAPI ServerWorkThread( LPVOID CompletionPortID );

void main()
{

    SOCKET acceptSocket;
    HANDLE completionPort;
    LPPER_HANDLE_DATA pHandleData;
    LPPER_IO_OPERATION_DATA pIoData;
    DWORD recvBytes;
    DWORD flags;

    WSADATA wsaData;
    DWORD ret;
    if ( ret = WSAStartup( 0x0202, &wsaData ) != 0 )
    {
        std::cout << "WSAStartup failed. Error:" << ret << std::endl;
        return;
    }

    completionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0 );
    if ( completionPort == NULL )
    {
        std::cout << "CreateIoCompletionPort failed. Error:" << GetLastError() << std::endl;
        return;
    }

    SYSTEM_INFO mySysInfo;
    GetSystemInfo( &mySysInfo );

    // 创建 2 * CPU核数 + 1 个线程
    DWORD threadID;
    for ( DWORD i = 0; i < ( mySysInfo.dwNumberOfProcessors * 2 + 1 ); ++i )
    {
        HANDLE threadHandle;
        threadHandle = CreateThread( NULL, 0, ServerWorkThread, completionPort, 0, &threadID );
        if ( threadHandle == NULL )
        {
            std::cout << "CreateThread failed. Error:" << GetLastError() << std::endl;
            return;
        }

        CloseHandle( threadHandle );
    }

    // 启动一个监听socket
    SOCKET listenSocket = WSASocket( AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED );
    if ( listenSocket == INVALID_SOCKET )
    {
        std::cout << " WSASocket( listenSocket ) failed. Error:" << GetLastError() << std::endl;
        return;
    }

    SOCKADDR_IN internetAddr;
    internetAddr.sin_family = AF_INET;
    internetAddr.sin_addr.s_addr = htonl( INADDR_ANY );
    internetAddr.sin_port = htons( DefaultPort );

    // 绑定监听端口
    if ( bind( listenSocket, (PSOCKADDR)&internetAddr, sizeof( internetAddr ) ) == SOCKET_ERROR )
    {
        std::cout << "Bind failed. Error:" << GetLastError() << std::endl;
        return;
    }

    if ( listen( listenSocket, 5 ) ==  SOCKET_ERROR )
    {
        std::cout << "listen failed. Error:" << GetLastError() << std::endl;
        return;
    }

    // 开始死循环,处理数据
    while ( 1 )
    {
        acceptSocket = WSAAccept( listenSocket, NULL, NULL, NULL, 0 );
        if ( acceptSocket == SOCKET_ERROR )
        {
            std::cout << "WSAAccept failed. Error:" << GetLastError() << std::endl;
            return;
        }

        pHandleData = (LPPER_HANDLE_DATA)GlobalAlloc( GPTR, sizeof( PER_HANDLE_DATA ) );
        if ( pHandleData = NULL )
        {
            std::cout << "GlobalAlloc( HandleData ) failed. Error:" << GetLastError() << std::endl;
            return;
        }

        pHandleData->socket = acceptSocket;
        if ( CreateIoCompletionPort( (HANDLE)acceptSocket, completionPort, (ULONG_PTR)pHandleData, 0 ) == NULL )
        {
            std::cout << "CreateIoCompletionPort failed. Error:" << GetLastError() << std::endl;
            return;
        }

        pIoData = ( LPPER_IO_OPERATION_DATA )GlobalAlloc( GPTR, sizeof( PER_IO_OPERATEION_DATA ) );
        if ( pIoData == NULL )
        {
            std::cout << "GlobalAlloc( IoData ) failed. Error:" << GetLastError() << std::endl;
            return;
        }

        ZeroMemory( &( pIoData->overlapped ), sizeof( pIoData->overlapped ) );
        pIoData->bytesSend = 0;
        pIoData->bytesRecv = 0;
        pIoData->databuff.len = DataBuffSize;
        pIoData->databuff.buf = pIoData->buffer;

        flags = 0;
        if ( WSARecv( acceptSocket, &(pIoData->databuff), 1, &recvBytes, &flags, &(pIoData->overlapped), NULL ) == SOCKET_ERROR )
        {
            if ( WSAGetLastError() != ERROR_IO_PENDING )
            {
                std::cout << "WSARecv() failed. Error:" << GetLastError() << std::endl;
                return;
            }
            else
            {
                std::cout << "WSARecv() io pending" << std::endl;
                return;
            }
        }
    }
}

DWORD WINAPI ServerWorkThread( LPVOID CompletionPortID )
{
    HANDLE complationPort = (HANDLE)CompletionPortID;
    DWORD bytesTransferred;
    LPPER_HANDLE_DATA pHandleData = NULL;
    LPPER_IO_OPERATION_DATA pIoData = NULL;
    DWORD sendBytes = 0;
    DWORD recvBytes = 0;
    DWORD flags;

    while ( 1 )
    {
        if ( GetQueuedCompletionStatus( complationPort, &bytesTransferred, (PULONG_PTR)&pHandleData, (LPOVERLAPPED *)&pIoData, INFINITE ) == 0 )
        {
            std::cout << "GetQueuedCompletionStatus failed. Error:" << GetLastError() << std::endl;
            return 0;
        }

        // 检查数据是否已经传输完了
        if ( bytesTransferred == 0 )
        {
            std::cout << " Start closing socket..." << std::endl;
            if ( CloseHandle( (HANDLE)pHandleData->socket ) == SOCKET_ERROR )
            {
                std::cout << "Close socket failed. Error:" << GetLastError() << std::endl;
                return 0;
            }

            GlobalFree( pHandleData );
            GlobalFree( pIoData );
            continue;
        }

        // 检查管道里是否有数据
        if ( pIoData->bytesRecv == 0 )
        {
            pIoData->bytesRecv = bytesTransferred;
            pIoData->bytesSend = 0;
        }
        else
        {
            pIoData->bytesSend += bytesTransferred;
        }

        // 数据没有发完,继续发送
        if ( pIoData->bytesRecv > pIoData->bytesSend )
        {
            ZeroMemory( &(pIoData->overlapped), sizeof( OVERLAPPED ) );
            pIoData->databuff.buf = pIoData->buffer + pIoData->bytesSend;
            pIoData->databuff.len = pIoData->bytesRecv - pIoData->bytesSend;

            // 发送数据出去
            if ( WSASend( pHandleData->socket, &(pIoData->databuff), 1, &sendBytes, 0, &(pIoData->overlapped), NULL ) == SOCKET_ERROR )
            {
                if ( WSAGetLastError() != ERROR_IO_PENDING )
                {
                    std::cout << "WSASend() failed. Error:" << GetLastError() << std::endl;
                    return 0;
                }
                else
                {
                    std::cout << "WSASend() failed. io pending. Error:" << GetLastError() << std::endl;
                    return 0;
                }
            }

            std::cout << "Send " << pIoData->buffer << std::endl;
        }
        else
        {
            pIoData->bytesRecv = 0;
            flags = 0;

            ZeroMemory( &(pIoData->overlapped), sizeof( OVERLAPPED ) );
            pIoData->databuff.len = DataBuffSize;
            pIoData->databuff.buf = pIoData->buffer;

            if ( WSARecv( pHandleData->socket, &(pIoData->databuff), 1, &recvBytes, &flags, &(pIoData->overlapped), NULL ) == SOCKET_ERROR )
            {
                if ( WSAGetLastError() != ERROR_IO_PENDING )
                {
                    std::cout << "WSARecv() failed. Error:" << GetLastError() << std::endl;
                    return 0;
                }
                else
                {
                    std::cout << "WSARecv() io pending" << std::endl;
                    return 0;
                }
            }
        }
    }
}

整个过程还是类似于最基础的socket连接方式,主要部分就是使用IOCP的两个函数,创建IOCP和检测当前的状态。

大家先凑活看吧,后面本博客会有更精彩的IOCP内容呈现给大家,我也是逐步在学习,大家稍安勿躁。