Chinaunix首页 | 论坛 | 博客
  • 博客访问: 746093
  • 博文数量: 116
  • 博客积分: 923
  • 博客等级: 准尉
  • 技术积分: 1635
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-06 21:43
个人简介

一直帮老板搬运代码!!!

文章分类
文章存档

2013年(47)

2012年(69)

分类: Windows平台

2013-02-24 17:10:52

 二、下面来看libevent的例子(自己大致修改了一些,以便方便运行)

 

1)libevent例子服务器(iocpserver.h)

 

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) 1998  Microsoft Corporation.  All Rights Reserved.
//
// Module:
//      iocpserver.h
//

#ifndef IOCPSERVER_H
#define IOCPSERVER_H


#define DEFAULT_PORT        5001
#define MAX_BUFF_SIZE       8192
#define MAX_WORKER_THREAD   16


typedef enum _IO_OPERATION {
    ClientIoAccept,
    ClientIoRead,
    ClientIoWrite
} IO_OPERATION, *PIO_OPERATION;


// data to be associated for every I/O operation on a socket
typedef struct _PER_IO_CONTEXT {
    WSAOVERLAPPED               Overlapped;
    CHAR                        Buffer[MAX_BUFF_SIZE];
    WSABUF                      wsabuf;
    int                         nTotalBytes;
    int                         nSentBytes;
    IO_OPERATION                IOOperation;
    SOCKET                      SocketAccept;
    struct _PER_IO_CONTEXT      *pIOContextForward;
} PER_IO_CONTEXT, *PPER_IO_CONTEXT;

// For AcceptEx, the IOCP key is the PER_SOCKET_CONTEXT for the listening socket,
// so we need to another field SocketAccept in PER_IO_CONTEXT. When the outstanding
// AcceptEx completes, this field is our connection socket handle.

 

// data to be associated with every socket added to the IOCP
typedef struct _PER_SOCKET_CONTEXT {
    SOCKET                      Socket;
    //linked list for all outstanding i/o on the socket
    PPER_IO_CONTEXT             pIOContext; 
    struct _PER_SOCKET_CONTEXT  *pCtxtBack;
    struct _PER_SOCKET_CONTEXT  *pCtxtForward;
} PER_SOCKET_CONTEXT, *PPER_SOCKET_CONTEXT;

 

BOOL ValidOptions(int argc, char *argv[]);


BOOL WINAPI CtrlHandler(
    DWORD dwEvent
    );

BOOL CreateListenSocket(void);

BOOL CreateAcceptSocket(
    BOOL fUpdateIOCP
    );

DWORD WINAPI WorkerThread (
    LPVOID WorkContext
    );

PPER_SOCKET_CONTEXT UpdateCompletionPort(
    SOCKET s,
    IO_OPERATION ClientIo,
    BOOL bAddToList
    );
// bAddToList is FALSE for listening socket, and TRUE for connection sockets.
// As we maintain the context for listening socket in a global structure, we
// don't need to add it to the list.

VOID CloseClient (
    PPER_SOCKET_CONTEXT lpPerSocketContext,
    BOOL bGraceful
    );


PPER_SOCKET_CONTEXT CtxtAllocate(
    SOCKET s,
    IO_OPERATION ClientIO
    );


VOID CtxtListFree(
    );

VOID CtxtListAddTo (
    PPER_SOCKET_CONTEXT lpPerSocketContext
    );

VOID CtxtListDeleteFrom(
    PPER_SOCKET_CONTEXT lpPerSocketContext
    );

 

#endif

 

 

2)libevent例子服务器(iocpserver.cpp)

 

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) 1998  Microsoft Corporation.  All Rights Reserved.
//
// Module:
//      iocpserver.cpp
//
// Abstract:
//      This program is a Winsock echo server program that uses I/O Completion Ports
//      (IOCP) to receive data from and echo data back to a sending client. The server
//      program supports multiple clients connecting via TCP/IP and sending arbitrary
//      sized data buffers which the server then echoes back to the client.  For
//      convenience a simple client program, iocpclient was developed to connect
//      and continually send data to the server to stress it.
//
//      Direct IOCP support was added to Winsock 2 and is fully implemented on the NT
//      platform.  IOCPs provide a model for developing very high performance and very
//      scablable server programs.
//
//      The basic idea is that this server continuously accepts connection requests from
//      a client program.  When this happens, the accepted socket descriptor is added to
//      the existing IOCP and an inital receive (WSARecv) is posted on that socket.  When
//      the client then sends data on that socket, a completion packet will be delivered
//      and handled by one of the server's worker threads.  The worker thread echoes the
//      data back to the sender by posting a send (WSASend) containing all the data just
//      received.  When sending the data back to the client completes, another completion
//      packet will be delivered and again handled by one of the server's worker threads. 
//      Assuming all the data that needed to be sent was actually sent, another receive
//      (WSARecv) is once again posted and the scenario repeats itself until the client
//      stops sending data.
//
//      When using IOCPs it is important to remember that the worker threads must be able
//      to distinguish between I/O that occurs on multiple handles in the IOCP as well as
//      multiple I/O requests initiated on a single handle.  The per handle data
//      (PER_SOCKET_CONTEXT) is associated with the handle as the CompletionKey when the
//      handle is added to the IOCP using CreateIoCompletionPort.  The per IO operation
//      data (PER_IO_CONTEXT) is associated with a specific handle during an I/O
//      operation as part of the overlapped structure passed to either WSARecv or
//      WSASend.  Please notice that the first member of the PER_IO_CONTEXT structure is
//      a WSAOVERLAPPED structure (compatible with the Win32 OVERLAPPED structure). 
//
//      When the worker thread unblocks from GetQueuedCompletionStatus, the key
//      associated with the handle when the handle was added to the IOCP is returned as
//      well as the overlapped structure associated when this particular I/O operation
//      was initiated.
//     
//      This program cleans up all resources and shuts down when CTRL-C is pressed. 
//      This will cause the main thread to break out of the accept loop and close all open
//      sockets and free all context data.  The worker threads get unblocked by posting 
//      special I/O packets with a NULL CompletionKey to the IOCP.  The worker
//      threads check for a NULL CompletionKey and exits if it encounters one. If CTRL-BRK
//      is pressed instead, cleanup process is same as above but instead of exit the process,
//      the program loops back to restart the server.
//
//  Usage:
//      Start the server and wait for connections on port 6001
//          iocpserver -e:6001
//
//  Build:
//      Use the headers and libs from the April98 Platform SDK or later.
//      Link with ws2_32.lib
//     
//  Author: Wei Hua, Barry Butterklee - Microsoft Developer Support
//
//


