• 开启服务和停止服务


    Start函数用于开启服务

    1 初始化状态变量

    2 创建监听套接字

    3 加载使用扩展API函数

    4 创建完成端口对象

    5 建立监听套接字和完成端口对象间的关联

    6 为监听套接字注册FD_ACCEPT时间

    7 投递AcceptEx IO不够时可以得到通知后创建监听线程

    BOOL CIOCOPServer::Start(int nPort,int nMaxConnnections,int nMaxFreeBuffers,int nMaxFreeContexts,int nInitialReads)
    {
        //检查服务是否启动
        if(m_bServerStarted)
            return FALSE;
        //保存参数
        m_nPort = nPort;
        m_nMaxConnnections = nMaxConnnections;
        m_nMaxFreeBuffers = nMaxFreeBuffers;
        m_nMaxFreeContexts = nMaxFreeContexts;
        m_nInitialReads = nInitialReads;
        //初始化变量
        m_bServerStarted = TRUE;
        m_bShutDown = FALSE;
        //创建监听套接字,绑定到本地端口, 进入监听模式
        m_sListen = ::WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
        SOCKADDR_IN si;
        si.sin_family = AF_INET;
        si.sin_port = 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_hConnection = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0);
        //加载扩展函数AcceptEx
        GUID GuidAcceptEx = WSAID_ACCEPTEX;
        DWORD dwBytes;
        WSAIotcl(
             m_sListen,
             SIO_GET_EXTENSION_FUNCTION_POINTER,
             &GuidAcceptEx,
             sizeof(GuidAcceptEx),
             &m_lpfnAcceptEx,
             sizeof(m_lpfnAcceptEx),
             &dwBytes,
             NULL,
             NULL
             );
        //加载GetAcceptExSockaddrs
        GUID GuidAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS;
        ::WSAIoctl(m_sListen,
                SIO_GET_EXTENSION_FUNCTION_POINTER,
                &GuiGetAcceptExSockaddrs,
                sizeof(GuidGetAcceptExSockaddrs),
                &m_lpfnAcceptExSockaddrs,
                sizeof(m_lofnAcceptExSockaddrs),
                &dwBytes,
                NULL,
                NULL);
        //将监听套接字关联到完成端口
        ::CreateIoCompletionPort((HANDLE)m_sListen,m_hConnection,(DWORD)0,0);
        //注册FD_ACCEPT事件
        WSAEventSelect(m_sListen,m_hAcceptEvent,FD_ACCEPT);
        //创建监听线程
        m_hListenThread = ::CreateThread(NULL,0,_ListenThreadProc,this,0,NULL);
        return TRUE;
    }

    监听线程_ListenThreadProc主要责任:监听套接字投递AcceptEx IO请求。

    m_hAcceptEvent:当winsock接收到新的连接请求,但是AcceptEx IO,请求来接收这个连接时,就会触发该时间对象。

    m_hRepostEvent:与IO进行交互。

    _ListenThreadProc在下面3中情况下投递Accept请求:

    1 程序初始化,要先投递几个Accept请求,个数由用户指定

    2 处理IO的线程接受到一个客户,使m_hRepostEvent时间受信,_ListenThreadProc线程得到通知后再投递一个Accept请求。

    3 程序运行期间,如果投递的Accept请求不够用,用户的连接请求未能够马上处理,这时候再投递若干个Accept请求。

    DWORD WINAPI CIOCPServer::_ListenThreadProc(LPVOID lpParam)
    {
        CIOCPServer *pThis = (CIOCPServer*)lpParam;
        //在监听套接字上投递几个AcceptIO
        CIOCPBuffer *pBuffer;
        for(int i=0;i<pThis->m_nInitialAccepts;i++)
        {
            pBuffer = pThis->AllocateBuffer(BUFFER_SIZE);
            if(pBuffer==NULL)
                return -1;
            pTHis->InsertPendingAccept(pBuffer);
            pThis->PostAccept(pBuffer);
        }
        //构建事件对象数组
        HANDLE hWaitEvents[2+MAX_THREAD];
        int nEventCount = 0;
        hWaitEvents[nEventCount++]=pThis->m_hAcceptEvent;
        hWaitEvents[nEventCount++]=pThis->m_hRepostEvent;
        //创建指定数量的工作线程在完成端口上处理IO
        for(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);
                //关闭监听套接字
                ::closesocket(pThis->m_sListen);
                pThis->m_sListen=INVALID_SOCKET;
                ::Sleep(0);
                //通知所有IO处理线程退出
                for(int i=2;i<MAX_THREAD+2;i++)
                {
                    ::PostQueuedCompletionStatus(pThis->m_hCompletion,-1,0,NULL);
                }
                //等待IO处理线程退出
                ::WaitForMultipleObjects(MAX_THREAD,&hWaitEvents[2],TRUE,5*1000);
                for(i=2;i<MAX_THREAD+2;i++)
                {
                    ::CloseHandle(hWaitEvents[i]);
                }
                ::CloseHandle(pThis->m_hCompletion);
                pThis->FreeBuffers();
                pThis->FreeContexts();
                ::ExitThread(0);
            }
            //定时检查所有未返回的AcceptEx IO的连接建立多长时间
            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);
                    //如果超过两分钟,就丢弃
                    if(nSeconds!=-1 && nSeconds>=2*60)
                    {
                        closesocket(pBuffer->sClient);
                        pBuffer->sClient = INVALIDE_SOCKET;
                    }
                    pBuffer = pBuffer->pNext;
    
                }
            }
            else
            {
                nIndex = nIndex-WAIT_OBJECT_0;
                WSANETWORKEVENTS ne;
                int nLimit=0;
                if(nIndex==0)//m_hAcceptEvent时间对象受信,说明投递的Accept请求不够,需要增加
                {
                    ::WSAEnumNetworkEvents(pThis->m_sListen,hWaitEvents[nIndex],&ne);
                    if(ne.lNetworkEvents & FD_ACCEPT)
                    {
                        nLimit = 50;
                    }
                }
                else if(nIndex==1)//m_hRepostEvent事件对象受信,说明处理IO的线程接受到新的客户
                {
                    nLimit = InterlockedExchange(&pThis->m_nRepostCount,0);
                }
                else if(nIndex>1)//IO服务线程退出,说明有错误发生,关闭服务器
                {
                    pThis->m_bShutDown = TRUE;
                    continue;
                }
                //投递nLimit个AcceptEx IO 请求
                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;
    }

     

     3 停止服务函数ShutDown

    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;
    }

     

  • 相关阅读:
    dd是___元素
    【电商8】footer mod_service
    外边距塌陷问题
    隐藏display: ____;
    判断一个32位的整数的二进制有几个1
    8 switch case
    键盘录入
    两个整数的交换
    运算符
    java 异常报错总结
  • 原文地址:https://www.cnblogs.com/xing901022/p/2729773.html
Copyright © 2020-2023  润新知