7种网络编程I/O模型代码实现实例

本文详细介绍了网络编程中常见的多种IO模型,包括阻塞模型、非阻塞模型、select模型、异步选择模型、事件选择模型、重叠I/O模型及IOCP模型。每种模型都配有具体的代码实现,旨在帮助读者深入理解各种模型的特点和适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载自:http://blog.csdn.net/woshinia/article/details/8585930#


部分代码参考《[WINDOWS网络与通信程序设计].王艳平》,网络中一些I/O模型的代码都没有对socket是否可写做过深入研究,我这边会提供一些解决方法。

阻塞模式下,send会发生阻塞(非阻塞模式下send返回WSAEWOULDBLOCK错误,重叠I/O下表现为投递的发送请求一直无法完成)的情况一般可以分为3种 :

  1. 服务器虽然发送了大量数据,但客户端并未调用recv函数去接。
  2. 网络状况不佳,发送缓冲区中的数据一直发不出去。
  3. 发送数据量很大,如下载功能,协议发送数据的速度比不上send函数将数据拷贝到发送缓冲区的速度。

对于1,2情况,我们似乎可以直接关闭套接字,让客户端重新请求。但对于3,却不行。而且实际操作过程中,我们无法区分是1,2,还是3,我们能做的是尽量去保证发送的正确性。当然防止1情况或者2情况中长时间网络不畅,可以设定超时。若socket一直处于不可写状态超过1分钟,那么就关闭套接字。在最后的IOCP模型中就加入了这种超时机制。其他模型若要加入,可参考它来做。


一,基本的阻塞模型


#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>

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

DWORD WINAPI WorkThread(void* param)
{
    SOCKET* psClient = (SOCKET*)param;
    char buf[4096];
    while(true)
    {
        int len = recv(*psClient,buf,4096,0);
        if(len <= 0)
        {
            printf("recv失败!%d\n",WSAGetLastError());
            Sleep(5000);
            break;
        }
        buf[len] = '\0';
        printf("收到数据:%s\n",buf);
    }
    closesocket(*psClient);
    delete psClient;
    return 0;
}

int main()
{
    WSAData wsaData;
    if(0 != WSAStartup(MAKEWORD(2,2),&wsaData))
    {
        printf("WSAStartup失败!\n",WSAGetLastError());
        Sleep(5000);
        return 0;
    }
    USHORT nPort = 3456;
    SOCKET sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(nPort);
    sin.sin_addr.S_un.S_addr = INADDR_ANY;

    if(SOCKET_ERROR == ::bind(sListen,(sockaddr*)&sin,sizeof(sin)))
    {
        printf("bind失败!%d\n",WSAGetLastError());
        Sleep(5000);
        return -1;
    }

    ::listen(sListen,5);

    while(true)
    {
        sockaddr_in addrRemote;
        int nAddrLen = sizeof(addrRemote);
        SOCKET *psClient = new SOCKET;
        *psClient = accept(sListen,(sockaddr*)&addrRemote,&nAddrLen);
        HANDLE hThread = CreateThread(NULL,0,WorkThread,psClient,0,NULL);
        CloseHandle(hThread);
    }
    closesocket(sListen);
    WSACleanup();
}

二,无任何优化的非阻塞模型


#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>
#include <vector>
using namespace std;

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

CRITICAL_SECTION g_cs;
HANDLE           g_StartEvent;
vector<SOCKET> g_vecClients;
int g_iVecSize = 0;
DWORD WINAPI WorkThread(void* param)
{
    char buf[4096];
    while(1)
    {
        if(g_vecClients.empty())
        {
            ResetEvent(g_StartEvent);
            WaitForSingleObject(g_StartEvent,INFINITE);
        }

        EnterCriticalSection(&g_cs);
        for(vector<SOCKET>::iterator it = g_vecClients.begin();it != g_vecClients.end();)
        {
            int len = recv(*it,buf,4096,0);
            if(len == SOCKET_ERROR)
            {
                if(WSAEWOULDBLOCK != WSAGetLastError())
                {
                    printf("recv Error:%d\n",WSAGetLastError());
                    closesocket(*it);
                    it = g_vecClients.erase(it);
                }
                else
                {
                    printf("%d.",*it);
                    ++it;
                }
            }
            else
            {
                buf[len] = 0;
                printf("收到数据: %s\n",buf);
                ++it;
            }
        }
        LeaveCriticalSection(&g_cs);
        Sleep(100);

    }
    return 0;
}

int main()
{
    InitializeCriticalSectionAndSpinCount(&g_cs,4000);
    g_StartEvent = CreateEvent(NULL,FALSE,FALSE,NULL);

    WSAData wsaDate;
    WSAStartup(MAKEWORD(2,2),&wsaDate);
    USHORT nport = 3456;
    u_long ul = 1;
    SOCKET s = socket(AF_INET,SOCK_STREAM,0);
    ioctlsocket(s,FIONBIO,&ul);
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(nport);
    sin.sin_addr.S_un.S_addr = ADDR_ANY;

    if(SOCKET_ERROR == ::bind(s,(sockaddr*)&sin,sizeof(sin)))
    {
        return -1;
    }

    ::listen(s,5);

    HANDLE hThread = CreateThread(NULL,0,WorkThread,NULL,0,NULL);
    CloseHandle(hThread);

    while(true)
    {
        sockaddr_in addrRemote;
        int nAddrLen = sizeof(addrRemote);
        SOCKET sClient = accept(s,(sockaddr*)&addrRemote,&nAddrLen);
        if(sClient != SOCKET_ERROR)
        {
            EnterCriticalSection(&g_cs);
            g_vecClients.push_back(sClient);
            LeaveCriticalSection(&g_cs);
            if(g_vecClients.size() == 1)
                SetEvent(g_StartEvent);
        }
        else if(WSAEWOULDBLOCK == WSAGetLastError())
        {
            printf(".");
            Sleep(100);
        }
        else
        {
            printf("accept failed! %d\n",WSAGetLastError());
        }
    }
    closesocket(s);
    WSACleanup();
    CloseHandle(g_StartEvent);
    DeleteCriticalSection(&g_cs);
}

三,select模型


#include <WinSock2.h>
#include <Windows.h>
#include <MSWSock.h>
#include <stdio.h>
#include <map>
using namespace std;


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


struct ThreadObj{
    OVERLAPPED *pOl;
    HANDLE s;
};


int g_iIndex = 0;
map<SOCKET,char*> g_map;


int main()
{
    WSAData wsaData;
    if(0 != WSAStartup(MAKEWORD(2,2),&wsaData))
    {
        printf("初始化失败!%d\n",WSAGetLastError());
        Sleep(5000);
        return -1;
    }
    USHORT nport = 3456;
    SOCKET sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    u_long ul = 1;
    ioctlsocket(sListen,FIONBIO,&ul);
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(nport);
    sin.sin_addr.S_un.S_addr = ADDR_ANY;


    if(SOCKET_ERROR == bind(sListen,(sockaddr*)&sin,sizeof(sin)))
    {
        printf("bind failed!%d\n",WSAGetLastError());
        Sleep(5000);
        return -1;
    }


    listen(sListen,5);


    //1)初始化一个套接字集合fdSocket,并将监听套接字放入
    fd_set fdSocket;
    FD_ZERO(&fdSocket);
    FD_SET(sListen,&fdSocket);
    TIMEVAL time={1,0};
    char buf[4096];
    fd_set fdWrite;
    FD_ZERO(&fdWrite);
    while(true)
    {
        //2)将fdSocket的一个拷贝fdRead传给select函数
        fd_set fdRead = fdSocket;
        fd_set fdTmp = fdWrite;
        int nRetAll = 0;
        if(fdTmp.fd_count > 0)
            nRetAll = select(0,&fdRead,&fdTmp,NULL,NULL/*&time*/);//若不设置超时则select为阻塞
        else
            nRetAll = select(0,&fdRead,NULL,NULL,NULL/*&time*/);
        if(nRetAll > 0)
        {
            //3)通过将原来的fdSocket和被select处理过的fdRead进行比较,决定由哪些socket有数据可以读取
            for(int i=0;i<fdSocket.fd_count;i++)
            {
                if(FD_ISSET(fdSocket.fd_array[i],&fdRead))
                {
                    if(fdSocket.fd_array[i] == sListen)
                    {
                        if(fdSocket.fd_count < FD_SETSIZE)
                        {
                            sockaddr_in addrRemote;
                            int nAddrLen = sizeof(addrRemote);
                            SOCKET sClient = accept(sListen,(sockaddr*)&addrRemote,&nAddrLen);
                            FD_SET(sClient,&fdSocket);
                            printf("接收到连接:(%s)\n",inet_ntoa(addrRemote.sin_addr));
                        }
                        else
                        {
                            printf("连接数量已达上限!\n");
                            continue;
                        }
                    }
                    else
                    {
                        int nRecv = recv(fdSocket.fd_array[i],buf,4096,0);
                        if(nRecv > 0)
                        {
                            buf[nRecv] = 0;

                            printf("收到数据:%s\n",buf);

                            int nRet = send(fdSocket.fd_array[i],buf,nRecv,0);
                            if(nRet <= 0)
                            {
                                SOCKET s = fdSocket.fd_array[i];
                                if(GetLastError() == WSAEWOULDBLOCK)
                                {                                   
                                    if(g_map.find(s) == g_map.end())
                                    {
                                        char* szTmp = new char[nRecv + 1];
                                        strncpy(szTmp,buf,nRecv);
                                        szTmp[nRecv] = 0;
                                        g_map[s] = szTmp;
                                    }
                                    else
                                    {
                                        char* szOld = g_map[s];
                                        char* szTmp2 = new char[strlen(szOld) + nRecv + 1];
                                        strncpy(szTmp2,szOld,strlen(szOld));
                                        strncpy(szTmp2 + strlen(szOld),buf,nRecv);
                                        szTmp2[strlen(szOld) + nRecv] = 0;
                                        delete [] szOld;
                                        g_map[s] = szTmp2;
                                    }
                                    FD_SET(fdSocket.fd_array[i],&fdWrite);
                                }
                                else
                                {
                                    closesocket(fdSocket.fd_array[i]);
                                    if(g_map.find(s) != g_map.end())
                                    {
                                        if(g_map[s] != NULL)
                                            delete [] g_map[s];
                                        g_map.erase(s);
                                    }
                                    FD_CLR(fdSocket.fd_array[i],&fdSocket);
                                }
                            }
                            printf("发送了%d\n",nRet);
                        }
                        else
                        {
                            printf("1个Client已断开\n");
                            closesocket(fdSocket.fd_array[i]);
                            FD_CLR(fdSocket.fd_array[i],&fdSocket);
                        }
                    }
                }
                if(FD_ISSET(fdSocket.fd_array[i],&fdTmp))
                {
                    SOCKET s = fdSocket.fd_array[i];
                    if(g_map.find(s) != g_map.end())
                    {
                        char* szToSend = g_map[s];
                        int nToSend = strlen(szToSend);
                        int nRet = send(fdSocket.fd_array[i],szToSend,nToSend,0);
                        if(nRet <= 0)
                        {
                            if(GetLastError() == WSAEWOULDBLOCK)
                            {                                   
                                //do nothing
                            }
                            else
                            {
                                closesocket(fdSocket.fd_array[i]);
                                if(g_map.find(s) != g_map.end())
                                {
                                    if(g_map[s] != NULL)
                                        delete [] g_map[s];
                                    g_map.erase(s);
                                }
                                FD_CLR(fdSocket.fd_array[i],&fdSocket);
                            }
                        }
                        else if(nRet < nToSend)
                        {
                            printf("发送了%d/%d\n",nRet,nToSend);
                            nToSend -= nRet;
                            char* szTmp = new char[nToSend + 1];
                            strncpy(szTmp,szToSend + nRet,nToSend);
                            szTmp[nToSend] = 0;
                            delete [] szToSend;
                            g_map[s] = szTmp;                   
                        }
                        else
                        {
                            if(g_map[s] != NULL)
                                delete [] g_map[s];
                            g_map.erase(s);
                            FD_CLR(fdSocket.fd_array[i],&fdWrite);
                        }
                        printf("============================================发送了%d\n",nRet);
                    }
                }
            }
        }
        else if(nRetAll == 0)
        {
            printf("time out!\n");
        }
        else
        {
            printf("select error!%d\n",WSAGetLastError());
            Sleep(5000);
            break;
        }
    }
    closesocket(sListen);
    WSACleanup();
}