#define WIN32_LEAN_AND_MEAN
#include
#include
#include

#include "iocpserver.h"


unsigned short  g_Port          = DEFAULT_PORT;

BOOL            g_bEndServer    = FALSE;            // set to TRUE on CTRL-C

BOOL            g_bRestart      = TRUE;             // set to TRUE to CTRL-BRK

BOOL            g_bVerbose      = FALSE;

DWORD           g_dwThreadCount = 0;        //worker thread count

HANDLE          g_hIOCP         = NULL;

SOCKET          g_sdListen      = INVALID_SOCKET;

HANDLE          g_ThreadHandles[MAX_WORKER_THREAD];

PPER_SOCKET_CONTEXT g_pCtxtList = NULL;     // linked list of context info structures
                                            // maintained to allow the the cleanup
                                            // handler to cleanly close all sockets and
                                            // free resources.

CRITICAL_SECTION    g_CriticalSection;      // guard access to the global context list


 
void main (int argc, char *argv[])
    {
    SYSTEM_INFO         systemInfo;
    WSADATA             wsaData;
    SOCKET              sdAccept = INVALID_SOCKET;
    PPER_SOCKET_CONTEXT lpPerSocketContext = NULL;
    PPER_IO_CONTEXT     lpIOContext = NULL;
    DWORD               dwRecvNumBytes = 0;    
    DWORD               dwFlags = 0;           
    int                 nRet;


    //if (!ValidOptions(argc, argv))
    //    return;

 //配置端口和
 g_Port = (unsigned short)atoi("6000");

 //控制台的句柄
    if (!SetConsoleCtrlHandler(CtrlHandler, TRUE))
        {
        printf ("SetConsoleCtrlHandler failed to install console handler: %d\n",
        GetLastError());
        return;
        }

    GetSystemInfo(&systemInfo);
    g_dwThreadCount = systemInfo.dwNumberOfProcessors * 2;
   
    if (WSAStartup(0x202, &wsaData) == SOCKET_ERROR)
        {
        printf("WSAStartup failed: %d\n",WSAGetLastError());
        return;
        }
   
    InitializeCriticalSection(&g_CriticalSection);
   
    while (g_bRestart)
    {
        g_bRestart = FALSE;
        g_bEndServer = FALSE;
        __try
            {

            g_hIOCP = CreateIoCompletionPort(      
                INVALID_HANDLE_VALUE, NULL, 0, 0);
            if (NULL == g_hIOCP)
                {
                printf( "CreateIoCompletionPort failed to create I/O completion port: %d\n",
                    GetLastError());
                __leave;
                }

            for (DWORD dwCPU=0; dwCPU < g_dwThreadCount; dwCPU++)
                {
                // Create worker threads to service the overlapped I/O requests.  The decision
                // to create 2 worker threads per CPU in the system is a heuristic.  Also,
                // note that thread handles are closed right away, because we will not need them
                // and the worker threads will continue to execute.
                HANDLE  hThread;
                DWORD   dwThreadId;

                hThread = CreateThread(NULL, 0, WorkerThread, g_hIOCP, 0, &dwThreadId);
                if (hThread == NULL)
                    {
                    printf("CreateThread failed to create worker thread: %d\n",
                        GetLastError());
                    __leave;
                    }
                g_ThreadHandles[dwCPU] = hThread;
                }
           


            if (!CreateListenSocket())
                __leave;


            while (TRUE)
                {
                // Loop forever accepting connections from clients until console shuts down.

                sdAccept = WSAAccept(g_sdListen, NULL, NULL, NULL, 0);
                if (SOCKET_ERROR == sdAccept)
                    {
                    // If user hits Ctrl+C or Ctrl+Brk or console window is closed, the control
                    // handler will close the g_sdListen socket. The above WSAAccept call will
                    // fail and we thus break out the loop,
                    printf("WSAAccept: %d\n", WSAGetLastError());
                    __leave;
                    }

                // we add the just returned socket descriptor to the IOCP along with its
                // associated key data.  Also the global list of context structures
                // (the key data) gets added to a global list.
                lpPerSocketContext = UpdateCompletionPort(sdAccept, ClientIoRead, TRUE);
                if (NULL == lpPerSocketContext)
                    __leave;

                // if a CTRL-C was pressed "after" WSAAccept returns, the CTRL-C handler
                // will have set this flag and we can break out of the loop here before
                // we go ahead and post another read (but after we have added it to the
                // list of sockets to close).
                if (g_bEndServer)
                    break;

                // post initial receive on this socket
                nRet = WSARecv(
                    sdAccept,
                    &(lpPerSocketContext->pIOContext->wsabuf), 1, &dwRecvNumBytes,
                    &dwFlags,
                    &(lpPerSocketContext->pIOContext->Overlapped), NULL);
                if (nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()))
                    {
                    printf("WSARecv Failed: %d\n", WSAGetLastError());
                    CloseClient(lpPerSocketContext, FALSE);
                    }
                } //while
            }

        __finally
            {

            g_bEndServer = TRUE;

            // Cause worker threads to exit
            if (g_hIOCP)       
                {
                for (DWORD i = 0; i < g_dwThreadCount; i++)
                    PostQueuedCompletionStatus(g_hIOCP, 0, 0, NULL);
                }

            //Make sure worker threads exits.
            if (WAIT_OBJECT_0 != WaitForMultipleObjects( g_dwThreadCount,  g_ThreadHandles, TRUE, 1000))
                printf("WaitForMultipleObjects failed: %d\n", GetLastError());
            else
                for (DWORD i = 0; i < g_dwThreadCount; i++)
                {
                if (g_ThreadHandles[i] != INVALID_HANDLE_VALUE) CloseHandle(g_ThreadHandles[i]);
                g_ThreadHandles[i] = INVALID_HANDLE_VALUE;
                }

            CtxtListFree();
     
            if (g_hIOCP)   
                {
                CloseHandle(g_hIOCP);
                g_hIOCP         = NULL;
                }


            if (g_sdListen != INVALID_SOCKET)
                {
                closesocket(g_sdListen);
                g_sdListen = INVALID_SOCKET;
                }

        } //finally
   
        if (g_bRestart)
            {
            printf("\niocpserver is restarting...\n");
            }
        else
            printf("\niocpserver is exiting...\n");

        } //while (g_bRestart)
   
    DeleteCriticalSection(&g_CriticalSection);
    WSACleanup();
    SetConsoleCtrlHandler(CtrlHandler, FALSE);
    } //main

   


