IOCP小猪案例修改

#include   
#include 
#include   
#include 
#include 
#include 
#include   
#include 
using namespace std;
#define _AFXDLL
#pragma comment(lib, "Ws2_32.lib")      // Socket编程需用的动态链接库  
#pragma comment(lib, "Kernel32.lib")    // IOCP需要用到的动态链接库  

//#include 

#define MAX_BUFFER_LEN 2048
#define MAX_POST_ACCEPT              10
// 传递给Worker线程的退出信号
#define EXIT_CODE                    NULL


// 释放指针和句柄资源的宏

// 释放指针宏
#define RELEASE(x)                      {if(x != NULL ){delete x;x=NULL;}}
// 释放句柄宏
#define RELEASE_HANDLE(x)               {if(x != NULL && x!=INVALID_HANDLE_VALUE){ CloseHandle(x);x = NULL;}}
// 释放Socket宏
#define RELEASE_SOCKET(x)               {if(x !=INVALID_SOCKET) { closesocket(x);x=INVALID_SOCKET;}}
// 在完成端口上投递的I/O操作的类型
typedef enum _OPERATION_TYPE
{
    ACCEPT_POSTED,                     // 标志投递的Accept操作
    SEND_POSTED,                       // 标志投递的是发送操作
    RECV_POSTED,                       // 标志投递的是接收操作
    NULL_POSTED                        // 用于初始化,无意义

}OPERATION_TYPE;

CRITICAL_SECTION m_csContextList;
HANDLE  m_hShutdownEvent;
HANDLE  m_hIOCompletionPort;           // 完成端口的句柄
const int m_nThreads = 16;//建立对应的线程数
HANDLE*   m_phWorkerThreads = NULL;             // 工作者线程的句柄指针
LPFN_ACCEPTEX m_lpfnAcceptEx;                // AcceptEx 和 GetAcceptExSockaddrs 的函数指针,用于调用这两个扩展函数
LPFN_GETACCEPTEXSOCKADDRS    m_lpfnGetAcceptExSockAddrs;
typedef struct _tagThreadParams_WORKER
{
    int  nThreadNo;                                    //线程编号
} THREADPARAMS_WORKER, *PTHREADPARAM_WORKER;

THREADPARAMS_WORKER* pThreadParams[m_nThreads] = { NULL };
unsigned __stdcall _WorkerThread(LPVOID  lpParam);

//每次套接字操作(如:AcceptEx, WSARecv, WSASend等)对应的数据结构:OVERLAPPED结构(标识本次操作),关联的套接字,缓冲区,操作类型;
typedef struct _PER_IO_CONTEXT
{
    OVERLAPPED     m_Overlapped;                               // 每一个重叠网络操作的重叠结构(针对每一个Socket的每一个操作,都要有一个)              
    SOCKET         m_sockAccept;                               // 这个网络操作所使用的Socket
    WSABUF         m_wsaBuf;                                   // WSA类型的缓冲区,用于给重叠操作传参数的
    char           m_szBuffer[MAX_BUFFER_LEN];                 // 这个是WSABUF里具体存字符的缓冲区
    OPERATION_TYPE m_OpType;                                   // 标识网络操作的类型(对应上面的枚举)

    DWORD           m_nTotalBytes;  //数据总的字节数
    DWORD           m_nSendBytes;   //已经发送的字节数,如未发送数据则设置为0

    //构造函数
    _PER_IO_CONTEXT()
    {
        ZeroMemory(&m_Overlapped, sizeof(m_Overlapped));
        ZeroMemory(m_szBuffer, MAX_BUFFER_LEN);
        m_sockAccept = INVALID_SOCKET;
        m_wsaBuf.buf = m_szBuffer;
        m_wsaBuf.len = MAX_BUFFER_LEN;
        m_OpType = NULL_POSTED;

        m_nTotalBytes = 0;
        m_nSendBytes = 0;
    }
    //析构函数
    ~_PER_IO_CONTEXT()
    {
        if (m_sockAccept != INVALID_SOCKET)
        {
            closesocket(m_sockAccept);
            m_sockAccept = INVALID_SOCKET;
        }
    }
    //重置缓冲区内容
    void ResetBuffer()
    {
        ZeroMemory(m_szBuffer, MAX_BUFFER_LEN);
        m_wsaBuf.buf = m_szBuffer;
        m_wsaBuf.len = MAX_BUFFER_LEN;
    }

} PER_IO_CONTEXT, *PPER_IO_CONTEXT;

