windows下iocp的简单代码

//////server
#include <WinSock2.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>

//该例子代码在并发情况下存在问题,需要对clientRecord进行mutex保护才能保证操作的有效性。

#define HOST_PORT                   10000
#define MAX_BUFF_SIZE               8192


typedef struct msg_watch_para_struct msg_watch_para_t;
struct msg_watch_para_struct {
    HANDLE      cp;
};

typedef struct wsa_data_struct wsa_data_t;
struct wsa_data_struct {
    WSAOVERLAPPED               Overlapped;

    char                        Buffer[MAX_BUFF_SIZE];
    WSABUF                      wsabuf;
    int                         nTotalBytes;
    int                         nSentBytes;
    SOCKET                      SocketAccept; 
};

typedef struct port_info_struct port_info_t;
struct port_info_struct {
    int         client_socket;
    wsa_data_t  *pIOContext;
};

void CALLBACK CompletionROUTINE(
  IN DWORD dwError, 
  IN DWORD cbTransferred, 
  IN LPWSAOVERLAPPED lpOverlapped, 
  IN DWORD dwFlags
)
{
    //仅仅是为了看lpOverlapped的值
    wsa_data_t      *was_data = (wsa_data_t*)lpOverlapped;
}


//接收线程
unsigned
__stdcall
msg_watch_thread(void*   p)
{
    msg_watch_para_t    *para = (msg_watch_para_t*)p;
    port_info_t         *port_info;
    char                buf[1024 * 11];
    LPWSAOVERLAPPED     lpOverlapped = NULL;
    wsa_data_t          *was_data;

    BOOL bSuccess = FALSE;
    DWORD dwIoSize = 0;

    while (1)
    {
        bSuccess = GetQueuedCompletionStatus(para->cp, &dwIoSize,
                                                (PDWORD_PTR)&port_info,
                                                &lpOverlapped, 
                                                INFINITE);
        if (!bSuccess)
        {
            DWORD ecode = GetLastError();
        }

        was_data = (wsa_data_t*)lpOverlapped;

        {
            DWORD dwRecvNumBytes = 0;     
            int nRet = 0;
            DWORD dwFlags = 0;
            while (TRUE)
            {
                nRet = WSARecv(port_info->client_socket, &(port_info->pIOContext->wsabuf), 
                                1, &dwRecvNumBytes, &dwFlags,
                                &(port_info->pIOContext->Overlapped), NULL);
                if( nRet == SOCKET_ERROR  ) 
                {
                    if ((ERROR_IO_PENDING != WSAGetLastError()))    //数据读取完成
                        break;

                    //出现其他错误,关闭端口,干掉session
                    printf("WSARecv() Failed: %d\n", WSAGetLastError());
                }
            }
            buf[1023] = 0;
            send(port_info->client_socket, buf, 1024, 0);
        }
    }

    return 0;
}

int main()
{
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;
    msg_watch_para_t    watch_para;


    wVersionRequested = MAKEWORD( 2, 2 );

    err = WSAStartup( wVersionRequested, &wsaData );

    if ( err != 0 )
    {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */
        return 0;
    }

    /* Confirm that the WinSock DLL supports 2.2.*/
    /* Note that if the DLL supports versions greater    */
    /* than 2.2 in addition to 2.2, it will still return */
    /* 2.2 in wVersion since that is the version we      */
    /* requested.                                        */
    if ( LOBYTE( wsaData.wVersion ) != 2 ||
            HIBYTE( wsaData.wVersion ) != 2 )
    {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */
        WSACleanup( );
        return 0;
    }

    int servfd, clientfd;

    //servfd = socket(AF_INET, SOCK_STREAM, 0);
    servfd = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED);

    if(servfd < 0)
    {
        printf("server create socket failed.\n");

        return 0;
    }

    int on = 1;

    if(setsockopt(servfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)) < 0)
    {
        printf("set socket option failed.\n");

        return 0;
    }

    struct sockaddr_in servaddr;

    struct sockaddr_in clientaddr;

    memset(&servaddr,0,sizeof(servaddr));

    memset(&clientaddr,0,sizeof(clientaddr));

    servaddr.sin_family = AF_INET;

    servaddr.sin_port = htons(HOST_PORT);

    servaddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

    if(bind(servfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
    {
        printf("bind failed.\n");
        return 0;
    }

    if(listen(servfd,10) < 0)
    {
        printf("listen failed.\n");
        return 0;
    }

    int length = sizeof(clientaddr);

    watch_para.cp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 10);

    //创建消息事件处理线程
    _beginthreadex((void*)NULL,
                       1024 * 512,
                       msg_watch_thread, 
                       &watch_para,
                       0,
                       NULL);


    for(;;)
    {
        HANDLE      cp;
        port_info_t *port_info;
        //接收用户连接
        //clientfd = accept(servfd, (struct sockaddr *)&clientaddr,&length);
        clientfd = WSAAccept(servfd, NULL, NULL, NULL, 0);
        if ( clientfd == SOCKET_ERROR ) 
        {
            printf("socket error.\n");
        }

        //初始化端口
        port_info = (port_info_t*)malloc(sizeof(port_info_t));
        port_info->pIOContext = (wsa_data_t*)malloc(sizeof(wsa_data_t));
        {
            port_info->client_socket = clientfd;
            port_info->pIOContext->Overlapped.Internal = 0;
            port_info->pIOContext->Overlapped.InternalHigh = 0;
            port_info->pIOContext->Overlapped.Offset = 0;
            port_info->pIOContext->Overlapped.OffsetHigh = 0;
            port_info->pIOContext->Overlapped.hEvent = NULL;
            port_info->pIOContext->nTotalBytes = 0;
            port_info->pIOContext->nSentBytes  = 0;
            port_info->pIOContext->wsabuf.buf  = port_info->pIOContext->Buffer;
            port_info->pIOContext->wsabuf.len  = sizeof(port_info->pIOContext->Buffer);
        }

        //将端口加入iocp的数组
        cp = CreateIoCompletionPort((HANDLE)clientfd, watch_para.cp, (ULONG_PTR)port_info, 0);
        if (cp == INVALID_HANDLE_VALUE)
        {
            DWORD ecode = GetLastError();
        }

        {
            DWORD dwRecvNumBytes = 0;     
            int nRet = 0;
            DWORD dwFlags = 0;
            nRet = WSARecv(clientfd, &(port_info->pIOContext->wsabuf), 
                            1, &dwRecvNumBytes, &dwFlags,
                            &(port_info->pIOContext->Overlapped), NULL);
            if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) 
            {
                printf("WSARecv() Failed: %d\n", WSAGetLastError());
            }
        }
    }

    closesocket(servfd);

    return 0;
}

只是简单的服务器端iocp的代码实现。msg_watch_thread函数完成消息的接收,这个结构设计的目的是使用专门的线程获得消息事件,但本身并不处理消息事件的内容,再次生成任务抛出,由其他的线程进行消息的处理。通过该种方式简化线程工作内容,使不同的线程专注于某个特定领域的工作,每个iocp的对象只需要一个线程进行对应,如果端口较多,并发量一个线程无法承担,则可以增加新的消息监视线程。

不过这种模型下,任务的生成可能成为处理的瓶颈。任务加入队列和从队列中取出将被多个线程争用。不过可以通过将任务的分成多个不同类型的队列,使用多组队列达到并发的效果。

posted @ 2012-07-02 17:56  惡盈好謙  阅读(598)  评论(0编辑  收藏  举报