开启服务和停止服务

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏黑泽君的专栏

day54_BOS项目_06

第一步:根据提供的 业务受理.pdm 文件生成建表文件 bos_qp.sql 第二步:由于业务受理.pdm 文件中有伪表,所以我们需要修改生成的建表文件,修改如...

982
来自专栏比原链

Derek解读Bytom源码-P2P网络 地址簿

Gitee地址:https://gitee.com/BytomBlockchain/bytom

1143
来自专栏Bug生活2048

.net core下配置、数据库访问等操作实现

.net core下读取配置还是有点麻烦的,本身没有System.Configuration.dll,所以在进行配置前需要自行引用Microsoft.Exten...

872
来自专栏MasiMaro 的技术博文

socket模型处理多个客户端

最近学完了简单的socket编程,发现其实socket的网络编程其实并没有什么难度,只是简单的函数调用,记住客户端与服务端的步骤,写起来基本没有什么问题。 ...

2712
来自专栏Java帮帮-微信公众号-技术文章全总结

Web-第二十一天 Web商城实战一【悟空教程】

public class BaseServlet extends HttpServlet {

2054
来自专栏一只程序汪的自我修养

手把手教你用.NET Core写爬虫

自从上一个项目58HouseSearch从.NET迁移到.NET core之后,磕磕碰碰磨蹭了一个月才正式上线到新版本。

35012
来自专栏高性能服务器开发

windows完成端口(二)

系列目录 windows完成端口(一) windows完成端口(二) windows完成端口(三) windows完成端口(四) windows完成端口(五) ...

41211
来自专栏社区的朋友们

Node 架构从三层到 N 层,实现代码重用和解耦

三层架构通常意义上是将整个业务应用划分为:控制层、业务逻辑层以及数据访问层,三层架构在 Java Web 项目中很常见,那么这种架构能否运用在 Node 项目中...

2.2K2
来自专栏北京马哥教育

Vim自动补全神器:YouCompleteMe

第一次听说这个插件还是在偶然的情况下看到别人的博客,听说了这个插件的大名。本来打算在实训期间来完成安装的,无奈网实在不给力,也就拖到了回家的时候。在开始准备工作...

8096
来自专栏FreeBuf

远程RPC溢出EXP编写实战之MS06-040

0x01 前言 MS06-040算是个比较老的洞了,在当年影响十分之广,基本上Microsoft大部分操作系统都受到了影响,威力不亚于17年爆出的”永恒之蓝”漏...

30410

扫码关注云+社区

领取腾讯云代金券