typedef struct _PER_SOCKET_CONTEXT
{
    SOCKET      m_Socket;                                  //连接客户端的socket
    SOCKADDR_IN m_ClientAddr;                              //客户端地址
    vector<_PER_IO_CONTEXT*> m_arrayIoContext;             //套接字操作,本例是WSARecv和WSASend共用一个PER_IO_CONTEXT
    //构造函数
    _PER_SOCKET_CONTEXT()
    {
        m_Socket = INVALID_SOCKET;
        memset(&m_ClientAddr, 0, sizeof(m_ClientAddr));
    }
    //析构函数
    ~_PER_SOCKET_CONTEXT()
    {
        if (m_Socket != INVALID_SOCKET)
        {
            closesocket(m_Socket);
            m_Socket = INVALID_SOCKET;
        }
        // 释放掉所有的IO上下文数据
        for (int i = 0; i < m_arrayIoContext.size(); i++)
        {
            delete m_arrayIoContext.at(i);
        }
        m_arrayIoContext.clear();
    }

    //进行套接字操作时,调用此函数返回PER_IO_CONTEX结构
    _PER_IO_CONTEXT* GetNewIoContext()
    {
        _PER_IO_CONTEXT* p = new _PER_IO_CONTEXT;
        m_arrayIoContext.push_back(p);

        return p;
    }

    // 从数组中移除一个指定的IoContext
    void RemoveContext(_PER_IO_CONTEXT* pContext)
    {


        for (int i = 0; i < m_arrayIoContext.size(); i++)
        {
            if (pContext == m_arrayIoContext.at(i))
            {
                delete pContext;
                pContext = NULL;
                m_arrayIoContext.erase(m_arrayIoContext.begin() + i);
                break;
            }
        }
    }

} PER_SOCKET_CONTEXT, *PPER_SOCKET_CONTEXT;
PER_SOCKET_CONTEXT*  m_pListenContext;  // 用于监听的Socket的Context信息
vectorm_arrayClientContext;          // 客户端Socket的Context信息 
void _DeInitialize();
bool _PostAccept(PER_IO_CONTEXT* pAcceptIoContext);
void _RemoveContext(PER_SOCKET_CONTEXT *pSocketContext);
bool _IsSocketAlive(SOCKET s);
bool _DoAccpet(PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext);
bool _DoFirstRecvWithoutData(PER_IO_CONTEXT* pIoContext);
void _AddToContextList(PER_SOCKET_CONTEXT *pHandleData);
bool PostRecv(PER_IO_CONTEXT* pIoContext);
bool _AssociateWithIOCP(PER_SOCKET_CONTEXT *pContext);
bool PostWrite(PER_IO_CONTEXT* pIoContext);
bool _DoFirstRecvWithData(PER_IO_CONTEXT* pIoContext);
bool _DoRecv(PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext);
bool HandleError(PER_SOCKET_CONTEXT *pContext, const DWORD& dwErr);
void _ClearContextList();
void stop();
int main() {

    WSADATA wsaData;
    int nResult;
    nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    // 错误(一般都不可能出现)
    if (NO_ERROR != nResult)
    {
        cout << "初始化winsock失败" << endl;
        return 0;
    }
    // 初始化线程互斥量
    InitializeCriticalSection(&m_csContextList);

    // 建立系统退出的事件通知
    m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    m_hIOCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

    if (NULL == m_hIOCompletionPort)
    {
        cout << ("建立完成端口失败!错误代码: %d!\n") << endl;
        return false;
    }
    // 为工作者线程初始化句柄
    m_phWorkerThreads = new HANDLE[m_nThreads];
    // 根据计算出来的数量建立工作者线程
    for (int i = 0; i < m_nThreads; i++)
    {
        pThreadParams[i] = new THREADPARAMS_WORKER;
        pThreadParams[i]->nThreadNo = i + 1;
        m_phWorkerThreads[i] = (HANDLE)_beginthreadex(0, 0, _WorkerThread, (pThreadParams[i]), 0, NULL);
    }
    cout << " 建立 _WorkerThread %d 个." << m_nThreads << endl;
    // AcceptEx 和 GetAcceptExSockaddrs 的GUID,用于导出函数指针
    GUID GuidAcceptEx = { 0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92} };
    GUID GuidGetAcceptExSockAddrs = { 0xb5367df2,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92} };

    // 服务器地址信息,用于绑定Socket
    struct sockaddr_in ServerAddress;

    // 生成用于监听的Socket的信息
    m_pListenContext = new PER_SOCKET_CONTEXT;

    // 需要使用重叠IO,必须得使用WSASocket来建立Socket,才可以支持重叠IO操作
    m_pListenContext->m_Socket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
    if (INVALID_SOCKET == m_pListenContext->m_Socket)
    {
        cout << "初始化Socket失败,错误代码: %d.\n" << WSAGetLastError() << endl;
        return false;
    }
    else
    {
        cout << ("WSASocket() 完成.\n") << endl;;
    }

    // 将Listen Socket绑定至完成端口中
    if (NULL == CreateIoCompletionPort((HANDLE)m_pListenContext->m_Socket, m_hIOCompletionPort, (DWORD)m_pListenContext, 0))
    {
        cout << "绑定 Listen Socket至完成端口失败!错误代码: %d/n" << WSAGetLastError() << endl;
        RELEASE_SOCKET(m_pListenContext->m_Socket);
        return false;
    }
    else
    {
        cout << "Listen Socket绑定完成端口 完成.\n" << endl;
    }

    // 填充地址信息
    ZeroMemory((char *)&ServerAddress, sizeof(ServerAddress));
    ServerAddress.sin_family = AF_INET;
    // 这里可以绑定任何可用的IP地址,或者绑定一个指定的IP地址 
    //ServerAddress.sin_addr.s_addr = htonl(INADDR_ANY);                      
    ServerAddress.sin_addr.s_addr = inet_addr("127.0.0.1");
    ServerAddress.sin_port = htons(6000);

    // 绑定地址和端口
    if (SOCKET_ERROR == bind(m_pListenContext->m_Socket, (struct sockaddr *) &ServerAddress, sizeof(ServerAddress)))
    {
        cout << "bind()函数执行错误.\n" << endl;
        return false;
    }
    else
    {
        cout << "bind() 完成.\n" << endl;
    }

    // 开始进行监听
    if (SOCKET_ERROR == listen(m_pListenContext->m_Socket, SOMAXCONN))
    {
        cout << "Listen()函数执行出现错误.\n" << endl;
        return false;
    }
    else
    {
        cout << "Listen() 完成.\n" << endl;
    }

    // 使用AcceptEx函数,因为这个是属于WinSock2规范之外的微软另外提供的扩展函数
    // 所以需要额外获取一下函数的指针,
    // 获取AcceptEx函数指针
    DWORD dwBytes = 0;
    if (SOCKET_ERROR == WSAIoctl(
        m_pListenContext->m_Socket,
        SIO_GET_EXTENSION_FUNCTION_POINTER,
        &GuidAcceptEx,
        sizeof(GuidAcceptEx),
        &m_lpfnAcceptEx,
        sizeof(m_lpfnAcceptEx),
        &dwBytes,
        NULL,
        NULL))
    {
        cout << "WSAIoctl 未能获取AcceptEx函数指针。错误代码: %d\n" << WSAGetLastError() << endl;
        _DeInitialize();
        return false;
    }

    // 获取GetAcceptExSockAddrs函数指针,也是同理
    if (SOCKET_ERROR == WSAIoctl(
        m_pListenContext->m_Socket,
        SIO_GET_EXTENSION_FUNCTION_POINTER,
        &GuidGetAcceptExSockAddrs,
        sizeof(GuidGetAcceptExSockAddrs),
        &m_lpfnGetAcceptExSockAddrs,
        sizeof(m_lpfnGetAcceptExSockAddrs),
        &dwBytes,
        NULL,
        NULL))
    {
        cout << "WSAIoctl 未能获取GuidGetAcceptExSockAddrs函数指针。错误代码: %d\n" << WSAGetLastError() << endl;
        _DeInitialize();
        return false;
    }


    // 为AcceptEx 准备参数,然后投递AcceptEx I/O请求
    //创建10个套接字,投递AcceptEx请求,即共有10个套接字进行accept操作;
    for (int i = 0; i < MAX_POST_ACCEPT; i++)
    {
        // 新建一个IO_CONTEXT
        PER_IO_CONTEXT* pAcceptIoContext = m_pListenContext->GetNewIoContext();

        if (false == _PostAccept(pAcceptIoContext))
        {
            m_pListenContext->RemoveContext(pAcceptIoContext);
            return false;
        }
    }

    cout << "投递 %d 个AcceptEx请求完毕" << MAX_POST_ACCEPT << endl;

    WaitForMultipleObjects(m_nThreads, m_phWorkerThreads, 0, INFINITE);