//  Just validate the command line options.
//
BOOL ValidOptions(int argc, char *argv[])
    {
    BOOL bRet = TRUE;


    for (int i=1; i         {
        if ((argv[i][0] =='-') || (argv[i][0] == '/'))
            {
            switch (tolower(argv[i][1]))
                {
                case 'e':
                    if (strlen(argv[i]) > 3)
                        g_Port = (unsigned short)atoi(&argv[i][3]);
                    break;

                case 'v':
                    g_bVerbose = TRUE;
                    break;

                case '?':
                    printf("Usage:\n  iocpserver [-p:port] [-v] [-?]\n");
                    printf("  -e:port\tSpecify echoing port number\n");       
                    printf("  -v\t\tVerbose\n");       
                    printf("  -?\t\tDisplay this help\n");
                    bRet = FALSE;
                    break;

                default:
                    printf("Unknown options flag %s\n", argv[i]);
                    bRet = FALSE;
                    break;
                }
            }
        }  

    return(bRet);
    }

 


//  Intercept CTRL-C or CTRL-BRK events and cause the server to initiate shutdown.
//  CTRL-BRK resets the restart flag, and after cleanup the server restarts.
BOOL WINAPI CtrlHandler (
    DWORD dwEvent
    )
    {
    SOCKET sockTemp;
   
    switch (dwEvent)
        {
        case CTRL_BREAK_EVENT:
            g_bRestart = TRUE;
        case CTRL_C_EVENT:
        case CTRL_LOGOFF_EVENT:
        case CTRL_SHUTDOWN_EVENT:
        case CTRL_CLOSE_EVENT:
            if (g_bVerbose)
                printf("CtrlHandler: closing listening socket\n");
            // cause the accept in the main thread loop to fail

            //We want to make closesocket the last call in the handler because it will
            //cause the WSAAccept to return in the main thread
            sockTemp = g_sdListen;
            g_sdListen = INVALID_SOCKET;
            g_bEndServer = TRUE;
            closesocket(sockTemp);
            break;

        default:
            // unknown type--better pass it on.
            return(FALSE);
        }
    return(TRUE);
    }

 


//  Create a listening socket.
BOOL CreateListenSocket(void)
    {
    SOCKADDR_IN si_addrlocal;
    int         nRet;
    int         nZero = 0;
    LINGER      lingerStruct;

    lingerStruct.l_onoff = 1;
    lingerStruct.l_linger = 0;


    g_sdListen = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED);
    if (INVALID_SOCKET == g_sdListen)
        {
        printf("WSASocket(g_sdListen): %d\n", WSAGetLastError());
        return(FALSE);
        }

    si_addrlocal.sin_family = AF_INET;
    si_addrlocal.sin_port = htons(g_Port);
    si_addrlocal.sin_addr.s_addr = htonl(INADDR_ANY);      
    nRet = bind(g_sdListen, (struct sockaddr *)&si_addrlocal, sizeof(si_addrlocal));
    if (SOCKET_ERROR == nRet)
        {
        printf("bind: %d\n", WSAGetLastError());
        return(FALSE);
        }

    nRet = listen(g_sdListen, 5);
    if (SOCKET_ERROR == nRet)
        {
        printf("listen: %d\n", WSAGetLastError());
        return(FALSE);
        }

 printf( "listening on port [%d]\n", g_Port );

    // Disable send buffering on the socket.  Setting SO_SNDBUF
    // to 0 causes winsock to stop bufferring sends and perform
    // sends directly from our buffers, thereby reducing CPU usage.
    nZero = 0;
    nRet = setsockopt(g_sdListen, SOL_SOCKET, SO_SNDBUF, (char *)&nZero, sizeof(nZero));
    if (SOCKET_ERROR == nRet)
        {
        printf("setsockopt(SNDBUF): %d\n", WSAGetLastError());
        return(FALSE);
        }

    // Disable receive buffering on the socket.  Setting SO_RCVBUF
    // to 0 causes winsock to stop bufferring receive and perform
    // receives directly from our buffers, thereby reducing CPU usage.
    nZero = 0;
    nRet = setsockopt(g_sdListen, SOL_SOCKET, SO_RCVBUF, (char *)&nZero, sizeof(nZero));
    if (SOCKET_ERROR == nRet)
        {
        printf("setsockopt(SO_RCVBUF): %d\n", WSAGetLastError());
        return(FALSE);
        }
       
    //nRet = setsockopt(g_sdListen, SOL_SOCKET, SO_LINGER,
                  //(char *)&lingerStruct, sizeof(lingerStruct) );
    if (SOCKET_ERROR == nRet)
       {
        printf("setsockopt(SO_LINGER): %d\n", WSAGetLastError());
        return(FALSE);
        }

    return(TRUE);
    }

 