四,异步选择模型

注意:收到FD_Write消息有2种情况:1,在socket第一次和窗口句柄绑定后。2,socket从不可写状态变成可写状态。下面的事件选择模型也是同理。


#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>
#include <map>
using namespace std;


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


#define WM_SOCKET (WM_USER + 100) 


map<SOCKET,char*> g_map;
LRESULT WINAPI WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    switch(uMsg)
    {
    case WM_SOCKET:
        {
            SOCKET s = wParam;
            if(WSAGETSELECTERROR(lParam))
            {
                printf("消息错误!\n");
                closesocket(s);
                return 0;
            }


            switch(WSAGETSELECTEVENT(lParam))
            {
            case FD_ACCEPT:
                {
                    sockaddr_in addrRemote;
                    int nAddrLen = sizeof(addrRemote);
                    SOCKET sClient = accept(s,(sockaddr*)&addrRemote,&nAddrLen);
                    WSAAsyncSelect(sClient,hwnd,WM_SOCKET,FD_READ | FD_WRITE | FD_CLOSE);
                }break;
            case FD_WRITE:
                {
                    printf("write====================\n");
                    if(!g_map.empty())
                    {
                        char* buf = g_map[s];
                        int nLenth = strlen(buf);
                        while(nLenth > 0)
                        {
                            int nRet = send(s,buf,nLenth,0);
                            if(nRet > 0)
                            {
                                buf += nRet;
                                nLenth -= nRet;
                            }
                            else if(10035 == GetLastError())
                            {
                                char* newBuf = new char[nLenth + 1];
                                strncpy(newBuf,buf,nLenth);
                                newBuf[nLenth] = 0;
                                delete [] g_map[s];
                                g_map[s] = newBuf;
                                break;
                            }
                            else
                            {
                                delete [] g_map[s];
                                g_map.erase(s);
                                closesocket(s);
                            }
                        }
                        if(nLenth == 0)
                        {
                            g_map.erase(s);
                        }
                    }
                }break;
            case FD_READ:
                {
                    char buf[4096];
                    int nRet = recv(s,buf,4096,0);
                    if(nRet > 0)
                    {
                        buf[nRet] = 0;
                        //printf("收到数据:%s\n",buf);
                        int x = send(s,buf,nRet,0);
                        printf("已发送字节数:%d , 线程号:%d\n",x,GetCurrentThreadId());
                        if(x < 0)
                        {
                            int iError = GetLastError();
                            printf("数据:%s ,错误:%d\n",buf,iError);
                            if(10035 == iError)
                            {
                                if(g_map.end() != g_map.find(s))
                                {
                                    int newLength = strlen(g_map[s]) + strlen(buf);
                                    char* newBuf = new char[newLength + 1];
                                    strncpy(newBuf,g_map[s],strlen(g_map[s]));
                                    strncpy(newBuf+strlen(g_map[s]),buf,strlen(buf));
                                    newBuf[newLength] = 0;
                                    delete [] g_map[s];
                                    g_map[s] = newBuf;
                                }
                                else
                                {
                                    char* newBuf = new char[strlen(buf) + 1];
                                    strncpy(newBuf,buf,strlen(buf));
                                    newBuf[strlen(buf)] = 0;
                                    g_map[s] = newBuf;
                                }
                            }
                            else
                            {
                                if(g_map.end() != g_map.find(s))
                                {
                                    delete [] g_map[s];
                                    g_map.erase(s);
                                }
                                closesocket(s);
                            }
                        }   
                    }
                    else
                    {
                        printf("1个Client已经断开1111!\n");
                        if(g_map.end() != g_map.find(s))
                        {
                            delete [] g_map[s];
                            g_map.erase(s);
                        }
                        closesocket(s);
                    }
                }break;
            case FD_CLOSE:
                {
                    printf("1个Client已经断开222!\n");
                    if(g_map.end() != g_map.find(s))
                    {
                        delete [] g_map[s];
                        g_map.erase(s);
                    }
                    closesocket(s);
                }break; 
            }
        }break;
    case WM_DESTROY:
        {
            printf("窗口已关闭!\n");
            PostQuitMessage(0);
        }
    }
    return DefWindowProc(hwnd,uMsg,wParam,lParam);
}