stop();

    system("pause");
    return 0;
}
unsigned __stdcall _WorkerThread(LPVOID  lpParam)
{
    THREADPARAMS_WORKER* pParam = (THREADPARAMS_WORKER*)lpParam;
    int nThreadNo = (int)pParam->nThreadNo;

    cout << "工作者线程启动,ID: %d." << nThreadNo << endl;

    OVERLAPPED           *pOverlapped = NULL;
    PER_SOCKET_CONTEXT   *pSocketContext = NULL;
    DWORD                dwBytesTransfered = 0;

    //循环处理请求,直到接收到Shutdown信息为止
    while (WAIT_OBJECT_0 != WaitForSingleObject(m_hShutdownEvent, 0))
    {
        BOOL bReturn = GetQueuedCompletionStatus(m_hIOCompletionPort, &dwBytesTransfered, (PULONG_PTR)&pSocketContext, &pOverlapped, INFINITE);

        //接收EXIT_CODE退出标志,则直接退出
        if (EXIT_CODE == (DWORD)pSocketContext)
        {
            break;
        }

        //返回值为0,表示出错
        if (!bReturn)
        {
            DWORD dwErr = GetLastError();

            // 显示一下提示信息
            if (!HandleError(pSocketContext, dwErr))
            {
                break;
            }

            continue;
        }
        else
        {
            // 读取传入的参数
            PER_IO_CONTEXT* pIoContext = CONTAINING_RECORD(pOverlapped, PER_IO_CONTEXT, m_Overlapped);

            // 判断是否有客户端断开了
            if ((0 == dwBytesTransfered) && (RECV_POSTED == pIoContext->m_OpType || SEND_POSTED == pIoContext->m_OpType))
            {
                cout << "客户端 %s:%d 断开连接." << inet_ntoa(pSocketContext->m_ClientAddr.sin_addr) << ntohs(pSocketContext->m_ClientAddr.sin_port) << endl;

                // 释放掉对应的资源
                _RemoveContext(pSocketContext);

                continue;
            }
            else
            {
                switch (pIoContext->m_OpType)
                {
                    // Accept  
                case ACCEPT_POSTED:
                {
                    pIoContext->m_nTotalBytes = dwBytesTransfered;
                    _DoAccpet(pSocketContext, pIoContext);
                }
                break;

                // RECV
                case RECV_POSTED:
                {
                    pIoContext->m_nTotalBytes = dwBytesTransfered;
                    _DoRecv(pSocketContext, pIoContext);
                }
                break;

                case SEND_POSTED:
                {
                    pIoContext->m_nSendBytes += dwBytesTransfered;
                    if (pIoContext->m_nSendBytes < pIoContext->m_nTotalBytes)
                    {
                        //数据未能发送完,继续发送数据
                        pIoContext->m_wsaBuf.buf = pIoContext->m_szBuffer + pIoContext->m_nSendBytes;
                        pIoContext->m_wsaBuf.len = pIoContext->m_nTotalBytes - pIoContext->m_nSendBytes;
                        PostWrite(pIoContext);
                    }
                    else
                    {
                        PostRecv(pIoContext);
                    }
                }
                break;
                default:
                    // 不应该执行到这里
                    cout << "_WorkThread中的 pIoContext->m_OpType 参数异常.\n" << endl;
                    break;
                } //switch
            }//if
        }//if

    }//while

    cout << "工作者线程 %d 号退出.\n" << nThreadNo << endl;

    // 释放线程参数
    RELEASE(lpParam);

    return 0;
}