// Worker thread that handles all I/O requests on any socket handle added to the IOCP.
//
DWORD WINAPI WorkerThread (
    LPVOID WorkThreadContext
    )
    {
    HANDLE          hIOCP = (HANDLE)WorkThreadContext;
    BOOL            bSuccess = FALSE;
    int             nRet;
    LPOVERLAPPED    lpOverlapped = NULL;
    PPER_SOCKET_CONTEXT lpPerSocketContext = NULL;
    PPER_IO_CONTEXT     lpIOContext = NULL;
    WSABUF          buffRecv;
    WSABUF          buffSend;
    DWORD           dwRecvNumBytes = 0;
    DWORD           dwSendNumBytes = 0;
    DWORD           dwFlags = 0;
    DWORD           dwIoSize;
   
    while (TRUE)
        {

        // continually loop to service io completion packets
        bSuccess = GetQueuedCompletionStatus(
                       hIOCP,
                       &dwIoSize,
                       (LPDWORD)&lpPerSocketContext,
                       &lpOverlapped,
                       INFINITE
                       );
        if (!bSuccess)
            printf("GetQueuedCompletionStatus: %d\n", GetLastError());


        if (lpPerSocketContext == NULL)
            {
            // CTRL-C handler used PostQueuedCompletionStatus to post an I/O packet with
            // a NULL CompletionKey (or if we get one for any reason).  It is time to exit.
            return(0);
            }

        if (g_bEndServer)
            {
            // main thread will do all cleanup needed - see finally block
            return 0;
            }

        if (!bSuccess || (bSuccess && (0 == dwIoSize)))
            {
            // client connection dropped, continue to service remaining (and possibly
            // new) client connections
            CloseClient(lpPerSocketContext, FALSE);
            continue;
            }


        // determine what type of IO packet has completed by checking the PER_IO_CONTEXT
        // associated with this socket.  This will determine what action to take.
        lpIOContext = (PPER_IO_CONTEXT)lpOverlapped;
        switch (lpIOContext->IOOperation)
            {
            case ClientIoRead:
                // a read operation has completed, post a write operation to echo the
                // data back to the client using the same data buffer.
                lpIOContext->IOOperation = ClientIoWrite;
                lpIOContext->nTotalBytes = dwIoSize;
                lpIOContext->nSentBytes  = 0;
                lpIOContext->wsabuf.len  = dwIoSize;
                dwFlags = 0;
                nRet = WSASend(
                    lpPerSocketContext->Socket,
                    &lpIOContext->wsabuf, 1, &dwSendNumBytes,
                    dwFlags,
                    &(lpIOContext->Overlapped), NULL);
                if (SOCKET_ERROR == nRet && (ERROR_IO_PENDING != WSAGetLastError()))
                    {
                    printf("WSASend: %d\n", WSAGetLastError());
                    CloseClient(lpPerSocketContext, FALSE);
                    }
                else if (g_bVerbose)
                    {
                    printf("WorkerThread %d: Socket(%d) Recv completed (%d bytes), Send posted\n",
                        GetCurrentThreadId(), lpPerSocketContext->Socket, dwIoSize);
                    }
                break;

            case ClientIoWrite:
                // a write operation has completed, determine if all the data intended to be
                // sent actually was sent.
                lpIOContext->IOOperation = ClientIoWrite;
                lpIOContext->nSentBytes  += dwIoSize;
                dwFlags = 0;
                if (lpIOContext->nSentBytes < lpIOContext->nTotalBytes)
                    {
                    // the previous write operation didn't send all the data,
                    // post another send to complete the operation
                    buffSend.buf = lpIOContext->Buffer + lpIOContext->nSentBytes;
                    buffSend.len = lpIOContext->nTotalBytes - lpIOContext->nSentBytes;
                    nRet = WSASend (
                        lpPerSocketContext->Socket,
                        &buffSend, 1, &dwSendNumBytes,
                        dwFlags,
                        &(lpIOContext->Overlapped), NULL);
                    if (SOCKET_ERROR == nRet && (ERROR_IO_PENDING != WSAGetLastError()))
                        {
                        printf ("WSASend: %d\n", WSAGetLastError());
                        CloseClient(lpPerSocketContext, FALSE);
                        }
                    else if (g_bVerbose)
                        {
                        printf("WorkerThread %d: Socket(%d) Send partially completed (%d bytes), Recv posted\n",
                            GetCurrentThreadId(), lpPerSocketContext->Socket, dwIoSize);
                        }
                    }
                else
    {
     if( lpIOContext->nTotalBytes >= sizeof( lpIOContext->Buffer ) )
      --lpIOContext->nTotalBytes;
     lpIOContext->Buffer[ lpIOContext->nTotalBytes ] = '\0';
     if( NULL != strstr( lpIOContext->Buffer, "qui" ) )
     {
                        //printf ("Client(%d) quit.\n", lpPerSocketContext->Socket);
                        CloseClient(lpPerSocketContext, TRUE);
     } else {
      
      // previous write operation completed for this socket, post another recv
      lpIOContext->IOOperation = ClientIoRead;
      dwRecvNumBytes = 0;
      dwFlags = 0;
      buffRecv.buf = lpIOContext->Buffer,
       buffRecv.len = MAX_BUFF_SIZE;
      nRet = WSARecv(
       lpPerSocketContext->Socket,
       &buffRecv, 1, &dwRecvNumBytes,
       &dwFlags,
       &lpIOContext->Overlapped, NULL);
      if (SOCKET_ERROR == nRet && (ERROR_IO_PENDING != WSAGetLastError()))
                        {
       printf ("WSARecv: %d\n", WSAGetLastError());
       CloseClient(lpPerSocketContext, FALSE);
                        }
      else if (g_bVerbose)
                        {
       printf("WorkerThread %d: Socket(%d) Send completed (%d bytes), Recv posted\n",
        GetCurrentThreadId(), lpPerSocketContext->Socket, dwIoSize);
                        }
                    }
    }
                break;

            } //switch
        } //while
    return(0);
    }

 