int main()
{
    char szClassName[] = "WSAAsyncSelect Test";
    static WNDCLASSEX wndClass;
    wndClass.cbSize = sizeof(wndClass);
    wndClass.style = CS_HREDRAW | CS_VREDRAW;
    wndClass.lpfnWndProc = WindowProc;
    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = GetModuleHandle(0);
    wndClass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
    wndClass.hCursor = LoadCursor(NULL,IDC_ARROW);
    wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndClass.lpszMenuName = NULL;
    wndClass.lpszClassName = szClassName;
    wndClass.hIconSm = NULL;


    ATOM atom = RegisterClassEx(&wndClass);
    if(0 == atom)
    {
        char error[256];
        sprintf(error,"RegisterClassEx错误!%d",GetLastError());
        MessageBox(NULL,error,"error",MB_OK);
        return -1;
    }
    HWND hwnd = CreateWindowEx(0,(char *)atom,"",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
        CW_USEDEFAULT,CW_USEDEFAULT,HWND_MESSAGE,NULL,NULL,NULL);
    if(hwnd == NULL)
    {
        char error[256];
        sprintf(error,"创建窗口错误!%d",GetLastError());
        MessageBox(NULL,error,"error",MB_OK);
        return -1;
    }


    WSAData wsaData;
    if(0 != WSAStartup(MAKEWORD(2,2),&wsaData))
    {
        printf("初始化失败!%d\n",WSAGetLastError());
        Sleep(5000);
        return -1;
    }
    USHORT nport = 3456;
    SOCKET sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(nport);
    sin.sin_addr.S_un.S_addr = ADDR_ANY;


    if(SOCKET_ERROR == bind(sListen,(sockaddr*)&sin,sizeof(sin)))
    {
        printf("bind failed!%d\n",WSAGetLastError());
        Sleep(5000);
        return -1;
    }


    WSAAsyncSelect(sListen,hwnd,WM_SOCKET,FD_ACCEPT | FD_CLOSE);
    listen(sListen,5);


    MSG msg;
    while(GetMessage(&msg,NULL,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    closesocket(sListen);
    WSACleanup();
    return msg.wParam;
}

五,事件选择模型

事件选择模型主要难点是对线程池的使用,send操作可以参考异步选择模型。


#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>
#include <vector>
using namespace std;


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


typedef struct _THREAD_OBJ
{
    HANDLE events[WSA_MAXIMUM_WAIT_EVENTS];
    SOCKET sockets[WSA_MAXIMUM_WAIT_EVENTS];
    int nSocksUsed;
    CRITICAL_SECTION cs;
    _THREAD_OBJ *pNext;
}THREAD_OBJ,*PTHREAD_OBJ;


PTHREAD_OBJ g_pThreadList = NULL;
CRITICAL_SECTION g_cs;
BOOL g_bServerRunning = FALSE;
HANDLE g_hThreads[1000] = {0};
int g_nThreadsCount = 0;


PTHREAD_OBJ CreateThreadObj()
{ 
    PTHREAD_OBJ pThread = new THREAD_OBJ();
    if(pThread != NULL)
    {
        InitializeCriticalSectionAndSpinCount(&pThread->cs,4000);
        pThread->events[0] = WSACreateEvent();
        pThread->nSocksUsed = 1;
        EnterCriticalSection(&g_cs);
        pThread->pNext = g_pThreadList;
        g_pThreadList = pThread;
        LeaveCriticalSection(&g_cs);
    }
    return pThread;
}


void FreeThreadObj(PTHREAD_OBJ pThread)
{
    if(pThread == NULL)
        return;
    EnterCriticalSection(&g_cs);
    PTHREAD_OBJ p = g_pThreadList;
    if(p == pThread)
    {
        g_pThreadList = p->pNext;
    }
    else
    {
        while(p != NULL && p->pNext != pThread)
        {
            p = p->pNext;
        }
        if(p != NULL)
        {
            p->pNext = pThread->pNext;
        }
    }
    LeaveCriticalSection(&g_cs);


    DeleteCriticalSection(&pThread->cs);
    WSACloseEvent(pThread->events[0]);
    delete pThread;
}


LONG g_nTotalConnections;
LONG g_nCurrentConnections;


BOOL InsertSocket(PTHREAD_OBJ pThread,SOCKET s)
{
    if(pThread == NULL || s == INVALID_SOCKET)
        return FALSE;


    BOOL bRet = FALSE;
    EnterCriticalSection(&pThread->cs);
    if(pThread->nSocksUsed < WSA_MAXIMUM_WAIT_EVENTS)
    {
        pThread->events[pThread->nSocksUsed] = WSACreateEvent();
        pThread->sockets[pThread->nSocksUsed] = s;
        WSAEventSelect(s,pThread->events[pThread->nSocksUsed],FD_READ | FD_CLOSE | FD_WRITE);
        pThread->nSocksUsed++;
        bRet = TRUE;
        WSASetEvent(pThread->events[0]);//通知线程,有新的事件加入了,需要重新调用WSAWaitFormultipleEvents
    }
    LeaveCriticalSection(&pThread->cs);


    if(bRet)
    {
        InterlockedIncrement(&g_nTotalConnections);
        InterlockedIncrement(&g_nCurrentConnections);
    }
    return bRet;
}


void RemoveSocket(PTHREAD_OBJ pThread,SOCKET s)
{
    if(pThread == NULL || s == INVALID_SOCKET)
        return;
    EnterCriticalSection(&pThread->cs);
    for(int i=1;i<pThread->nSocksUsed;i++)
    {
        if(pThread->sockets[i] == s)
        {
            WSACloseEvent(pThread->events[i]);
            closesocket(s);
            for(int j=i;j<pThread->nSocksUsed - 1;j++)
            {
                pThread->events[j] = pThread->events[j+1];
                pThread->sockets[j] = pThread->sockets[j+1];
            }
            pThread->nSocksUsed--;
            break;
        }
    }
    LeaveCriticalSection(&pThread->cs);
    InterlockedDecrement(&g_nCurrentConnections);
}


BOOL HandleIo(PTHREAD_OBJ pThread,int nIndex)
{
    WSANETWORKEVENTS event;
    SOCKET s = pThread->sockets[nIndex];
    HANDLE sEvent = pThread->events[nIndex];
    if(0 != WSAEnumNetworkEvents(s,sEvent,&event))
    {
        printf("socket error!\n");
        RemoveSocket(pThread,s);
        return FALSE;
    }
    do 
    {
        if(event.lNetworkEvents & FD_READ)
        {
            if(event.iErrorCode[FD_READ_BIT] == 0)
            {
                char szText[256];
                int nRecv = recv(s,szText,strlen(szText),0);
                if(nRecv > 0)
                {
                    szText[nRecv] = '\0';
                    printf("接收到数据:%s\n",szText);        
                }
                else
                {
                    break;
                }
            }
            else
                break;
        }
        else if(event.lNetworkEvents & FD_CLOSE)
        {
            break;
        }
        else if(event.lNetworkEvents & FD_WRITE)
        {
            printf("FD_WRITE==========================\n");
        }
        return TRUE;
    } while (FALSE);
    printf("socket error2!\n");
    RemoveSocket(pThread,s);
    return FALSE;
}


DWORD WINAPI ServerThread(LPVOID lpParam)
{
    PTHREAD_OBJ pThread = (PTHREAD_OBJ)lpParam;


    while(TRUE)
    {
        int nIndex = WSAWaitForMultipleEvents(
            pThread->nSocksUsed,pThread->events,FALSE,WSA_INFINITE,FALSE);
        nIndex = nIndex - WSA_WAIT_EVENT_0;


        if(nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT)
        {
            printf("WSAWaitForMultipleEvents error!\n");
            continue;
        }
        else if(nIndex == 0)
        {
            ResetEvent(pThread->events[0]);
        }
        else
        {
            HandleIo(pThread,nIndex);
        }
        if(!g_bServerRunning && pThread->nSocksUsed == 1)
            break;
    }
    FreeThreadObj(pThread);
    return 0;
}


BOOL AssignToFreeThread(SOCKET s)
{
    if(s == INVALID_SOCKET)
        return FALSE;
    BOOL bAllSucceed = TRUE;
    EnterCriticalSection(&g_cs);
    PTHREAD_OBJ pThread = g_pThreadList;
    while(pThread != NULL)
    {
        if(InsertSocket(pThread,s))
        {
            break;
        }
        pThread = pThread->pNext;
    }
    if(pThread == NULL)
    {
        if(g_nThreadsCount < 1000)
        {
            pThread = CreateThreadObj();
            HANDLE hThread = CreateThread(NULL,0,ServerThread,pThread,0,NULL);
            if(!hThread)
            {
                bAllSucceed = FALSE;
                FreeThreadObj(pThread);
            }
            else
            {
                g_hThreads[g_nThreadsCount++] = hThread;
                InsertSocket(pThread,s);
            }
        }
        else
            bAllSucceed = FALSE;
    }
    LeaveCriticalSection(&g_cs);
    return bAllSucceed;
}


DWORD WINAPI ControlThread(LPVOID lpParma)
{
    HANDLE wsaEvent = (HANDLE)lpParma;
    char cmd[128];
    while(scanf("%s",cmd))
    {
        if(cmd[0] == 's')
        {
            g_bServerRunning = FALSE;
            EnterCriticalSection(&g_cs);
            PTHREAD_OBJ pThread = g_pThreadList;
            while(pThread != NULL)
            {
                EnterCriticalSection(&pThread->cs);
                for(int i=0;i<pThread->nSocksUsed;i++)
                {
                    closesocket(pThread->sockets[i]);
                }
                WSASetEvent(pThread->events[0]);
                LeaveCriticalSection(&pThread->cs);
                pThread = pThread->pNext;
            }
            LeaveCriticalSection(&g_cs);
            WSASetEvent(wsaEvent);
            break;
        }
    }
    return 0;
}


int main()
{
    WSAData wsaData;
    if(0 != WSAStartup(MAKEWORD(2,2),&wsaData))
    {
        printf("初始化失败!%d\n",WSAGetLastError());
        Sleep(5000);
        return -1;
    }
    USHORT nport = 3456;
    SOCKET sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(nport);
    sin.sin_addr.S_un.S_addr = ADDR_ANY;


    if(SOCKET_ERROR == bind(sListen,(sockaddr*)&sin,sizeof(sin)))
    {
        printf("bind failed!%d\n",WSAGetLastError());
        Sleep(5000);
        return -1;
    }


    listen(sListen,200);


    WSAEVENT wsaEvent = WSACreateEvent();
    WSAEventSelect(sListen,wsaEvent,FD_ACCEPT | FD_CLOSE);
    InitializeCriticalSectionAndSpinCount(&g_cs,4000);
    g_bServerRunning = TRUE;
    HANDLE hThread = CreateThread(NULL,0,ControlThread,wsaEvent,0,NULL);
    CloseHandle(hThread);
    while(TRUE)
    {
        int nRet = WaitForSingleObject(wsaEvent,5*1000);
        if(!g_bServerRunning)
        {
            closesocket(sListen);
            WSACloseEvent(wsaEvent);
            WSAWaitForMultipleEvents(g_nThreadsCount,g_hThreads,TRUE,INFINITE,FALSE);
            for(int i=0;i<g_nThreadsCount;i++)
            {
                CloseHandle(g_hThreads[i]);
            }
            break;
        }
        if(nRet == WAIT_FAILED)
        {
            printf("WaitForSingleObject Failed!\n");
            break;
        }
        else if(nRet == WAIT_TIMEOUT)
        {
            printf("\nTotalConnections: %d\nCurrentConnections: %d\nThreads:%d\n",
                g_nTotalConnections,g_nCurrentConnections,g_nThreadsCount);
            continue;
        }
        else
        {
            ResetEvent(wsaEvent);
            while(TRUE)
            {
                sockaddr_in addrRemote;
                int nLen = sizeof(addrRemote);
                SOCKET sNew = accept(sListen,(sockaddr*)&addrRemote,&nLen);
                if(sNew == SOCKET_ERROR)
                    break;

                if(!AssignToFreeThread(sNew))
                {
                    closesocket(sNew);
                    printf("AssignToFreeThread Failed!\n");
                }
            }
        }
    }
    DeleteCriticalSection(&g_cs);
    return 0;
}

六,重叠I/O模型

若需要建线程池,可参考事件选择模型。若纠结于send,可参考下面的IOCP。


#include <WinSock2.h>
#include <Windows.h>
#include <MSWSock.h>
#include <stdio.h>


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


#define BUFFER_SIZE 4096


typedef struct _SOCKET_OBJ
{
    SOCKET s;
    int nOutstandingOps;
    LPFN_ACCEPTEX lpfnAcceptEx;
}SOCKET_OBJ,*PSOCKET_OBJ;


PSOCKET_OBJ CreateSocketObj(SOCKET s)
{
    PSOCKET_OBJ pSocket = new SOCKET_OBJ();
    if(pSocket != NULL)
        pSocket->s = s;
    return pSocket;
}


void FreeSocketObj(PSOCKET_OBJ pSocket)
{
    if(pSocket == NULL)
        return;
    if(pSocket->s != INVALID_SOCKET)
        closesocket(pSocket->s);
    delete pSocket;
}


typedef struct _BUFFER_OBJ
{
    OVERLAPPED ol;
    char* buff;
    int nLen;
    PSOCKET_OBJ pSocket;
    int nOperation;
#define OP_ACCEPT 1
#define OP_READ 2
#define OP_WRITE 3
    SOCKET sAccept;
    _BUFFER_OBJ* pNext;
}BUFFER_OBJ,*PBUFFER_OBJ;






HANDLE g_events[WSA_MAXIMUM_WAIT_EVENTS];
int g_nBufferCount;
PBUFFER_OBJ g_pBufferHeader,g_pBufferTail;
BOOL g_bServerRunning;
CRITICAL_SECTION g_cs;


PBUFFER_OBJ CreateBufferObj(PSOCKET_OBJ pSocket,ULONG nLen)
{
    if(g_nBufferCount > WSA_MAXIMUM_WAIT_EVENTS - 1)
        return NULL;
    PBUFFER_OBJ pBuffer = new BUFFER_OBJ();
    if(pBuffer != NULL)
    {
        pBuffer->buff = new char[nLen];
        pBuffer->nLen = nLen;
        pBuffer->ol.hEvent = WSACreateEvent();
        pBuffer->pSocket = pSocket;
        pBuffer->sAccept = INVALID_SOCKET;
        pBuffer->pNext = NULL;
        EnterCriticalSection(&g_cs);
        if(g_pBufferHeader == NULL)
        {
            g_pBufferHeader = g_pBufferTail = pBuffer;
        }
        else
        {
            g_pBufferTail->pNext = pBuffer;
            g_pBufferTail = pBuffer;
        }
        LeaveCriticalSection(&g_cs);
        g_events[++g_nBufferCount] = pBuffer->ol.hEvent;
    }
    return pBuffer;
}


void FreeBufferObj(PBUFFER_OBJ pBuffer)
{
    EnterCriticalSection(&g_cs);
    PBUFFER_OBJ pTest = g_pBufferHeader;
    BOOL bFind = FALSE;
    if(pTest == pBuffer)
    {
        if(g_pBufferHeader == g_pBufferTail)
            g_pBufferHeader = g_pBufferTail = NULL;
        else
            g_pBufferHeader = g_pBufferHeader->pNext;
        bFind = TRUE;
    }
    else
    {
        while(pTest != NULL && pTest->pNext != pBuffer)
            pTest = pTest->pNext;
        if(pTest != NULL)
        {
            pTest->pNext = pBuffer->pNext;
            if(pTest->pNext == NULL)
                g_pBufferTail = pTest;
            bFind = TRUE;
        }
    }

    if(bFind)
    {
        g_nBufferCount--;
        WSACloseEvent(pBuffer->ol.hEvent);
        delete [] pBuffer->buff;
        delete pBuffer;
    }
    LeaveCriticalSection(&g_cs);
}


PBUFFER_OBJ FindBufferObj(HANDLE hEvent)
{
    if(hEvent == NULL || hEvent == INVALID_HANDLE_VALUE)
        return NULL;
    EnterCriticalSection(&g_cs);
    PBUFFER_OBJ pTest = g_pBufferHeader;
    while(pTest != NULL && pTest->ol.hEvent != hEvent)
        pTest = pTest->pNext;
    LeaveCriticalSection(&g_cs);
    return pTest;
}


void RebuildArray()
{
    EnterCriticalSection(&g_cs);
    PBUFFER_OBJ pBuffer = g_pBufferHeader;
    int i=1;
    while(pBuffer != NULL)
    {
        g_events[i++] = pBuffer->ol.hEvent;
        pBuffer = pBuffer->pNext;
    }
    LeaveCriticalSection(&g_cs);
}


BOOL PostAccept(PBUFFER_OBJ pBuffer)
{
    PSOCKET_OBJ pSocket = pBuffer->pSocket;
    if(pSocket->lpfnAcceptEx != NULL)
    {
        pBuffer->nOperation = OP_ACCEPT;
        pSocket->nOutstandingOps++;


        DWORD dwBytes;
        pBuffer->sAccept = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
        BOOL b = pSocket->lpfnAcceptEx(pSocket->s,
            pBuffer->sAccept,pBuffer->buff,BUFFER_SIZE - ((sizeof(sockaddr_in) + 16)*2),
            sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16,&dwBytes,&pBuffer->ol);
        if(!b)
        {
            if(WSAGetLastError() != WSA_IO_PENDING)
                return FALSE;
        }
        return TRUE;
    }
    return FALSE;
}


BOOL PostRecv(PBUFFER_OBJ pBuffer)
{
    pBuffer->nOperation = OP_READ;
    pBuffer->pSocket->nOutstandingOps++;


    DWORD dwBytes;
    DWORD dwFlags = 0;
    WSABUF buf;
    buf.buf = pBuffer->buff;
    buf.len = pBuffer->nLen;
    if(WSARecv(pBuffer->pSocket->s,&buf,1,&dwBytes,&dwFlags,&pBuffer->ol,NULL))
    {
        if(WSAGetLastError() != WSA_IO_PENDING)
            return FALSE;
    }
    return TRUE;
}


BOOL PostSend(PBUFFER_OBJ pBuffer)
{
    pBuffer->nOperation = OP_WRITE;
    pBuffer->pSocket->nOutstandingOps++;
    DWORD dwBytes;
    DWORD dwFlags = 0;
    WSABUF buf;
    buf.buf = pBuffer->buff;
    buf.len = pBuffer->nLen;
    if(WSASend(pBuffer->pSocket->s,&buf,1,&dwBytes,dwFlags,&pBuffer->ol,NULL))
    {
        if(WSAGetLastError() != WSA_IO_PENDING)
            return FALSE;
    }
    return TRUE;
}


BOOL HandleIo(PBUFFER_OBJ pBuffer)
{
    if(pBuffer == NULL)
        return FALSE;


    PSOCKET_OBJ pSocket = pBuffer->pSocket;
    pSocket->nOutstandingOps--;


    DWORD dwTrans;
    DWORD dwFlags;
    BOOL bRet = WSAGetOverlappedResult(pSocket->s,&pBuffer->ol,&dwTrans,FALSE,&dwFlags);
    if(!bRet)
    {
        if(pSocket->s != INVALID_SOCKET)
        {
            closesocket(pSocket->s);
            pSocket->s = INVALID_SOCKET;
        }
        if(pBuffer->nOperation == OP_ACCEPT && pBuffer->sAccept != INVALID_SOCKET)
        {
            closesocket(pBuffer->sAccept);
            pBuffer->sAccept = INVALID_SOCKET;
        }
        if(pSocket->nOutstandingOps == 0)
        {
            FreeSocketObj(pSocket);
        }
        FreeBufferObj(pBuffer);
        return FALSE;
    }


    switch(pBuffer->nOperation)
    {
    case OP_ACCEPT:
        {
            if(dwTrans > 0)
            {
                pBuffer->buff[dwTrans] = 0;
                printf("Accept收到数据:%s\n",pBuffer->buff);


                PSOCKET_OBJ pClient = CreateSocketObj(pBuffer->sAccept);
                PBUFFER_OBJ pRecv = CreateBufferObj(pClient,BUFFER_SIZE);
                if(pRecv == NULL)
                {
                    printf("Too much connections!\n");
                    FreeSocketObj(pClient);
                    return FALSE;
                }
                RebuildArray();
                if(!PostRecv(pRecv))
                {
                    FreeSocketObj(pClient);
                    FreeBufferObj(pBuffer);
                    return FALSE;
                }
            }
            else
            {
                if(pSocket->s != INVALID_SOCKET)
                {
                    closesocket(pSocket->s);
                    pSocket->s = INVALID_SOCKET;
                }
                if(pBuffer->sAccept != INVALID_SOCKET)
                {
                    closesocket(pBuffer->sAccept);
                    pBuffer->sAccept = INVALID_SOCKET;
                }
                if(pSocket->nOutstandingOps == 0)
                {
                    FreeSocketObj(pSocket);
                }
                FreeBufferObj(pBuffer);
            }
//          PBUFFER_OBJ pSend = CreateBufferObj(pClient,BUFFER_SIZE);
            //if(pSend == NULL)
            //{
            //  printf("Too much connections!\n");
            //  FreeSocketObj(pClient);
            //  return FALSE;
            //}
            //RebuildArray();
            //pSend->nLen = dwTrans;
            //memcpy(pSend->buff,pBuffer->buff,dwTrans);


            //if(!PostSend(pSend))
            //{
            //  FreeSocketObj(pSocket);
            //  FreeBufferObj(pBuffer);
            //  return FALSE;
            //}


            PostAccept(pBuffer);
        }break;
    case OP_READ:
        {
            if(dwTrans > 0)
            {
                pBuffer->buff[dwTrans] = 0;
                printf("Recv收到数据:%s\n",pBuffer->buff);
                PostRecv(pBuffer);
            }
            else
            {
                if(pSocket->s != INVALID_SOCKET)
                {
                    closesocket(pSocket->s);
                    pSocket->s = INVALID_SOCKET;
                }
                if(pSocket->nOutstandingOps == 0)
                {
                    FreeSocketObj(pSocket);
                }
                FreeBufferObj(pBuffer);
            }
        }break;
    case OP_WRITE:
        {
            if(dwTrans > 0)
            {
                pBuffer->buff[dwTrans] = 0;
                printf("发送数据: %s 成功!\n",pBuffer->buff);
                FreeBufferObj(pBuffer);
            }
            else
            {
                if(pSocket->s != INVALID_SOCKET)
                {
                    closesocket(pSocket->s);
                    pSocket->s = INVALID_SOCKET;
                }
                if(pSocket->nOutstandingOps == 0)
                {
                    FreeSocketObj(pSocket);
                }
                FreeBufferObj(pBuffer);
            }
        }break;
    }
}


DWORD WINAPI ControlThread(LPVOID lpParma)
{
    char cmd[128];
    while(scanf("%s",cmd))
    {
        if(cmd[0] == 's')
        {
            g_bServerRunning = FALSE;
            EnterCriticalSection(&g_cs);
            PBUFFER_OBJ pBuffer = g_pBufferHeader;
            while(pBuffer != NULL)
            {
                if(pBuffer->pSocket != NULL && pBuffer->pSocket->s != INVALID_SOCKET)
                    closesocket(pBuffer->pSocket->s);
                pBuffer = pBuffer->pNext;
            }
            LeaveCriticalSection(&g_cs);
            break;
        }
    }
    return 0;
}


int main()
{
    InitializeCriticalSectionAndSpinCount(&g_cs,4000);
    WSAData wsaData;
    if(0 != WSAStartup(MAKEWORD(2,2),&wsaData))
    {
        printf("初始化失败!%d\n",WSAGetLastError());
        Sleep(5000);
        return -1;
    }
    USHORT nport = 3456;
    SOCKET sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(nport);
    sin.sin_addr.S_un.S_addr = ADDR_ANY;


    if(SOCKET_ERROR == bind(sListen,(sockaddr*)&sin,sizeof(sin)))
    {
        printf("bind failed!%d\n",WSAGetLastError());
        Sleep(5000);
        return -1;
    }


    listen(sListen,200);




    g_bServerRunning = TRUE;
    PSOCKET_OBJ pListen = CreateSocketObj(sListen);
    GUID GuidAcceptEx = WSAID_ACCEPTEX;
    DWORD dwBytes;
    WSAIoctl(pListen->s,
        SIO_GET_EXTENSION_FUNCTION_POINTER,
        &GuidAcceptEx,
        sizeof(GuidAcceptEx),
        &pListen->lpfnAcceptEx,
        sizeof(pListen->lpfnAcceptEx),
        &dwBytes,
        NULL,
        NULL);
    g_events[0] = WSACreateEvent();


    for(int i=0;i<5;++i)
    {
        PostAccept(CreateBufferObj(pListen,BUFFER_SIZE));
    }

    HANDLE hThread = CreateThread(NULL,0,ControlThread,NULL,0,NULL);
    while(TRUE)
    {
        int nIndex = WSAWaitForMultipleEvents(g_nBufferCount+1,g_events,FALSE,WSA_INFINITE,FALSE);
        if(nIndex == WSA_WAIT_FAILED)
        {
            printf("WSAWaitForMultipleEvents Failed!\n");
            break;
        }
        nIndex = nIndex - WSA_WAIT_EVENT_0;
        for(int i=nIndex;i<= g_nBufferCount;i++)
        {
            int nRet = WSAWaitForMultipleEvents(1,&g_events[i],FALSE,0,FALSE);
            if(nRet == WSA_WAIT_TIMEOUT)
                continue;


            if(i == 0)
            {           
                RebuildArray();
                continue;
            }


            PBUFFER_OBJ pBuffer = FindBufferObj(g_events[i]);
            if(pBuffer != NULL)
            {
                if(!HandleIo(pBuffer))
                    RebuildArray();
            }
        }
        if(!g_bServerRunning && g_nBufferCount == 0)
            break;
    }
    WSACloseEvent(g_events[0]);
    WaitForSingleObject(hThread,INFINITE);
    CloseHandle(hThread);
    closesocket(sListen);
    WSACleanup();
    DeleteCriticalSection(&g_cs);
    return 0;
}

七,IOCP

大框架为书中例子,对强化了发送操作,部分异常处理,且加入了连接超时处理。

注意:当一个投递完成,且对应socket上已经没有未决的投递,必须要再投递一个请求或者关闭连接,否则socket对应的数据结构无法被释放,对应socket连接断开时也无法被检测到。所以如果业务逻辑结束,要关闭连接。或者你需要等客户端来断开连接,那么你可以在业务逻辑结束后,再投递一个接收请求(客户端断开时,接收请求返回且接收的字节数为0,则此类中的异常处理逻辑便会将资源清理掉)。

头文件:

////////////////////////////////////////
// IOCP.h文件

#ifndef __IOCP_H__
#define __IOCP_H__

#include <winsock2.h>
#include <windows.h>
#include <Mswsock.h>

#define BUFFER_SIZE 1024*4      // I/O请求的缓冲区大小
#define MAX_THREAD  1           // I/O服务线程的数量

// 这是per-I/O数据。它包含了在套节字上处理I/O操作的必要信息
struct CIOCPBuffer
{
    CIOCPBuffer()
    {
        memset(&ol,0,sizeof(WSAOVERLAPPED));
        sClient = INVALID_SOCKET;
        memset(buff,0,BUFFER_SIZE);
        nLen = 0;
        nSequenceNumber = 0;
        bIsReleased = FALSE;
        nOperation = 0;
        pNext = NULL;
    }
    WSAOVERLAPPED ol;

    SOCKET sClient;         // AcceptEx接收的客户方套节字

    char buff[BUFFER_SIZE];             // I/O操作使用的缓冲区
    int nLen;               // buff缓冲区(使用的)大小

    ULONG nSequenceNumber;  // 此I/O的序列号
    BOOL  bIsReleased;

    int nOperation;         // 操作类型
#define OP_ACCEPT   1
#define OP_WRITE    2
#define OP_READ     3

    CIOCPBuffer *pNext;
};
struct CIOCPNextToSend;
struct CIOCPTimerData;
// 这是per-Handle数据。它包含了一个套节字的信息
struct CIOCPContext
{
    CIOCPContext()
    {
        s = INVALID_SOCKET;
        memset(&addrLocal,0,sizeof(SOCKADDR_IN));
        memset(&addrRemote,0,sizeof(SOCKADDR_IN));
        bClosing = FALSE;
        nOutstandingRecv = 0;
        nOutstandingSend = 0;
        nReadSequence = 0;
        nCurrentReadSequence = 0;
        nCurrentStep = 0;
        pOutOfOrderReads = NULL;
        pNextToSend = NULL;
        bIsReleased = FALSE;
        pNext = NULL;
        pPreData = NULL;
        strcpy(szClientName,"");
        hTimer = NULL;
        hCompletion = NULL;
    }
    CIOCPBuffer m_pBuffer;
    SOCKET s;                       // 套节字句柄

    SOCKADDR_IN addrLocal;          // 连接的本地地址
    SOCKADDR_IN addrRemote;         // 连接的远程地址

    BOOL bClosing;                  // 套节字是否关闭

    int nOutstandingRecv;           // 此套节字上抛出的重叠操作的数量
    int nOutstandingSend;           

    ULONG nReadSequence;            // 安排给接收的下一个序列号
    ULONG nCurrentReadSequence;     // 当前要读的序列号

    CIOCPBuffer *pOutOfOrderReads;  // 记录没有按顺序完成的读I/O
    CIOCPNextToSend *pNextToSend;       //xss,按顺序发送的下一个要发送的。

    LPVOID pPreData; //xss,用于2个过程之间的数据交流。
    ULONG  nCurrentStep;//xss,用于记录当前处于的过程步骤数。
    BOOL   bIsReleased;

    CRITICAL_SECTION Lock;          // 保护这个结构
    CIOCPContext *pNext;

    char szClientName[256];//xss
    HANDLE hTimer;//xss
    HANDLE hCompletion;//xss
};

struct CIOCPNextToSend//xss
{
    CIOCPBuffer * pBuffer;
    CIOCPNextToSend * pNext;
};

struct CIOCPTimerData
{
    CIOCPContext* pContext;
    HANDLE hCompletion;
};

class CIOCPServer   // 处理线程
{
public:
    CIOCPServer();
    ~CIOCPServer();

    // 开始服务
    BOOL Start(int nPort = 3456, int nMaxConnections = 2000, 
            int nMaxFreeBuffers = 200, int nMaxFreeContexts = 100, int nInitialReads = 4);
    // 停止服务
    void Shutdown();

    // 关闭一个连接和关闭所有连接
    void CloseAConnection(CIOCPContext *pContext);
    void CloseAllConnections(); 

    // 取得当前的连接数量
    ULONG GetCurrentConnection() { return m_nCurrentConnection; }

    // 向指定客户发送文本
    BOOL SendText(CIOCPContext *pContext, char *pszText, int nLen); 

protected:
    // 申请和释放缓冲区对象
    CIOCPBuffer *AllocateBuffer(int nLen);
    void ReleaseBuffer(CIOCPBuffer *pBuffer);

    // 申请和释放套节字上下文
    CIOCPContext *AllocateContext(SOCKET s);
    void ReleaseContext(CIOCPContext *pContext);

    // 释放空闲缓冲区对象列表和空闲上下文对象列表
    void FreeBuffers();
    void FreeContexts();

    // 向连接列表中添加一个连接
    BOOL AddAConnection(CIOCPContext *pContext);

    // 插入和移除未决的接受请求
    BOOL InsertPendingAccept(CIOCPBuffer *pBuffer);
    BOOL RemovePendingAccept(CIOCPBuffer *pBuffer);

    //xss,把要发送的数据加入队列,按顺序发送
    BOOL PostSendToList(CIOCPContext *pContext, CIOCPBuffer *pBuffer);
    //xss,发送下一个需要发送的
    BOOL PostNextWriteBuffer(CIOCPContext *pContext, CIOCPBuffer *pBuffer);
    // 取得下一个要读取的
    CIOCPBuffer *GetNextReadBuffer(CIOCPContext *pContext, CIOCPBuffer *pBuffer);

    void ErrorHandle(CIOCPContext *pContext, CIOCPBuffer *pBuffer);//xss,错误集中处理
    // 投递接受I/O、发送I/O、接收I/O
    BOOL PostAccept(CIOCPBuffer *pBuffer);
    BOOL PostSend(CIOCPContext *pContext, CIOCPBuffer *pBuffer);
    BOOL PostRecv(CIOCPContext *pContext, CIOCPBuffer *pBuffer);
    BOOL PostRecv2(CIOCPContext *pContext, CIOCPBuffer *pBuffer);

    void HandleIO(DWORD dwKey, CIOCPBuffer *pBuffer, DWORD dwTrans, int nError);

    // 事件通知函数
    // 建立了一个新的连接
    virtual void OnConnectionEstablished(CIOCPContext *pContext, CIOCPBuffer *pBuffer);
    // 一个连接关闭
    virtual void OnConnectionClosing(CIOCPContext *pContext, CIOCPBuffer *pBuffer);
    // 在一个连接上发生了错误
    virtual void OnConnectionError(CIOCPContext *pContext, CIOCPBuffer *pBuffer, int nError);
    // 一个连接上的读操作完成
    virtual void OnReadCompleted(CIOCPContext *pContext, CIOCPBuffer *pBuffer);
    // 一个连接上的写操作完成
    virtual void OnWriteCompleted(CIOCPContext *pContext, CIOCPBuffer *pBuffer);

protected:
    // 记录空闲结构信息
    CIOCPBuffer *m_pFreeBufferList;
    CIOCPContext *m_pFreeContextList;
    int m_nFreeBufferCount;
    int m_nFreeContextCount;    
    CRITICAL_SECTION m_FreeBufferListLock;
    CRITICAL_SECTION m_FreeContextListLock;

    CRITICAL_SECTION m_HeapLock;
    CRITICAL_SECTION m_RepostLock;

    // 记录抛出的Accept请求
    CIOCPBuffer *m_pPendingAccepts;   // 抛出请求列表。
    long m_nPendingAcceptCount;
    CRITICAL_SECTION m_PendingAcceptsLock;

    // 记录连接列表
    CIOCPContext *m_pConnectionList;
    int m_nCurrentConnection;
    CRITICAL_SECTION m_ConnectionListLock;


    // 用于投递Accept请求
    HANDLE m_hAcceptEvent;
    HANDLE m_hRepostEvent;
    LONG m_nRepostCount;

    int m_nPort;                // 服务器监听的端口
    int m_nInitialAccepts;
    int m_nInitialReads;
    int m_nMaxAccepts;
    int m_nMaxSends;
    int m_nMaxFreeBuffers;
    int m_nMaxFreeContexts;
    int m_nMaxConnections;

    HANDLE m_hListenThread;         // 监听线程
    HANDLE m_hCompletion;           // 完成端口句柄
    SOCKET m_sListen;               // 监听套节字句柄
    LPFN_ACCEPTEX m_lpfnAcceptEx;   // AcceptEx函数地址
    LPFN_GETACCEPTEXSOCKADDRS m_lpfnGetAcceptExSockaddrs; // GetAcceptExSockaddrs函数地址

    BOOL m_bShutDown;       // 用于通知监听线程退出
    BOOL m_bServerStarted;  // 记录服务是否启动

    HANDLE m_hTimerQueue;//xss 

private:    // 线程函数
    static DWORD WINAPI _ListenThreadProc(LPVOID lpParam);
    static DWORD WINAPI _WorkerThreadProc(LPVOID lpParam);
};

#endif // __IOCP_H__

cpp文件:


//
// IOCP.cpp文件
#define _WIN32_WINNT 0x0500 //xss

#include "iocp.h"
#pragma comment(lib, "WS2_32.lib")

#include <stdio.h>
#include "httpFun.h"

static int iBufferCount = 0;
static int iContextCount = 0;
CIOCPServer::CIOCPServer()
{
    // 列表
    m_pFreeBufferList = NULL;
    m_pFreeContextList = NULL;  
    m_pPendingAccepts = NULL;
    m_pConnectionList = NULL;

    m_nFreeBufferCount = 0;
    m_nFreeContextCount = 0;
    m_nPendingAcceptCount = 0;
    m_nCurrentConnection = 0;

    ::InitializeCriticalSection(&m_FreeBufferListLock);
    ::InitializeCriticalSection(&m_FreeContextListLock);
    ::InitializeCriticalSection(&m_PendingAcceptsLock);
    ::InitializeCriticalSection(&m_ConnectionListLock);

    ::InitializeCriticalSection(&m_HeapLock);
    ::InitializeCriticalSection(&m_RepostLock);

    // Accept请求
    m_hAcceptEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
    m_hRepostEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
    m_nRepostCount = 0;
    m_nPort = 8888;
    m_nInitialAccepts = 10;
    m_nInitialReads = 4;
    m_nMaxAccepts = 100;
    m_nMaxSends = 20;
    m_nMaxFreeBuffers = 200;
    m_nMaxFreeContexts = 100;
    m_nMaxConnections = 2000;

    m_hListenThread = NULL;
    m_hCompletion = NULL;
    m_sListen = INVALID_SOCKET;
    m_lpfnAcceptEx = NULL;
    m_lpfnGetAcceptExSockaddrs = NULL;

    m_bShutDown = FALSE;
    m_bServerStarted = FALSE;
    m_hTimerQueue = ::CreateTimerQueue();

    // 初始化WS2_32.dll
    WSADATA wsaData;
    WORD sockVersion = MAKEWORD(2, 2);
    ::WSAStartup(sockVersion, &wsaData);
}

CIOCPServer::~CIOCPServer()
{
    Shutdown();

    if(m_sListen != INVALID_SOCKET)
        ::closesocket(m_sListen);
    if(m_hListenThread != NULL)
        ::CloseHandle(m_hListenThread);

    ::CloseHandle(m_hRepostEvent);
    ::CloseHandle(m_hAcceptEvent);

    ::DeleteCriticalSection(&m_FreeBufferListLock);
    ::DeleteCriticalSection(&m_FreeContextListLock);
    ::DeleteCriticalSection(&m_PendingAcceptsLock);
    ::DeleteCriticalSection(&m_ConnectionListLock);

    ::DeleteCriticalSection(&m_HeapLock);
    ::DeleteCriticalSection(&m_RepostLock);

    ::DeleteTimerQueue(m_hTimerQueue);//xss
    ::WSACleanup(); 
}

///
static VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{
    CIOCPContext* pContext = (CIOCPContext*)lpParam;
    if(pContext != NULL && pContext->bClosing == FALSE)
    {
        EnterCriticalSection(&pContext->Lock);
        if(pContext->hCompletion != NULL)
        {
            PostQueuedCompletionStatus(pContext->hCompletion,-2,(ULONG_PTR)pContext,NULL);
        }
        LeaveCriticalSection(&pContext->Lock);
    }
}

///
// 自定义帮助函数
CIOCPBuffer *CIOCPServer::AllocateBuffer(int nLen)
{
    CIOCPBuffer *pBuffer = NULL;
    if(nLen > BUFFER_SIZE)
        return NULL;

    // 为缓冲区对象申请内存
    ::EnterCriticalSection(&m_FreeBufferListLock);
    if(m_pFreeBufferList == NULL)  // 内存池为空,申请新的内存
    {
//      pBuffer = (CIOCPBuffer *)::HeapAlloc(GetProcessHeap(), 
//                      HEAP_ZERO_MEMORY, sizeof(CIOCPBuffer) + BUFFER_SIZE);
        pBuffer = new CIOCPBuffer();
    }
    else    // 从内存池中取一块来使用
    {
        pBuffer = m_pFreeBufferList;
        m_pFreeBufferList = m_pFreeBufferList->pNext;   
        pBuffer->pNext = NULL;
        m_nFreeBufferCount --;
    }
    ::LeaveCriticalSection(&m_FreeBufferListLock);

    EnterCriticalSection(&m_HeapLock);
    iBufferCount++;
    LeaveCriticalSection(&m_HeapLock);

    // 初始化新的缓冲区对象
    if(pBuffer != NULL)
    {
        //pBuffer->buff = (char*)(pBuffer + sizeof(CIOCPBuffer)/*1*/);//xss,个人以为应该+sizeof(CIOCPBuffer);
        pBuffer->nLen = nLen;
        pBuffer->bIsReleased = FALSE;
    }
    return pBuffer;
}

void CIOCPServer::ReleaseBuffer(CIOCPBuffer *pBuffer)
{
    if(pBuffer == NULL || pBuffer->bIsReleased)
        return;

    ::EnterCriticalSection(&m_FreeBufferListLock);

    if(m_nFreeBufferCount <= m_nMaxFreeBuffers) // 将要释放的内存添加到空闲列表中
    {
        memset(pBuffer, 0, sizeof(CIOCPBuffer) /*+ BUFFER_SIZE*/);
        pBuffer->pNext = m_pFreeBufferList;
        m_pFreeBufferList = pBuffer;

        m_nFreeBufferCount ++ ;
        pBuffer->bIsReleased = TRUE;
    }
    else            // 已经达到最大值,真正的释放内存
    {
        //::HeapFree(::GetProcessHeap(), 0, pBuffer);
        delete pBuffer;
    }
    ::LeaveCriticalSection(&m_FreeBufferListLock);

    EnterCriticalSection(&m_HeapLock);
    iBufferCount--;
    LeaveCriticalSection(&m_HeapLock);
}

CIOCPContext *CIOCPServer::AllocateContext(SOCKET s)
{
    CIOCPContext *pContext;

    // 申请一个CIOCPContext对象
    ::EnterCriticalSection(&m_FreeContextListLock);

    if(m_pFreeContextList == NULL)
    {
        //pContext = (CIOCPContext *)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CIOCPContext));
        pContext = new CIOCPContext();

        ::InitializeCriticalSection(&pContext->Lock);
    }
    else    
    {
        // 在空闲列表中申请
        pContext = m_pFreeContextList;
        m_pFreeContextList = m_pFreeContextList->pNext;
        pContext->pNext = NULL;
        m_nFreeBufferCount --;
    }
    ::LeaveCriticalSection(&m_FreeContextListLock);

    EnterCriticalSection(&m_HeapLock);
    iContextCount++;
    LeaveCriticalSection(&m_HeapLock);

    // 初始化对象成员
    if(pContext != NULL)
    {
        pContext->s = s;
        pContext->bIsReleased = FALSE;
    }
    return pContext;
}

void CIOCPServer::ReleaseContext(CIOCPContext *pContext)
{
    if(pContext == NULL || pContext->bIsReleased)
        return;

    printf("\n%s释放了Context\n\n",pContext->szClientName);
    if(pContext->s != INVALID_SOCKET)
        ::closesocket(pContext->s);

    // 首先释放(如果有的话)此套节字上的没有按顺序完成的读I/O的缓冲区
    CIOCPBuffer *pNext;
    while(pContext->pOutOfOrderReads != NULL)
    {
        pNext = pContext->pOutOfOrderReads->pNext;
        ReleaseBuffer(pContext->pOutOfOrderReads);
        pContext->pOutOfOrderReads = pNext;
    }

    //xss,再释放(如果有的话)此套接字上未完成的写I/O缓冲区
    CIOCPNextToSend* pSend = NULL;
    while(pContext->pNextToSend != NULL)
    {
        pSend = pContext->pNextToSend->pNext;
        if(pContext->pNextToSend->pBuffer != NULL && pContext->pNextToSend->pBuffer->bIsReleased == FALSE)
        {
            ReleaseBuffer(pContext->pNextToSend->pBuffer);
        }
        delete pContext->pNextToSend;
        pContext->pNextToSend = pSend;
    }

    if(pContext->hTimer != NULL)
    {
        DeleteTimerQueueTimer(m_hTimerQueue,pContext->hTimer,NULL);
        pContext->hTimer = NULL;    
    }

    ::EnterCriticalSection(&m_FreeContextListLock);

    if(m_nFreeContextCount <= m_nMaxFreeContexts) // 添加到空闲列表
    {
        // 先将关键代码段变量保存到一个临时变量中
        CRITICAL_SECTION cstmp = pContext->Lock;
        // 将要释放的上下文对象初始化为0
        memset(pContext, 0, sizeof(CIOCPContext));

        // 再放会关键代码段变量,将要释放的上下文对象添加到空闲列表的表头
        pContext->Lock = cstmp;
        pContext->pNext = m_pFreeContextList;
        m_pFreeContextList = pContext;

        // 更新计数
        m_nFreeContextCount ++;

        pContext->bIsReleased = TRUE;
    }
    else
    {
        ::DeleteCriticalSection(&pContext->Lock);
        //::HeapFree(::GetProcessHeap(), 0, pContext);
        delete pContext;
    }
    ::LeaveCriticalSection(&m_FreeContextListLock);

    EnterCriticalSection(&m_HeapLock);
    iContextCount--;
    LeaveCriticalSection(&m_HeapLock);  
}

void CIOCPServer::FreeBuffers()
{
    // 遍历m_pFreeBufferList空闲列表,释放缓冲区池内存
    ::EnterCriticalSection(&m_FreeBufferListLock);

    CIOCPBuffer *pFreeBuffer = m_pFreeBufferList;
    CIOCPBuffer *pNextBuffer;
    while(pFreeBuffer != NULL)
    {
        pNextBuffer = pFreeBuffer->pNext;

        delete pFreeBuffer;
//      if(!::HeapFree(::GetProcessHeap(), 0, pFreeBuffer))
//      {
// #ifdef _DEBUG
//          ::OutputDebugString("  FreeBuffers释放内存出错!");
// #endif // _DEBUG
//          break;
//      }
        pFreeBuffer = pNextBuffer;
    }
    m_pFreeBufferList = NULL;
    m_nFreeBufferCount = 0;

    ::LeaveCriticalSection(&m_FreeBufferListLock);
}

void CIOCPServer::FreeContexts()
{
    // 遍历m_pFreeContextList空闲列表,释放缓冲区池内存
    ::EnterCriticalSection(&m_FreeContextListLock);

    CIOCPContext *pFreeContext = m_pFreeContextList;
    CIOCPContext *pNextContext;
    while(pFreeContext != NULL)
    {
        pNextContext = pFreeContext->pNext;

        ::DeleteCriticalSection(&pFreeContext->Lock);
        delete pFreeContext;
//      if(!::HeapFree(::GetProcessHeap(), 0, pFreeContext))
//      {
// #ifdef _DEBUG
//          ::OutputDebugString("  FreeBuffers释放内存出错!");
// #endif // _DEBUG
//          break;
//      }
        pFreeContext = pNextContext;
    }
    m_pFreeContextList = NULL;
    m_nFreeContextCount = 0;

    ::LeaveCriticalSection(&m_FreeContextListLock);
}

BOOL CIOCPServer::AddAConnection(CIOCPContext *pContext)
{
    // 向客户连接列表添加一个CIOCPContext对象

    ::EnterCriticalSection(&m_ConnectionListLock);
    if(m_nCurrentConnection <= m_nMaxConnections)
    {
        // 添加到表头
        pContext->pNext = m_pConnectionList;
        m_pConnectionList = pContext;
        // 更新计数
        m_nCurrentConnection ++;

        ::LeaveCriticalSection(&m_ConnectionListLock);
        return TRUE;
    }
    ::LeaveCriticalSection(&m_ConnectionListLock);

    return FALSE;
}

void CIOCPServer::CloseAConnection(CIOCPContext *pContext)
{
    if(pContext == NULL || pContext->bClosing == TRUE)
        return;

    // 首先从列表中移除要关闭的连接
    ::EnterCriticalSection(&m_ConnectionListLock);

    CIOCPContext* pTest = m_pConnectionList;
    if(pTest == pContext)
    {
        m_pConnectionList =  pContext->pNext;
        m_nCurrentConnection --;
    }
    else
    {
        while(pTest != NULL && pTest->pNext !=  pContext)
            pTest = pTest->pNext;
        if(pTest != NULL)
        {
            pTest->pNext =  pContext->pNext;
            m_nCurrentConnection --;
        }
    }
    ::LeaveCriticalSection(&m_ConnectionListLock);

    // 然后关闭客户套节字
    ::EnterCriticalSection(&pContext->Lock);
    if(pContext->s != INVALID_SOCKET)
    {
        ::closesocket(pContext->s); 
        pContext->s = INVALID_SOCKET;
    }
    pContext->bClosing = TRUE;
    ::LeaveCriticalSection(&pContext->Lock);
}

void CIOCPServer::CloseAllConnections()
{
    // 遍历整个连接列表,关闭所有的客户套节字
    ::EnterCriticalSection(&m_ConnectionListLock);

    CIOCPContext *pContext = m_pConnectionList;
    while(pContext != NULL)
    {   
        ::EnterCriticalSection(&pContext->Lock);
        if(pContext->s != INVALID_SOCKET)
        {
            ::closesocket(pContext->s);
            pContext->s = INVALID_SOCKET;
        }

        pContext->bClosing = TRUE;
        ::LeaveCriticalSection(&pContext->Lock);    
        pContext = pContext->pNext;
    }

    m_pConnectionList = NULL;
    m_nCurrentConnection = 0;
    ::LeaveCriticalSection(&m_ConnectionListLock);
}

BOOL CIOCPServer::InsertPendingAccept(CIOCPBuffer *pBuffer)
{
    // 将一个I/O缓冲区对象插入到m_pPendingAccepts表中
    ::EnterCriticalSection(&m_PendingAcceptsLock);

    if(m_pPendingAccepts == NULL)
        m_pPendingAccepts = pBuffer;
    else
    {
        pBuffer->pNext = m_pPendingAccepts;
        m_pPendingAccepts = pBuffer;
    }
    m_nPendingAcceptCount ++;
    ::LeaveCriticalSection(&m_PendingAcceptsLock);
    return TRUE;
}

BOOL CIOCPServer::RemovePendingAccept(CIOCPBuffer *pBuffer)
{
    BOOL bResult = FALSE;

    // 遍历m_pPendingAccepts表,从中移除pBuffer所指向的缓冲区对象
    ::EnterCriticalSection(&m_PendingAcceptsLock);

    CIOCPBuffer *pTest = m_pPendingAccepts;
    if(pTest == pBuffer)    // 如果是表头元素
    {
        m_pPendingAccepts = pBuffer->pNext;
        bResult = TRUE;
    }
    else                    // 不是表头元素的话,就要遍历这个表来查找了
    {
        while(pTest != NULL && pTest->pNext != pBuffer)
            pTest = pTest->pNext;
        if(pTest != NULL)
        {
            pTest->pNext = pBuffer->pNext;
             bResult = TRUE;
        }
    }
    // 更新计数
    if(bResult)
        m_nPendingAcceptCount --;

    ::LeaveCriticalSection(&m_PendingAcceptsLock);
    return  bResult;
}

void CIOCPServer::ErrorHandle(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
{
    CloseAConnection(pContext);
}

BOOL CIOCPServer::PostSendToList(CIOCPContext *pContext, CIOCPBuffer *pBuffer)//xss
{
    ::EnterCriticalSection(&pContext->Lock);
    CIOCPNextToSend *ptr = pContext->pNextToSend;

    CIOCPNextToSend * pSend = new CIOCPNextToSend();
    pSend->pBuffer = pBuffer;
    pSend->pNext = NULL;
    if(ptr == NULL)
    {
        printf("数据:%10.10s ...,被直接发送。\n",pBuffer->buff);
        //::EnterCriticalSection(&pContext->Lock);
        pContext->pNextToSend = pSend;
        //::LeaveCriticalSection(&pContext->Lock);
        if(!PostSend(pContext,pBuffer))//如果没有需要等待的send就直接发送
        {
            ::LeaveCriticalSection(&pContext->Lock);
            return FALSE;
        }
    }
    else
    {
        printf("数据:%10.10s ...,被放入链表结尾。\n",pBuffer->buff);
        while(ptr->pNext != NULL)
        {
            ptr = ptr->pNext;
        }
        ptr->pNext = pSend;//新的发送请求放在链表结尾
    }
    ::LeaveCriticalSection(&pContext->Lock);
    return TRUE;
}

BOOL CIOCPServer::PostNextWriteBuffer(CIOCPContext *pContext, CIOCPBuffer *pBuffer)//xss
{
    ::EnterCriticalSection(&pContext->Lock);
    CIOCPNextToSend* pSend = pContext->pNextToSend;
    CIOCPNextToSend* pNextSend = NULL;
    if(pSend != NULL && pSend->pNext != NULL)//发送成功的pBuffer是队列的第一个,发送下一个,pNextToSend指向下一个,pBuffer由外面释放。
    {
        pNextSend = pSend->pNext;
        if(pNextSend->pBuffer != NULL)
        {
            printf("数据:%10.10s ...从链表中弹出被发送。\n",pNextSend->pBuffer->buff);
            if(!PostSend(pContext,pNextSend->pBuffer))
            {
                delete pSend;
                pContext->pNextToSend = pNextSend;
                ::LeaveCriticalSection(&pContext->Lock);
                return FALSE;
            }
        }
    }
    if(pSend != NULL)
    {
        pNextSend = pSend->pNext;
        delete pSend;
        pContext->pNextToSend = pNextSend;
    }
    ::LeaveCriticalSection(&pContext->Lock);
    return TRUE;
}

CIOCPBuffer *CIOCPServer::GetNextReadBuffer(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
{
    if(pBuffer != NULL)
    {
        // 如果与要读的下一个序列号相等,则读这块缓冲区
        if(pBuffer->nSequenceNumber == pContext->nCurrentReadSequence)
        {
            return pBuffer;
        }

        // 如果不相等,则说明没有按顺序接收数据,将这块缓冲区保存到连接的pOutOfOrderReads列表中

        // 列表中的缓冲区是按照其序列号从小到大的顺序排列的

        pBuffer->pNext = NULL;

        CIOCPBuffer *ptr = pContext->pOutOfOrderReads;
        CIOCPBuffer *pPre = NULL;
        while(ptr != NULL)
        {
            if(pBuffer->nSequenceNumber < ptr->nSequenceNumber)
                break;

            pPre = ptr;
            ptr = ptr->pNext;
        }

        if(pPre == NULL) // 应该插入到表头
        {
            pBuffer->pNext = pContext->pOutOfOrderReads;
            pContext->pOutOfOrderReads = pBuffer;
        }
        else            // 应该插入到表的中间
        {
            pBuffer->pNext = pPre->pNext;
            pPre->pNext = pBuffer/*->pNext*/;//xss,个人觉得应该是pPre->pNext = pBuffer;
        }
    }

    // 检查表头元素的序列号,如果与要读的序列号一致,就将它从表中移除,返回给用户
    CIOCPBuffer *ptr = pContext->pOutOfOrderReads;
    if(ptr != NULL && (ptr->nSequenceNumber == pContext->nCurrentReadSequence))
    {
        pContext->pOutOfOrderReads = ptr->pNext;
        return ptr;
    }
    return NULL;
}

BOOL CIOCPServer::PostAccept(CIOCPBuffer *pBuffer)  // 在监听套节字上投递Accept请求
{
    // 设置I/O类型
    pBuffer->nOperation = OP_ACCEPT;

    // 投递此重叠I/O  
    DWORD dwBytes;
    pBuffer->sClient = ::WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
    BOOL b = m_lpfnAcceptEx(m_sListen, 
        pBuffer->sClient,
        pBuffer->buff, 
        pBuffer->nLen - ((sizeof(sockaddr_in) + 16) * 2),//xss,第一次都是收一个cmd_header
        sizeof(sockaddr_in) + 16, 
        sizeof(sockaddr_in) + 16, 
        &dwBytes, 
        &pBuffer->ol);
    if(!b && ::WSAGetLastError() != WSA_IO_PENDING)
    {
        return FALSE;
    }
    if(pBuffer->nOperation == 0)
    {
        int x = 0;
    }
    return TRUE;
};

BOOL CIOCPServer::PostRecv(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
{
    // 设置I/O类型
    pBuffer->nOperation = OP_READ;  

    ::EnterCriticalSection(&pContext->Lock);

    // 设置序列号
    pBuffer->nSequenceNumber = pContext->nReadSequence;

    // 投递此重叠I/O
    DWORD dwBytes;
    DWORD dwFlags = 0;
    WSABUF buf;
    buf.buf = pBuffer->buff;
    buf.len = pBuffer->nLen;
    if(::WSARecv(pContext->s, &buf, 1, &dwBytes, &dwFlags, &pBuffer->ol, NULL) != NO_ERROR)
    {
        if(::WSAGetLastError() != WSA_IO_PENDING)
        {
            printf("WSARecv出错:%d\n",WSAGetLastError());
            ::LeaveCriticalSection(&pContext->Lock);
            return FALSE;
        }
    }

    // 增加套节字上的重叠I/O计数和读序列号计数
    pContext->nOutstandingRecv ++;
    pContext->nReadSequence ++;

    ::LeaveCriticalSection(&pContext->Lock);
    return TRUE;
}

BOOL CIOCPServer::PostSend(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
{   
    // 跟踪投递的发送的数量,防止用户仅发送数据而不接收,导致服务器抛出大量发送操作
    if(pContext->nOutstandingSend > m_nMaxSends)
        return FALSE;

    // 设置I/O类型,增加套节字上的重叠I/O计数
    pBuffer->nOperation = OP_WRITE;

    // 投递此重叠I/O
    DWORD dwBytes;
    DWORD dwFlags = 0;
    WSABUF buf;
    buf.buf = pBuffer->buff;
    buf.len = pBuffer->nLen;
    if(::WSASend(pContext->s, &buf, 1, &dwBytes, dwFlags, &pBuffer->ol, NULL) != NO_ERROR)
    {
        int x;
        if((x=::WSAGetLastError()) != WSA_IO_PENDING)
        {
            printf("发送失败!错误码:%d",x);
            return FALSE;
        }
    }   
    // 增加套节字上的重叠I/O计数
    ::EnterCriticalSection(&pContext->Lock);
    pContext->nOutstandingSend ++;
    ::LeaveCriticalSection(&pContext->Lock);

    if(pBuffer->nOperation == 0)
    {
        int x = 0;
    }
    return TRUE;
}

BOOL CIOCPServer::Start(int nPort, int nMaxConnections, int nMaxFreeBuffers, int nMaxFreeContexts, int nInitialReads)
{
    // 检查服务是否已经启动
    if(m_bServerStarted)
        return FALSE;

    // 保存用户参数
    m_nPort = nPort;
    m_nMaxConnections = nMaxConnections;
    m_nMaxFreeBuffers = nMaxFreeBuffers;
    m_nMaxFreeContexts = nMaxFreeContexts;
    m_nInitialReads = nInitialReads;

    // 初始化状态变量
    m_bShutDown = FALSE;
    m_bServerStarted = TRUE;

    // 创建监听套节字,绑定到本地端口,进入监听模式
    m_sListen = ::WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
    SOCKADDR_IN si;
    si.sin_family = AF_INET;
    si.sin_port = ::ntohs(m_nPort);
    si.sin_addr.S_un.S_addr = INADDR_ANY;
    if(::bind(m_sListen, (sockaddr*)&si, sizeof(si)) == SOCKET_ERROR)
    {
        m_bServerStarted = FALSE;
        return FALSE;
    }
    ::listen(m_sListen, 200);

    // 创建完成端口对象
    m_hCompletion = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);

    // 加载扩展函数AcceptEx
    GUID GuidAcceptEx = WSAID_ACCEPTEX;
    DWORD dwBytes;
    ::WSAIoctl(m_sListen, 
        SIO_GET_EXTENSION_FUNCTION_POINTER, 
        &GuidAcceptEx, 
        sizeof(GuidAcceptEx),
        &m_lpfnAcceptEx, 
        sizeof(m_lpfnAcceptEx), 
        &dwBytes, 
        NULL, 
        NULL);

    // 加载扩展函数GetAcceptExSockaddrs
    GUID GuidGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS;
    ::WSAIoctl(m_sListen,
        SIO_GET_EXTENSION_FUNCTION_POINTER,
        &GuidGetAcceptExSockaddrs,
        sizeof(GuidGetAcceptExSockaddrs),
        &m_lpfnGetAcceptExSockaddrs,
        sizeof(m_lpfnGetAcceptExSockaddrs),
        &dwBytes,
        NULL,
        NULL
        );

    // 将监听套节字关联到完成端口,注意,这里为它传递的CompletionKey为0
    ::CreateIoCompletionPort((HANDLE)m_sListen, m_hCompletion, (DWORD)0, 0);

    // 注册FD_ACCEPT事件。
    // 如果投递的AcceptEx I/O不够,线程会接收到FD_ACCEPT网络事件,说明应该投递更多的AcceptEx I/O
    WSAEventSelect(m_sListen, m_hAcceptEvent, FD_ACCEPT);

    // 创建监听线程
    m_hListenThread = ::CreateThread(NULL, 0, _ListenThreadProc, this, 0, NULL);

    return TRUE;
}

void CIOCPServer::Shutdown()
{
    if(!m_bServerStarted)
        return;

    // 通知监听线程,马上停止服务
    m_bShutDown = TRUE;
    ::SetEvent(m_hAcceptEvent);
    // 等待监听线程退出
    ::WaitForSingleObject(m_hListenThread, INFINITE);
    ::CloseHandle(m_hListenThread);
    m_hListenThread = NULL;
    m_bServerStarted = FALSE;
}

DWORD WINAPI CIOCPServer::_ListenThreadProc(LPVOID lpParam)
{
    CIOCPServer *pThis = (CIOCPServer*)lpParam;

    // 先在监听套节字上投递几个Accept I/O
    CIOCPBuffer *pBuffer;
    for(int i=0; i<pThis->m_nInitialAccepts; i++)
    {
        pBuffer = pThis->AllocateBuffer(BUFFER_SIZE);//xss,BUFFER_SIZE
        if(pBuffer == NULL)
            return -1;
        pThis->InsertPendingAccept(pBuffer);
        pThis->PostAccept(pBuffer);
    }

    // 构建事件对象数组,以便在上面调用WSAWaitForMultipleEvents函数
    HANDLE hWaitEvents[2 + MAX_THREAD];
    int nEventCount = 0;
    hWaitEvents[nEventCount ++] = pThis->m_hAcceptEvent;
    hWaitEvents[nEventCount ++] = pThis->m_hRepostEvent;

    // 创建指定数量的工作线程在完成端口上处理I/O
    for(int i=0; i<MAX_THREAD; i++)
    {
        hWaitEvents[nEventCount ++] = ::CreateThread(NULL, 0, _WorkerThreadProc, pThis, 0, NULL);
    }

    // 下面进入无限循环,处理事件对象数组中的事件
    while(TRUE)
    {
        int nIndex = ::WSAWaitForMultipleEvents(nEventCount, hWaitEvents, FALSE, 60*1000, FALSE);

        // 首先检查是否要停止服务
        if(pThis->m_bShutDown || nIndex == WSA_WAIT_FAILED)
        {
            // 关闭所有连接
            pThis->CloseAllConnections();
            ::Sleep(0);     // 给I/O工作线程一个执行的机会
            // 关闭监听套节字
            ::closesocket(pThis->m_sListen);
            pThis->m_sListen = INVALID_SOCKET;
            ::Sleep(0);     // 给I/O工作线程一个执行的机会

            // 通知所有I/O处理线程退出
            for(int i=2; i<MAX_THREAD + 2; i++)
            {   
                ::PostQueuedCompletionStatus(pThis->m_hCompletion, -1, 0, NULL);
            }

            // 等待I/O处理线程退出
            ::WaitForMultipleObjects(MAX_THREAD, &hWaitEvents[2], TRUE, 5*1000);

            for(int i=2; i<MAX_THREAD + 2; i++)
            {   
                ::CloseHandle(hWaitEvents[i]);
            }

            ::CloseHandle(pThis->m_hCompletion);

            pThis->FreeBuffers();
            pThis->FreeContexts();
            ::ExitThread(0);
        }   

        // 1)定时检查所有未返回的AcceptEx I/O的连接建立了多长时间
        if(nIndex == WSA_WAIT_TIMEOUT)
        {
            pBuffer = pThis->m_pPendingAccepts;
            while(pBuffer != NULL)
            {
                int nSeconds;
                int nLen = sizeof(nSeconds);
                // 取得连接建立的时间
                ::getsockopt(pBuffer->sClient, 
                    SOL_SOCKET, SO_CONNECT_TIME, (char *)&nSeconds, &nLen); 
                // 如果超过2分钟客户还不发送初始数据,就让这个客户go away
                if(nSeconds != -1 && nSeconds > /*2*60*/50)
                {   
                    closesocket(pBuffer->sClient);
                    pBuffer->sClient = INVALID_SOCKET;
                }
                pBuffer = pBuffer->pNext;
            }
        }
        else
        {
            nIndex = nIndex - WAIT_OBJECT_0;
            WSANETWORKEVENTS ne;
            int nLimit=0;
            if(nIndex == 0)     // 2)m_hAcceptEvent事件对象受信,说明投递的Accept请求不够,需要增加
            {
                ::WSAEnumNetworkEvents(pThis->m_sListen, hWaitEvents[nIndex], &ne);
                if(ne.lNetworkEvents & FD_ACCEPT)
                {
                    nLimit = 50;  // 增加的个数,这里设为50个
                }
            }
            else if(nIndex == 1)    // 3)m_hRepostEvent事件对象受信,说明处理I/O的线程接受到新的客户
            {
                nLimit = InterlockedExchange(&pThis->m_nRepostCount, 0);
            }
            else if(nIndex > 1)     // I/O服务线程退出,说明有错误发生,关闭服务器
            {
                pThis->m_bShutDown = TRUE;
                continue;
            }

            // 投递nLimit个AcceptEx I/O请求
            int i = 0;
            while(i++ < nLimit && pThis->m_nPendingAcceptCount < pThis->m_nMaxAccepts)
            {
                pBuffer = pThis->AllocateBuffer(BUFFER_SIZE);
                if(pBuffer != NULL)
                {
                    pThis->InsertPendingAccept(pBuffer);
                    pThis->PostAccept(pBuffer);
                }
            }
        }
    }
    return 0;
}


DWORD WINAPI CIOCPServer::_WorkerThreadProc(LPVOID lpParam)
{
#ifdef _DEBUG
            ::OutputDebugString("   WorkerThread 启动... \n");
#endif // _DEBUG

    CIOCPServer *pThis = (CIOCPServer*)lpParam;

    CIOCPBuffer *pBuffer = NULL;
    DWORD dwKey;
    DWORD dwTrans;
    LPOVERLAPPED lpol;

    while(TRUE)
    {
        // 在关联到此完成端口的所有套节字上等待I/O完成
        BOOL bOK = ::GetQueuedCompletionStatus(pThis->m_hCompletion, 
                    &dwTrans, (LPDWORD)&dwKey, (LPOVERLAPPED*)&lpol, WSA_INFINITE);

        if(dwTrans == -1) // 用户通知退出
        {
#ifdef _DEBUG
            ::OutputDebugString("   WorkerThread 退出 \n");
#endif // _DEBUG
            ::ExitThread(0);
        }
        if(dwTrans != -2)
            pBuffer = CONTAINING_RECORD(lpol, CIOCPBuffer, ol);
        int nError = NO_ERROR;
        if(!bOK)                        // 在此套节字上有错误发生
        {
            printf("完成端口套接字上有错误:%d\n",GetLastError());
            SOCKET s;
            if(pBuffer->nOperation == OP_ACCEPT)
            {
                s = pThis->m_sListen;
            }
            else
            {
                if(dwKey == 0)
                    break;
                s = ((CIOCPContext*)dwKey)->s;
            }
            DWORD dwFlags = 0;
            if(!::WSAGetOverlappedResult(s, &pBuffer->ol, &dwTrans, FALSE, &dwFlags))
            {
                nError = ::WSAGetLastError();
            }
        }
        pThis->HandleIO(dwKey, pBuffer, dwTrans, nError);
        printf("Buffer:%d     Context:%d\n",iBufferCount,iContextCount);
    }

#ifdef _DEBUG
            ::OutputDebugString("   WorkerThread 退出 \n");
#endif // _DEBUG
    return 0;
}

int g_x = 0;
void CIOCPServer::HandleIO(DWORD dwKey, CIOCPBuffer *pBuffer, DWORD dwTrans, int nError)
{
    CIOCPContext *pContext = (CIOCPContext *)dwKey;

#ifdef _DEBUG
            ::OutputDebugString("   HandleIO... \n");
#endif // _DEBUG

    // 1)首先减少套节字上的未决I/O计数
    if(dwTrans == -2)
    {
        CloseAConnection(pContext);
        return;
    }
    if(pContext != NULL)
    {
        ::EnterCriticalSection(&pContext->Lock);

        if(pBuffer->nOperation == OP_READ)
            pContext->nOutstandingRecv --;
        else if(pBuffer->nOperation == OP_WRITE)
            pContext->nOutstandingSend --;

        ::LeaveCriticalSection(&pContext->Lock);

        // 2)检查套节字是否已经被我们关闭
        if(pContext->bClosing) 
        {
#ifdef _DEBUG
            ::OutputDebugString("   检查到套节字已经被我们关闭 \n");
#endif // _DEBUG
            if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
            {       
                ReleaseContext(pContext);
                pContext = NULL;
            }
            // 释放已关闭套节字的未决I/O
            ReleaseBuffer(pBuffer);
            pBuffer = NULL;
            return;
        }
    }
    else
    {
        RemovePendingAccept(pBuffer);
    }


    // 3)检查套节字上发生的错误,如果有的话,通知用户,然后关闭套节字
    if(nError != NO_ERROR)
    {
        if(pBuffer->nOperation != OP_ACCEPT)
        {
            OnConnectionError(pContext, pBuffer, nError);
            CloseAConnection(pContext);
            if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
            {       
                ReleaseContext(pContext);
                pContext = NULL;
            }
#ifdef _DEBUG
            ::OutputDebugString("   检查到客户套节字上发生错误 \n");
#endif // _DEBUG
        }
        else // 在监听套节字上发生错误,也就是监听套节字处理的客户出错了
        {
            // 客户端出错,释放I/O缓冲区
            if(pBuffer->sClient != INVALID_SOCKET)
            {
                ::closesocket(pBuffer->sClient);
                pBuffer->sClient = INVALID_SOCKET;
            }
#ifdef _DEBUG
            ::OutputDebugString("   检查到监听套节字上发生错误 \n");
#endif // _DEBUG
        }

        ReleaseBuffer(pBuffer);
        pBuffer = NULL;
        return;
    }

    // 开始处理
    if(pBuffer->nOperation == OP_ACCEPT)
    {
        if(dwTrans == 0)
        {
#ifdef _DEBUG
            ::OutputDebugString("   监听套节字上客户端关闭 \n");
#endif // _DEBUG

            if(pBuffer->sClient != INVALID_SOCKET)
            {
                ::closesocket(pBuffer->sClient);
                pBuffer->sClient = INVALID_SOCKET;
            }
        }
        else
        {
            // 为新接受的连接申请客户上下文对象
            CIOCPContext *pClient = AllocateContext(pBuffer->sClient);
            if(pClient != NULL)
            {
                if(AddAConnection(pClient))
                {   
                    // 取得客户地址
                    int nLocalLen, nRmoteLen;
                    LPSOCKADDR pLocalAddr, pRemoteAddr;
                    m_lpfnGetAcceptExSockaddrs(
                        pBuffer->buff,
                        pBuffer->nLen - (sizeof(sockaddr_in) + 16) * 2/*sizeof(cmd_header)*/,
                        sizeof(sockaddr_in) + 16,
                        sizeof(sockaddr_in) + 16,
                        (SOCKADDR **)&pLocalAddr,
                        &nLocalLen,
                        (SOCKADDR **)&pRemoteAddr,
                        &nRmoteLen);
                    memcpy(&pClient->addrLocal, pLocalAddr, nLocalLen);
                    memcpy(&pClient->addrRemote, pRemoteAddr, nRmoteLen);

                    // 关联新连接到完成端口对象
                    ::CreateIoCompletionPort((HANDLE)pClient->s, m_hCompletion, (DWORD)pClient, 0);

                    // 通知用户
                    pBuffer->nLen = dwTrans;
                    OnConnectionEstablished(pClient, pBuffer);

                    if(pClient->bClosing && pClient->nOutstandingRecv == 0 && pClient->nOutstandingSend == 0)
                    {
                        ReleaseContext(pClient);
                        pContext = NULL;
                    }
                    else if(pClient->hTimer == NULL)//接收一个客户端的同时创建一个检测I/O超时的Timer
                    {
                        pClient->hCompletion = m_hCompletion;
                        CreateTimerQueueTimer(&pClient->hTimer,m_hTimerQueue,(WAITORTIMERCALLBACK)TimerRoutine,(PVOID)pClient,60*1000,0,0);
                    }

                    // 向新连接投递Read请求或者Write请求,直接关闭这些空间在套节字关闭或出错时释放
//                      CIOCPBuffer *p = AllocateBuffer(BUFFER_SIZE);
//                      if(p != NULL)
//                      {
//                          if(!PostRecv(pClient, p))
//                          {
//                              CloseAConnection(pClient);
//                          }
//                      }

                }
                else    // 连接数量已满,关闭连接
                {
                    CloseAConnection(pClient);
                    ReleaseContext(pClient);
                    pContext = NULL;
                }
            }
            else
            {
                // 资源不足,关闭与客户的连接即可
                ::closesocket(pBuffer->sClient);
                pBuffer->sClient = INVALID_SOCKET;
            }
        }

        // Accept请求完成,释放I/O缓冲区
        ReleaseBuffer(pBuffer);
        pBuffer = NULL;

        // 通知监听线程继续再投递一个Accept请求
        ::InterlockedIncrement(&m_nRepostCount);
        ::SetEvent(m_hRepostEvent);
    }
    else if(pBuffer->nOperation == OP_READ)
    {
        if(dwTrans == 0)    // 对方关闭套节字
        {
            // 先通知用户
            pBuffer->nLen = 0;
            OnConnectionClosing(pContext, pBuffer); 
            // 再关闭连接
            CloseAConnection(pContext);
            // 释放客户上下文和缓冲区对象
            if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
            {       
                ReleaseContext(pContext);
                pContext = NULL;
            }
            ReleaseBuffer(pBuffer);
            pBuffer = NULL;
        }
        else
        {
            pBuffer->nLen = dwTrans;
            // 按照I/O投递的顺序读取接收到的数据
            CIOCPBuffer *p = GetNextReadBuffer(pContext, pBuffer);
            while(p != NULL)
            {
                // 通知用户
                OnReadCompleted(pContext, p);
                // 增加要读的序列号的值
                ::InterlockedIncrement((LONG*)&pContext->nCurrentReadSequence);
                // 释放这个已完成的I/O
                ReleaseBuffer(p);
                p = GetNextReadBuffer(pContext, NULL);
            }

            if(pContext->bClosing && pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
            {
                ReleaseContext(pContext);
                pContext = NULL;
            }
            else if(pContext->hTimer != NULL)
            {
                //重置监视时间,当一个投递完成后,60s内无任何交互则断开。
                ChangeTimerQueueTimer(m_hTimerQueue,pContext->hTimer,60*1000,0);
            }

            // 继续投递一个新的接收请求
         //   pBuffer = AllocateBuffer(BUFFER_SIZE);
            //if(pBuffer == NULL || !PostRecv(pContext, pBuffer))
            //{
            //  CloseAConnection(pContext);
            //}
        }
    }
    else if(pBuffer->nOperation == OP_WRITE)
    {
        if(dwTrans == 0)    // 对方关闭套节字
        {
            // 先通知用户
            pBuffer->nLen = 0;
            OnConnectionClosing(pContext, pBuffer); 

            // 再关闭连接
            CloseAConnection(pContext);

            // 释放客户上下文和缓冲区对象
            if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
            {       
                ReleaseContext(pContext);
                pContext = NULL;
            }
            ReleaseBuffer(pBuffer);
            pBuffer = NULL;
        }
        else
        {
            if(pContext->bClosing && pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
            {
                ReleaseContext(pContext);
                pContext = NULL;
                ReleaseBuffer(pBuffer);
                pBuffer = NULL;
                return;
            }
            else if(pContext->hTimer != NULL)
            {           
                ChangeTimerQueueTimer(m_hTimerQueue,pContext->hTimer,60*1000,0);
            }

            // 写操作完成,通知用户
            if(dwTrans < pBuffer->nLen)//如果此send没有发送完全,则发送剩下的部分(此部分如果还是没发完全,这里同样进行)
            {
                printf("send未发送完全,发送:%d,总长度:%d\n",dwTrans,pBuffer->nLen);
                CIOCPBuffer* p = AllocateBuffer(pBuffer->nLen - dwTrans);
                if(p != NULL)
                    memcpy(p->buff,pBuffer->buff + dwTrans,pBuffer->nLen - dwTrans);
                if(p == NULL || !PostSend(pContext,p))
                {
                    CloseAConnection(pContext);
                    return;
                }
            }
            else
            {
                if(!PostNextWriteBuffer(pContext,pBuffer))
                {
                    CloseAConnection(pContext);
                    return;
                }
            }
            pBuffer->nLen = dwTrans;
            OnWriteCompleted(pContext, pBuffer);
            if(pContext->bClosing && pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
            {
                ReleaseContext(pContext);
                pContext = NULL;
            }
            // 释放SendText函数申请的缓冲区
            ReleaseBuffer(pBuffer);
            pBuffer = NULL;
        }
    }
}

BOOL CIOCPServer::SendText(CIOCPContext *pContext, char *pszText, int nLen)
{
    CIOCPBuffer *pBuffer = AllocateBuffer(nLen);
    if(pBuffer != NULL)
    {
        memcpy(pBuffer->buff, pszText, nLen);
        return PostSend(pContext, pBuffer);
    }
    return FALSE;
}

//投递接收请求示例
//CIOCPBuffer *p = AllocateBuffer(BUFFER_SIZE);
//if(p != NULL)
//{
//  if(!PostRecv(pContext, p))
//  {
//      CloseAConnection(pContext);
//  }
//}
//投递发送请求示例
//CIOCPBuffer *p = AllocateBuffer(BUFFER_SIZE);
//if(p != NULL)
//{
//  if(!PostSendToList(pContext, p))
//  {
//      CloseAConnection(pContext);
//  }
//}
void CIOCPServer::OnConnectionEstablished(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
{
    //连接建立,且第一次数据接收完成。
    //接下来可以根据业务逻辑,PostRecv收或者PostSendToList发或者CloseAConnection(pContext)关闭连接
}

void CIOCPServer::OnConnectionClosing(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
{
}

void CIOCPServer::OnReadCompleted(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
{
    //一次数据接收完成。
    //接下来可以根据业务逻辑,PostRecv收或者PostSendToList发或者CloseAConnection(pContext)关闭连接
}

void CIOCPServer::OnWriteCompleted(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
{
    //一次数据发送完成。
    //接下来可以根据业务逻辑,PostRecv收或者PostSendToList发或者CloseAConnection(pContext)关闭连接
}

void CIOCPServer::OnConnectionError(CIOCPContext *pContext, CIOCPBuffer *pBuffer, int nError)
{
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值