void _DeInitialize()
{
    // 删除客户端列表的互斥量
    DeleteCriticalSection(&m_csContextList);

    // 关闭系统退出事件句柄
    RELEASE_HANDLE(m_hShutdownEvent);

    // 释放工作者线程句柄指针
    for (int i = 0; i < m_nThreads; i++)
    {
        RELEASE_HANDLE(m_phWorkerThreads[i]);
    }

    RELEASE(m_phWorkerThreads);

    // 关闭IOCP句柄
    RELEASE_HANDLE(m_hIOCompletionPort);

    // 关闭监听Socket
    RELEASE(m_pListenContext);

    cout << "释放资源完毕.\n" << endl;
}
bool _PostAccept(PER_IO_CONTEXT* pAcceptIoContext)
{


    // 准备参数
    DWORD dwBytes = 0;
    pAcceptIoContext->m_OpType = ACCEPT_POSTED;
    WSABUF *p_wbuf = &pAcceptIoContext->m_wsaBuf;
    OVERLAPPED *p_ol = &pAcceptIoContext->m_Overlapped;

    // 为以后新连入的客户端先准备好Socket( 这个是与传统accept最大的区别 ) 
    pAcceptIoContext->m_sockAccept = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
    if (INVALID_SOCKET == pAcceptIoContext->m_sockAccept)
    {
        cout << "创建用于Accept的Socket失败!错误代码: %d" << WSAGetLastError() << endl;
        return false;
    }

    // 投递AcceptEx
    if (FALSE == m_lpfnAcceptEx(m_pListenContext->m_Socket, pAcceptIoContext->m_sockAccept, p_wbuf->buf, p_wbuf->len - ((sizeof(SOCKADDR_IN) + 16) * 2),
        sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwBytes, p_ol))
    {
        if (WSA_IO_PENDING != WSAGetLastError())
        {
            cout << "投递 AcceptEx 请求失败,错误代码: %d" << WSAGetLastError() << endl;
            return false;
        }
    }

    return true;
}
bool HandleError(PER_SOCKET_CONTEXT *pContext, const DWORD& dwErr)
{
    // 如果是超时了,就再继续等吧  
    if (WAIT_TIMEOUT == dwErr)
    {
        // 确认客户端是否还活着...
        if (!_IsSocketAlive(pContext->m_Socket))
        {
            cout << "检测到客户端异常退出!" << endl;
            _RemoveContext(pContext);
            return true;
        }
        else
        {
            cout << "网络操作超时!重试中..." << endl;
            return true;
        }
    }

    // 可能是客户端异常退出了
    else if (ERROR_NETNAME_DELETED == dwErr)
    {
        cout << "检测到客户端异常退出!" << endl;
        _RemoveContext(pContext);
        return true;
    }

    else
    {
        cout << "完成端口操作出现错误,线程退出。错误代码:%d" << dwErr;
        return false;
    }
}
void _RemoveContext(PER_SOCKET_CONTEXT *pSocketContext)
{
    EnterCriticalSection(&m_csContextList);

    for (int i = 0; i < m_arrayClientContext.size(); i++)
    {
        if (pSocketContext == m_arrayClientContext.at(i))
        {
            RELEASE(pSocketContext);
            m_arrayClientContext.erase(m_arrayClientContext.begin() + i);
            break;
        }
    }

    LeaveCriticalSection(&m_csContextList);
}
bool _IsSocketAlive(SOCKET s)
{
    int nByteSent = send(s, "", 0, 0);
    if (-1 == nByteSent)
        return false;
    return true;
}
bool _DoAccpet(PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext)
{

    if (pIoContext->m_nTotalBytes > 0)
    {
        //客户接入时,第一次接收dwIOSize字节数据
        _DoFirstRecvWithData(pIoContext);
    }
    else
    {
        //客户端接入时,没有发送数据,则投递WSARecv请求,接收数据
        _DoFirstRecvWithoutData(pIoContext);

    }

    // 5. 使用完毕之后,把Listen Socket的那个IoContext重置,然后准备投递新的AcceptEx
    pIoContext->ResetBuffer();
    return _PostAccept(pIoContext);
}
bool _DoFirstRecvWithoutData(PER_IO_CONTEXT* pIoContext)
{
    //为新接入的套接字创建PER_SOCKET_CONTEXT结构,并绑定到完成端口
    PER_SOCKET_CONTEXT* pNewSocketContext = new PER_SOCKET_CONTEXT;
    SOCKADDR_IN ClientAddr;
    int Len = sizeof(ClientAddr);

    getpeername(pIoContext->m_sockAccept, (sockaddr*)&ClientAddr, &Len);

    pNewSocketContext->m_Socket = pIoContext->m_sockAccept;
    memcpy(&(pNewSocketContext->m_ClientAddr), &ClientAddr, sizeof(SOCKADDR_IN));

    //将该套接字绑定到完成端口
    if (false == _AssociateWithIOCP(pNewSocketContext))
    {
        RELEASE(pNewSocketContext);
        return false;
    }

    //投递WSARecv请求,接收数据
    PER_IO_CONTEXT* pNewIoContext = pNewSocketContext->GetNewIoContext();

    //此时是AcceptEx未接收到客户端第一次发送的数据,所以这里调用PostRecv,接收来自客户端的数据
    if (false == PostRecv(pNewIoContext))
    {
        pNewSocketContext->RemoveContext(pNewIoContext);
        return false;
    }

    //如果投递成功,那么就把这个有效的客户端信息,加入到ContextList中去(需要统一管理,方便释放资源)
    _AddToContextList(pNewSocketContext);

    return true;
}
void _AddToContextList(PER_SOCKET_CONTEXT *pHandleData)
{
    EnterCriticalSection(&m_csContextList);

    m_arrayClientContext.push_back(pHandleData);

    LeaveCriticalSection(&m_csContextList);
}
bool PostRecv(PER_IO_CONTEXT* pIoContext)
{
    // 初始化变量
    DWORD dwFlags = 0;
    DWORD dwBytes = 0;
    WSABUF *p_wbuf = &pIoContext->m_wsaBuf;
    OVERLAPPED *p_ol = &pIoContext->m_Overlapped;

    pIoContext->ResetBuffer();
    pIoContext->m_OpType = RECV_POSTED;
    pIoContext->m_nSendBytes = 0;
    pIoContext->m_nTotalBytes = 0;

    // 初始化完成后,,投递WSARecv请求
    int nBytesRecv = WSARecv(pIoContext->m_sockAccept, p_wbuf, 1, &dwBytes, &dwFlags, p_ol, NULL);

    // 如果返回值错误,并且错误的代码并非是Pending的话,那就说明这个重叠请求失败了
    if ((SOCKET_ERROR == nBytesRecv) && (WSA_IO_PENDING != WSAGetLastError()))
    {
        cout << "投递第一个WSARecv失败!" << endl;
        return false;
    }

    return true;
}
// 将句柄(Socket)绑定到完成端口中
bool _AssociateWithIOCP(PER_SOCKET_CONTEXT *pContext)
{
    // 将用于和客户端通信的SOCKET绑定到完成端口中
    HANDLE hTemp = CreateIoCompletionPort((HANDLE)pContext->m_Socket, m_hIOCompletionPort, (DWORD)pContext, 0);

    if (NULL == hTemp)
    {
        cout << "执行CreateIoCompletionPort()出现错误.错误代码:%d" << GetLastError() << endl;
        return false;
    }

    return true;
}
bool _DoFirstRecvWithData(PER_IO_CONTEXT* pIoContext)
{
    SOCKADDR_IN* ClientAddr = NULL;
    SOCKADDR_IN* LocalAddr = NULL;
    int remoteLen = sizeof(SOCKADDR_IN), localLen = sizeof(SOCKADDR_IN);

    //1. 首先取得连入客户端的地址信息
    m_lpfnGetAcceptExSockAddrs(pIoContext->m_wsaBuf.buf, pIoContext->m_wsaBuf.len - ((sizeof(SOCKADDR_IN) + 16) * 2),
        sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, (LPSOCKADDR*)&LocalAddr, &localLen, (LPSOCKADDR*)&ClientAddr, &remoteLen);

    //显示客户端信息
    cout << "客户额 %s:%d 信息:%s." << inet_ntoa(ClientAddr->sin_addr) << ntohs(ClientAddr->sin_port) << pIoContext->m_wsaBuf.buf << endl;


    //2.为新接入的套接创建PER_SOCKET_CONTEXT,并将该套接字绑定到完成端口
    PER_SOCKET_CONTEXT* pNewSocketContext = new PER_SOCKET_CONTEXT;
    pNewSocketContext->m_Socket = pIoContext->m_sockAccept;
    memcpy(&(pNewSocketContext->m_ClientAddr), ClientAddr, sizeof(SOCKADDR_IN));

    // 参数设置完毕,将这个Socket和完成端口绑定(这也是一个关键步骤)
    if (false == _AssociateWithIOCP(pNewSocketContext))
    {
        RELEASE(pNewSocketContext);
        return false;
    }

    //3. 继续,建立其下的IoContext,用于在这个Socket上投递第一个Recv数据请求
    PER_IO_CONTEXT* pNewIoContext = pNewSocketContext->GetNewIoContext();
    pNewIoContext->m_OpType = SEND_POSTED;
    pNewIoContext->m_sockAccept = pNewSocketContext->m_Socket;
    pNewIoContext->m_nTotalBytes = pIoContext->m_nTotalBytes;
    pNewIoContext->m_nSendBytes = 0;
    pNewIoContext->m_wsaBuf.len = pIoContext->m_nTotalBytes;
    memcpy(pNewIoContext->m_wsaBuf.buf, pIoContext->m_wsaBuf.buf, pIoContext->m_nTotalBytes);   //复制数据到WSASend函数的参数缓冲区

    //此时是第一次接收数据成功,所以这里投递PostWrite,向客户端发送数据
    if (false == PostWrite(pNewIoContext))
    {
        pNewSocketContext->RemoveContext(pNewIoContext);
        return false;
    }

    //4. 如果投递成功,那么就把这个有效的客户端信息,加入到ContextList中去(需要统一管理,方便释放资源)
    _AddToContextList(pNewSocketContext);

    return true;
}
bool PostWrite(PER_IO_CONTEXT* pIoContext)
{
    // 初始化变量
    DWORD dwFlags = 0;
    DWORD dwSendNumBytes = 0;
    WSABUF *p_wbuf = &pIoContext->m_wsaBuf;
    OVERLAPPED *p_ol = &pIoContext->m_Overlapped;

    pIoContext->m_OpType = SEND_POSTED;

    //投递WSASend请求 -- 需要修改
    int nRet = WSASend(pIoContext->m_sockAccept, &pIoContext->m_wsaBuf, 1, &dwSendNumBytes, dwFlags,
        &pIoContext->m_Overlapped, NULL);

    // 如果返回值错误,并且错误的代码并非是Pending的话,那就说明这个重叠请求失败了
    if ((SOCKET_ERROR == nRet) && (WSA_IO_PENDING != WSAGetLastError()))
    {
        cout << "投递WSASend失败!" << endl;
        return false;
    }
    return true;
}
bool _DoRecv(PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext)
{
    //输出接收的数据
    SOCKADDR_IN* ClientAddr = &pSocketContext->m_ClientAddr;
    cout << "收到  %s:%d 信息:%s" << inet_ntoa(ClientAddr->sin_addr) << ntohs(ClientAddr->sin_port) << pIoContext->m_wsaBuf.buf << endl;

    //发送数据
    pIoContext->m_nSendBytes = 0;
    pIoContext->m_nTotalBytes = pIoContext->m_nTotalBytes;
    pIoContext->m_wsaBuf.len = pIoContext->m_nTotalBytes;
    pIoContext->m_wsaBuf.buf = pIoContext->m_szBuffer;
    return PostWrite(pIoContext);
}
void stop()
{
    if (m_pListenContext != NULL && m_pListenContext->m_Socket != INVALID_SOCKET)
    {
        // 激活关闭消息通知
        SetEvent(m_hShutdownEvent);

        for (int i = 0; i < m_nThreads; i++)
        {
            // 通知所有的完成端口操作退出
            PostQueuedCompletionStatus(m_hIOCompletionPort, 0, (DWORD)EXIT_CODE, NULL);
        }

        // 等待所有的客户端资源退出
        WaitForMultipleObjects(m_nThreads, m_phWorkerThreads, TRUE, INFINITE);

        // 清除客户端列表信息
        _ClearContextList();

        // 释放其他资源
        _DeInitialize();
    }
}
void _ClearContextList()
{
    EnterCriticalSection(&m_csContextList);

    for (int i = 0; i < m_arrayClientContext.size(); i++)
    {
        delete m_arrayClientContext.at(i);
    }

    m_arrayClientContext.clear();

    LeaveCriticalSection(&m_csContextList);
}

你可能感兴趣的:(IOCP小猪案例修改)