//  Allocate a context structures for the socket and add the socket to the IOCP. 
//  Additionally, add the context structure to the global list of context structures.
//
PPER_SOCKET_CONTEXT UpdateCompletionPort(
    SOCKET sd,
    IO_OPERATION ClientIo,
    BOOL bAddToList
    )
    {
    PPER_SOCKET_CONTEXT lpPerSocketContext;

   
    lpPerSocketContext = CtxtAllocate(sd, ClientIo);
    if (lpPerSocketContext == NULL)
        return(NULL);

    g_hIOCP = CreateIoCompletionPort((HANDLE)sd, g_hIOCP, (DWORD)lpPerSocketContext, 0);
    if (NULL == g_hIOCP)
        {
        printf("CreateIoCompletionPort: %d\n", GetLastError());
        if (lpPerSocketContext->pIOContext)
            HeapFree(GetProcessHeap(), 0, lpPerSocketContext->pIOContext);
        HeapFree(GetProcessHeap(), 0, lpPerSocketContext);
        return(NULL);
        }

    //The listening socket context (bAddToList is FALSE) is not added to the list.
    //All other socket contexts are added to the list.
   if (bAddToList) CtxtListAddTo(lpPerSocketContext);

    if (g_bVerbose)
        printf("UpdateCompletionPort: Socket(%d) added to IOCP\n", lpPerSocketContext->Socket);

    return(lpPerSocketContext);
    }

 


//  Close down a connection with a client.  This involves closing the socket (when
//  initiated as a result of a CTRL-C the socket closure is not graceful).  Additionally,
//  any context data associated with that socket is free'd.
//
VOID
CloseClient (
    PPER_SOCKET_CONTEXT lpPerSocketContext,
    BOOL                bGraceful
    )
    {
    EnterCriticalSection(&g_CriticalSection);
    if (!lpPerSocketContext)
        {
        printf("CloseClient: lpPerSocketContext is NULL\n");
        return;
        }
    if (g_bVerbose)
        printf("CloseClient: Socket(%d) connection closing (graceful=%s)\n",
        lpPerSocketContext->Socket, (bGraceful?"TRUE":"FALSE"));
    if (!bGraceful)
        {
        // force the subsequent closesocket to be abortative.
        LINGER  lingerStruct;

        lingerStruct.l_onoff = 1;
        lingerStruct.l_linger = 0;
        setsockopt(lpPerSocketContext->Socket, SOL_SOCKET, SO_LINGER,
                    (char *)&lingerStruct, sizeof(lingerStruct) );
        }
    closesocket(lpPerSocketContext->Socket);
    CtxtListDeleteFrom(lpPerSocketContext);
    lpPerSocketContext = NULL;
    LeaveCriticalSection(&g_CriticalSection);
    return;   
    }

 


// Allocate a socket context for the new connection. 
//
PPER_SOCKET_CONTEXT CtxtAllocate(
    SOCKET          sd,
    IO_OPERATION    ClientIO
    )
    {
    PPER_SOCKET_CONTEXT lpPerSocketContext;


    EnterCriticalSection(&g_CriticalSection);
    lpPerSocketContext = (PPER_SOCKET_CONTEXT)HeapAlloc(
        GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_SOCKET_CONTEXT));
    if (NULL == lpPerSocketContext)
        {
        printf("HeapAlloc PER_SOCKET_CONTEXT Failed: %d\n", GetLastError());
        return(NULL);
        }

    lpPerSocketContext->pIOContext = (PPER_IO_CONTEXT)HeapAlloc(
        GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_IO_CONTEXT));
    if (NULL == lpPerSocketContext->pIOContext)
        {
        HeapFree(GetProcessHeap(), 0, lpPerSocketContext);
        printf("HeapAlloc PER_IO_CONTEXT Failed: %d\n", GetLastError());
        return(NULL);
        }

    lpPerSocketContext->Socket = sd;
    lpPerSocketContext->pCtxtBack = NULL;
    lpPerSocketContext->pCtxtForward = NULL;

    lpPerSocketContext->pIOContext->Overlapped.Internal = 0;
    lpPerSocketContext->pIOContext->Overlapped.InternalHigh = 0;
    lpPerSocketContext->pIOContext->Overlapped.Offset = 0;
    lpPerSocketContext->pIOContext->Overlapped.OffsetHigh = 0;
    lpPerSocketContext->pIOContext->Overlapped.hEvent = NULL;
    lpPerSocketContext->pIOContext->IOOperation = ClientIO;
    lpPerSocketContext->pIOContext->pIOContextForward = NULL;
    lpPerSocketContext->pIOContext->nTotalBytes = 0;
    lpPerSocketContext->pIOContext->nSentBytes  = 0;
    lpPerSocketContext->pIOContext->wsabuf.buf  = lpPerSocketContext->pIOContext->Buffer;
    lpPerSocketContext->pIOContext->wsabuf.len  = sizeof(lpPerSocketContext->pIOContext->Buffer);
    LeaveCriticalSection(&g_CriticalSection);

    return(lpPerSocketContext);
    }

 

 

//  Add a client connection context structure to the globnal list of context structures.
//
VOID CtxtListAddTo (
    PPER_SOCKET_CONTEXT lpPerSocketContext
    )
    {
    PPER_SOCKET_CONTEXT     pTemp;


    EnterCriticalSection(&g_CriticalSection);
    if (g_pCtxtList == NULL)
        { 
        // add the first node to the linked list
        lpPerSocketContext->pCtxtBack    = NULL;
        lpPerSocketContext->pCtxtForward = NULL;
        g_pCtxtList = lpPerSocketContext;
        }
    else
        {
        // add node to head of list

        pTemp = g_pCtxtList;
       
        g_pCtxtList = lpPerSocketContext;
        lpPerSocketContext->pCtxtBack    = pTemp;
        lpPerSocketContext->pCtxtForward = NULL;   

 

        pTemp->pCtxtForward = lpPerSocketContext;
         }
    LeaveCriticalSection(&g_CriticalSection);
    return;
    }

 


//  Remove a client context structure from the global list of context structures.
//
VOID CtxtListDeleteFrom(
    PPER_SOCKET_CONTEXT lpPerSocketContext
    )
    {
    PPER_SOCKET_CONTEXT pBack;
    PPER_SOCKET_CONTEXT pForward;
    PPER_IO_CONTEXT     pNextIO     = NULL;
    PPER_IO_CONTEXT     pTempIO     = NULL;

    EnterCriticalSection(&g_CriticalSection);
    pBack       = lpPerSocketContext->pCtxtBack;
    pForward    = lpPerSocketContext->pCtxtForward;
    if (!lpPerSocketContext)
        {
        printf("CtxtListDeleteFrom: lpPerSocketContext is NULL\n");
        return;
        }
    pBack       = lpPerSocketContext->pCtxtBack;
    pForward    = lpPerSocketContext->pCtxtForward;


    if (pBack == NULL && pForward == NULL)
        {
        // This is the only node in the list to delete
        g_pCtxtList = NULL;
        }
    else if (pBack == NULL && pForward != NULL)
        {
        // This is the start node in the list to delete
        pForward->pCtxtBack = NULL;
        g_pCtxtList = pForward;
        }
    else if (pBack != NULL && pForward == NULL)
        {
        // This is the end node in the list to delete
        pBack->pCtxtForward = NULL;
        }
    else if (pBack && pForward)
        {
        // Neither start node nor end node in the list
        pBack->pCtxtForward = pForward;
        pForward->pCtxtBack = pBack;
        }

    // Free all i/o context structures per socket
    pTempIO = (PPER_IO_CONTEXT)(lpPerSocketContext->pIOContext);
    do
        {
        pNextIO = (PPER_IO_CONTEXT)(pTempIO->pIOContextForward);
        if (pTempIO)
            {
            //The overlapped structure is safe to free when only the posted i/o has
            //completed. Here we only need to test those posted but not yet received
            //by PQCS in the shutdown process.
            if (g_bEndServer)
                while (!HasOverlappedIoCompleted((LPOVERLAPPED)pTempIO)) Sleep(0);
            HeapFree(GetProcessHeap(), 0, pTempIO);
            pTempIO = NULL;
            }
        pTempIO = pNextIO;
        } while (pNextIO);

    HeapFree(GetProcessHeap(), 0, lpPerSocketContext);
    lpPerSocketContext = NULL;
    LeaveCriticalSection(&g_CriticalSection);
    return;
    }

 

//  Free all context structure in the global list of context structures.
//
VOID CtxtListFree()
    {
    PPER_SOCKET_CONTEXT     pTemp1, pTemp2;

    EnterCriticalSection(&g_CriticalSection);
    pTemp1 = g_pCtxtList;
    while (pTemp1)
        {
        pTemp2 = pTemp1->pCtxtBack;
        CloseClient(pTemp1, FALSE);
        pTemp1 = pTemp2;
        }
    LeaveCriticalSection(&g_CriticalSection);
    return;
    }

 

 

3)libevent例子客户端(EventIOCPClient.cpp)

 

/*
* Copyright 2008 Stephen Liu
* For license terms, see the file COPYING along with this library.
*/

#include
#include
#include

#include
#include
#include
#include
#include
#include
#include

#pragma comment(lib,"ws2_32")
#pragma comment(lib,"mswsock")
#pragma comment(lib,"advapi32")

static const char * gHost = "127.0.0.1";
static int gPort = 6000;
static int gMsgs = 10;
static int gClients = 10;

struct SP_TestStat {
 int mRecvFail;
 int mWSARecvFail;

 int mSendFail;
 int mWSASendFail;

 int mGQCSFail;
};

static struct SP_TestStat gStat;
static time_t gStartTime = 0;

struct SP_TestEvent {
 enum { eEventRecv, eEventSend };

 OVERLAPPED mOverlapped;
 WSABUF mWsaBuf;
 int mType;
};

struct SP_TestClient {
 SOCKET mFd;

 SP_TestEvent mRecvEvent;
 SP_TestEvent mSendEvent;

 int mSendMsgs;
 int mRecvMsgs;
 int mIsStop;
};

void showUsage( const char * program )
{
 printf( "Stress Test Tools\n" );
 printf( "Usage: %s [-h ] [-p ] [-c ] [-m ]\n", program );
 printf( "\t-h default is %s\n", gHost );
 printf( "\t-p default is %d\n", gPort );
 printf( "\t-c how many clients, default is %d\n", gClients );
 printf( "\t-m messages per client, default is %d\n", gMsgs );
 printf( "\n" );
}

void close_client( SP_TestClient * client )
{
 if( 0 == client->mIsStop ) {
  client->mIsStop = 1;
  gClients--;
 }
}

void on_read( SP_TestClient * client, SP_TestEvent * event )
{
 char buffer[ 4096 ] = { 0 };
 //int bytesTransferred = recv( (int)client->mFd, buffer, sizeof( buffer ), 0 );

 /*if( bytesTransferred <= 0 ) {
  if( WSAEWOULDBLOCK != WSAGetLastError() ) {
   if( bytesTransferred < 0 ) {
    printf( "recv fail, errno %d\n", WSAGetLastError() );
    gStat.mRecvFail++;
   }
   close_client( client );
   return;
  }
 }*/

 /*for( int i = 0; i < bytesTransferred; i++ ) {
  if( '\n' == buffer[i] ) client->mRecvMsgs++;
 }*/

 //if( client->mRecvMsgs >= gMsgs ) close_client( client );

 memset( &( event->mOverlapped ), 0, sizeof( OVERLAPPED ) );
 event->mType = SP_TestEvent::eEventRecv;
 event->mWsaBuf.buf = NULL;
 event->mWsaBuf.len = 0;

 DWORD recvBytes = 0, flags = 0;
 if( SOCKET_ERROR == WSARecv( (SOCKET)client->mFd, &( event->mWsaBuf ), 1,
  &recvBytes, &flags, &( event->mOverlapped ), NULL ) ) {
   if( ERROR_IO_PENDING != WSAGetLastError() ) {
    gStat.mWSARecvFail++;
    printf( "WSARecv fail, errno %d\n", WSAGetLastError() );
    close_client( client );
   }
 }
}

void on_write( SP_TestClient * client, SP_TestEvent * event )
{
 if( client->mSendMsgs < gMsgs ) {
  client->mSendMsgs++;

  char buffer[ 4096 ] = { 0 };
  if( client->mSendMsgs >= gMsgs ) {
   _snprintf( buffer, sizeof( buffer ), "quit\n" );
  } else {
   _snprintf( buffer, sizeof( buffer ),
    "mail #%d, It's good to see how people hire; "
    "that tells us how to market ourselves to them.\n", client->mSendMsgs );
  }

  //int bytesTransferred = send( (SOCKET)client->mFd, buffer, strlen( buffer ), 0 );
  /*if( bytesTransferred <= 0 ) {
   if( bytesTransferred < 0 ) {
    printf( "send fail, errno %d\n", WSAGetLastError() );
    gStat.mSendFail++;
   }
   close_client( client );
   return;
  }*/

  DWORD sendBytes = 0;

  event->mType = SP_TestEvent::eEventSend;
  memset( &( event->mOverlapped ), 0, sizeof( OVERLAPPED ) );
  event->mWsaBuf.buf = NULL;
  event->mWsaBuf.len = 0;

  if( SOCKET_ERROR == WSASend( (SOCKET)client->mFd, &( event->mWsaBuf ), 1,
   &sendBytes, 0, &( event->mOverlapped ), NULL ) ) {
    if( ERROR_IO_PENDING != WSAGetLastError() ) {
     gStat.mWSASendFail++;
     printf( "WSASend fail, errno %d\n", WSAGetLastError() );
     close_client( client );
    }
  }
 } else {
  // do nothing
 }
}

void eventLoop( HANDLE hIocp )
{
 DWORD bytesTransferred = 0;
 SP_TestClient * client = NULL;
 SP_TestEvent * event = NULL;

 BOOL isSuccess = GetQueuedCompletionStatus( hIocp, &bytesTransferred,
  (DWORD*)&client, (OVERLAPPED**)&event, 100 );
 DWORD lastError = WSAGetLastError();

 if( ! isSuccess ) {
  if( NULL != client ) {
   gStat.mGQCSFail++;
   close_client( client );
  }
  return;
 }

 if( SP_TestEvent::eEventRecv == event->mType ) {
  on_read( client, event );
  return;
 }

 if( SP_TestEvent::eEventSend == event->mType ) {
  on_write( client, event );
  return;
 }
}

void parse_arg( int argc, char * argv[] )
{
 for( int i = 1; i < argc; i+=2 ) {
  if( 0 == strcmp( argv[i], "-h" ) ) {
   gHost = argv[i+1];
  }else if( 0 == strcmp( argv[i], "-p" ) ) {
   gPort = atoi( argv[i+1] );
  } else if( 0 == strcmp( argv[i], "-c" ) ) {
   gClients = atoi( argv[i+1] );
  } else if( 0 == strcmp( argv[i], "-m" ) ) {
   gMsgs = atoi( argv[i+1] );
  } else {
   showUsage( argv[0] );
   exit( 0 );
  }
 }
}

int main( int argc, char * argv[] )
{
 //parse_arg( argc, argv );

#ifdef SIGPIPE
 signal( SIGPIPE, SIG_IGN );
#endif

 WSADATA wsaData;

 int err = WSAStartup( MAKEWORD( 2, 0 ), &wsaData );
 if ( err != 0 ) {
  printf( "Couldn't find a useable winsock.dll.\n" );
  return -1;
 }

 HANDLE hIocp = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0 );
 if( NULL == hIocp ) {
  printf( "CreateIoCompletionPort failed, errno %d\n", WSAGetLastError() );
  return -1;
 }

 SP_TestClient * clientList = (SP_TestClient*)calloc( gClients, sizeof( SP_TestClient ) );

 struct sockaddr_in sin;
 memset( &sin, 0, sizeof(sin) );
 sin.sin_family = AF_INET;
 sin.sin_addr.s_addr = inet_addr( gHost );
 sin.sin_port = htons( gPort );

 int totalClients = gClients, i = 0;

 printf( "Create %d connections to server, it will take some minutes to complete.\n", gClients );
 for( i = 0; i < gClients; i++ ) {
  SP_TestClient * client = clientList + i;
  memset( client, 0, sizeof( SP_TestClient ) );

  client->mFd = socket( AF_INET, SOCK_STREAM, 0 );
  if( client->mFd < 0 ) {
   printf( "socket failed, errno %d, %s\n", errno, strerror( errno ) );
   return -1;
  }

  if( connect( client->mFd, (struct sockaddr *)&sin, sizeof(sin) ) != 0) {
   printf( "connect failed, errno %d, %s\n", errno, strerror( errno ) );
   return -1;
  }

  if( NULL == CreateIoCompletionPort( (HANDLE)client->mFd, hIocp, (DWORD)client, 0 ) ) {
   printf( "CreateIoCompletionPort failed, errno %d\n", WSAGetLastError() );
   return -1;
  }

  unsigned long nonblocking = 1;
  ioctlsocket( client->mFd, FIONBIO, (unsigned long*) &nonblocking );

  if( 0 == ( i % 10 ) ) printf( "." );
 }

 for( i = 0; i < gClients; i++ ) {
  SP_TestClient * client = clientList + i;
  on_write( client, &( client->mSendEvent ) );
  on_read( client, &( client->mRecvEvent ) );
 }

 printf( "\n" );

 time( &gStartTime );

 struct _timeb startTime, stopTime;

 _ftime( &startTime );

 time_t lastInfoTime = time( NULL );

 // start event loop until all clients are exit
 while( gClients > 0 ) {
  eventLoop( hIocp );

  if( time( NULL ) - lastInfoTime > 5 ) {
   time( &lastInfoTime );
   printf( "waiting for %d client(s) to exit\n", gClients );
  }
 }

 _ftime( &stopTime );

 double totalTime = (double) ( 1000000 * ( stopTime.time - startTime.time )
  + ( stopTime.millitm - startTime.millitm ) ) / 1000000;

 // show result
 printf( "\n\nTest result :\n" );
 printf( "Clients : %d, Messages Per Client : %d\n", totalClients, gMsgs );
 printf( "Failure : send %d, WSASend %d, recv %d, WSARecv %d, GQCS %d\n",
  gStat.mSendFail, gStat.mWSASendFail, gStat.mRecvFail,
  gStat.mWSARecvFail, gStat.mGQCSFail );
 printf( "ExecTimes : %.6f seconds\n\n", totalTime );

 printf( "client\tSend\tRecv\n" );
 int totalSend = 0, totalRecv = 0;
 for( i = 0; i < totalClients; i++ ) {
  SP_TestClient * client = clientList + i;

  //printf( "client#%d : %d\t%d\n", i, client->mSendMsgs, client->mRecvMsgs );

  totalSend += client->mSendMsgs;
  totalRecv += client->mRecvMsgs;

  if( INVALID_HANDLE_VALUE != (HANDLE)client->mFd ) {
   closesocket( client->mFd );
  }
 }

 printf( "total   : %d\t%d\n", totalSend, totalRecv );
 printf( "average : %.0f/s\t%.0f/s\n", totalSend / totalTime, totalRecv / totalTime );

 free( clientList );

 CloseHandle( hIocp );

 return 0;
}

 

 

总结:

1、iocp一般的编写和库iocp实现都差不多,都是以一种“生产者和消费者”模式进行开发windows服务器。

2、个人觉得,这个iocp模式只适合于多线程模式。

3、有机会的朋友可以试,每个线程自己处理接收和发送的情况,或每个进程自己处理接收和发送的形式(这可能非常不适合于windows);因为这样可能更有利于资源的利用,具体自己体验体验吧。

 

阅读(11032) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

光速2019-09-19 10:24:09

函数 CtxtListDeleteFrom中的
else if (pBack == NULL && pForward != NULL)
        {
        // This is the start node in the list to delete
        pForward->pCtxtBack = NULL;
        g_pCtxtList = pForward;
        }
   &nbs