IOCP框架之Windows Sockets网络编程

IOCP框架之Windows Sockets网络编程

 

//

// IPv4 Socket address, Internet style

//

 

typedef struct sockaddr_in {

 

#if(_WIN32_WINNT < 0x0600)

    short   sin_family;   

#else //(_WIN32_WINNT < 0x0600)

    ADDRESS_FAMILY sin_family;

#endif //(_WIN32_WINNT < 0x0600)

 

    USHORT sin_port;

    IN_ADDR sin_addr;

    CHAR sin_zero[8];

} SOCKADDR_IN, *PSOCKADDR_IN;

 

//

// Structure used to store most addresses.

//

typedef struct sockaddr {

 

#if (_WIN32_WINNT < 0x0600)

    u_short sa_family;

#else

    ADDRESS_FAMILY sa_family;           // Address family.

#endif //(_WIN32_WINNT < 0x0600)

 

    CHAR sa_data[14];                   // Up to 14 bytes of direct address.

} SOCKADDR, *PSOCKADDR, FAR *LPSOCKADDR;

 

/*

 * 启动服务--

 * 1. 启动接受客户端连接线程-- 接受客户端连接到服务器的请求

 * 2. 启动服务线程-- 处理每个客户端的数据操作

 */

void CServerView::OnServerStart()

{

    int reVal;                      //返回值

    CServerAddrDlg  servAddrDlg;    //服务器地址对话框

    if (IDOK != servAddrDlg.DoModal())

    {

        return;

    }

   

    // 1. 初始化套接字动态库

    WSADATA wsaData;   

    if ((reVal = WSAStartup(0x0202, &wsaData)) != 0)

    {

        AfxMessageBox(_T("初始化套接字动态库错误!"));

        return ;

    }

   

    // 2. 创建套接字

    if ((m_sListen = WSASocket(AF_INET,

                                SOCK_STREAM,

                                0,

                                NULL,

                                0,

                                WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)

    {

        AfxMessageBox(_T("创建套接字错误!"));

        WSACleanup();

        return ;

    }

   

    // 3. 绑定套接字

    SOCKADDR_IN servAddr;//服务器地址

    servAddr.sin_family = AF_INET;

    servAddr.sin_addr.s_addr = htonl(servAddrDlg.m_dwServIP);

    servAddr.sin_port = htons(servAddrDlg.m_shServPort);   

    if (bind(m_sListen, (SOCKADDR*)&servAddr, sizeof(servAddr))

                                            == SOCKET_ERROR)

    {

        AfxMessageBox(_T("绑定套接字错误!"));

        closesocket(m_sListen);

        WSACleanup();

        return ;

    }

   

    // 4. 设置监听套接字(Listen Socket)进入监听状态

    if(listen(m_sListen, SOMAXCONN) == SOCKET_ERROR)

    {

        AfxMessageBox(_T("监听套接字错误!"));

        closesocket(m_sListen);

        WSACleanup();

        return ;

    }

   

    m_bRunning = TRUE;  //服务器运行Flag

   

    // 5. 创建接受客户端连接事件对象

    m_hEvent = WSACreateEvent();

    if ( m_hEvent == WSA_INVALID_EVENT )

    {  

        closesocket(m_sListen);

        WSACleanup();

        return ;

    }

   

    // 6. 为监听套接字注册FD_ACCEPT网络事件

    int nRet = WSAEventSelect(m_sListen,

                                m_hEvent,

                                FD_ACCEPT);

    if ( nRet == SOCKET_ERROR )

    {

        AfxMessageBox(_T("注册网络事件错误!"));

        closesocket(m_sListen);

        WSACleanup();

        return ;

    }

   

    // 7. 创建完成端口

    if ((m_hCompPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)

    {

        AfxMessageBox(_T("创建完成端口失败!"));

        WSACloseEvent(m_hEvent);

        closesocket(m_sListen);

        WSACleanup();

        return ;

    }

   

    // 8. 创建接受客户端请求线程-- Socket IO模型:WSAEventSelect模型

    DWORD dwThreadID;

    m_hThread[0] = CreateThread(NULL,

                                0,

                                AcceptThread,   // 接受客户端连接线程

                                this,

                                0, 

                                &dwThreadID);

    if (NULL == m_hThread[0])

    {

        AfxMessageBox(_T("创建接受客户端线程失败!"));

        WSACloseEvent(m_hEvent);

        closesocket(m_sListen);

        WSACleanup();

        return;

    }

    m_nThreadNum = 1;

   

    // 获取CPU数量,根据CPU数量创建服务线程

    SYSTEM_INFO SystemInfo;

    GetSystemInfo(&SystemInfo);

   

    // 9. 创建服务线程-- Socket IO模型:IOCP模型

    for(int i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++)

    {

        if ((m_hThread[m_nThreadNum++] = CreateThread(NULL,

                                                        0,

                                                        ServiceThread,  // 服务器服务线程

                                                        this,

                                                        0,

                                                        &dwThreadID)) == NULL)

        {

            AfxMessageBox(_T("创建服务线程失败!"));

            WSACloseEvent(m_hEvent);

            closesocket(m_sListen);

            WSACleanup();

            return ;

        }

       

    }

 

    //设置定时器,每分钟发送WM_TIMER消息

    SetTimer(1, 1000 * 60 * 1,NULL);

}

/*

 * 接收客户端连接请求-- Socket IO模型:WSAEventSelect模型

 */

DWORD WINAPI CServerView::AcceptThread( void *pParam )

{

    CServerView *pServView = (CServerView*)pParam;  //主窗口指针

 

    HANDLE      hComPort = pServView->m_hCompPort;  //完成端口

    SOCKET      sListen = pServView->m_sListen;     //监听套接字

    SOCKET      sAccept = INVALID_SOCKET;           //接受套接字

   

    while(pServView->m_bRunning)

    {

        DWORD dwRet;

        dwRet = WSAWaitForMultipleEvents(1,         //等待网络事件

                                        &pServView->m_hEvent,

                                        FALSE,

                                        100,

                                        FALSE);    

        if(!pServView->m_bRunning)                  //服务器停止服务

            break;

       

        if (dwRet == WSA_WAIT_TIMEOUT)              //函数调用超时

            continue;   // 进入下一轮等待

       

        WSANETWORKEVENTS events;                    //查看发生的网络事件

        int nRet = WSAEnumNetworkEvents(pServView->m_sListen,

                                        pServView->m_hEvent,//事件对象被重置

                                        &events);      

        if (nRet == SOCKET_ERROR)

        {

            AfxMessageBox(_T("WSAEnumNetworkEvents函数错误"));

            break;

        }

 

        if ( events.lNetworkEvents & FD_ACCEPT)     //发生FD_ACCEPT网络事件

        {

            if ( events.iErrorCode[FD_ACCEPT_BIT] == 0 && pServView->m_bRunning)

            {

                // 1. 接受客户端请求

                SOCKADDR_IN servAddr;

                int serAddrLen = sizeof(servAddr);    

                if ((sAccept = WSAAccept(sListen,

                                        (SOCKADDR*)&servAddr,

                                        &serAddrLen,

                                        NULL,

                                        0)) == SOCKET_ERROR)

                {

                    AfxMessageBox(_T("WSAAccept函数错误"));

                    break;

                }

 

                // 2. 创建客户端节点

                CClientContext *pClient = new CClientContext(sAccept,pServView);               

               

                if (CreateIoCompletionPort((HANDLE)sAccept, //套接字与完成端口关联起来

                                            hComPort,

                                            (DWORD) pClient,//完成键

                                            0) == NULL)

                {

                    return -1;

                }

               

                // 3. 加入管理客户端链表

                CClientManager *pClientMgr = CClientManager::GetClientManager();

                pClientMgr->AddClient(pClient);

               

                // 4. 发起第一个针对该连接客户端的异步操作-- 等待接收客户端发送的数据

                if(!pClient->AsyncRecvHead())               //接收数据-- 发起异步操作

                {

                    pClientMgr->DeleteClient(pClient);

                }              

            } // end if ( events.iErrorCode[FD_ACCEPT_BIT] == 0 && pServView->m_bRunning)

        } // end if ( events.lNetworkEvents & FD_ACCEPT)   

    } // end while()

 

    //释放资源

    CClientManager *pClientMgr = CClientManager::GetClientManager();

    pClientMgr->DeleteAllClient();

    pClientMgr->ReleaseManager();

 

    // exit

    return 0;

}

 

/*

 * 服务线程-- Socket IO模型:IOCP模型

 */

DWORD WINAPI CServerView::ServiceThread( void *pParam )

{

    CServerView *pServerView = (CServerView*)pParam;//主窗口指针

 

    HANDLE      hComPort = pServerView->m_hCompPort;//完成端口

   

    DWORD           dwIoSize;       //传输字节数

    CClientContext  *pClient;       //客户端指针

    LPOVERLAPPED    lpOverlapped;   //重叠结构指针

    bool            bExit = FALSE;  //服务线程退出

 

    while (!bExit)

    {

        dwIoSize = -1;

        lpOverlapped = NULL;

        pClient = NULL;

 

        // 根据IO完成通知返回结果,进行不同的情况的数据处理

 

        // 1. 等待I/O操作结果

        BOOL bIORet = GetQueuedCompletionStatus(hComPort,

                                                &dwIoSize,

                                                (LPDWORD) &pClient,

                                                &lpOverlapped,

                                                INFINITE);

        // 1.1 失败的操作完成

        if (FALSE == bIORet && NULL != pClient)

        {  

            //客户端断开

            if (CClientContext::DOING == pClient->m_eState

                || CClientContext::LOGIN == pClient->m_eState)

            {

                pClient->m_eState = CClientContext::DISCON;

                pClient->SaveDisConnectState();

            }

            CClientManager *pClientMgr = CClientManager::GetClientManager();

            pClientMgr->DeleteClient(pClient);             

        }

 

        // 1.2 成功的操作完成

        if(bIORet && lpOverlapped && pClient)

        {              

            CClientManager *pClientMgr = CClientManager::GetClientManager();

            // 进行当前客户端完成的IO数据处理-- 客户端管理器负责Client类内函数的派遣

            pClientMgr->ProcessIO(pClient, lpOverlapped, dwIoSize);            

        }  

 

        // 1.3 服务器退出

        if(pClient == NULL&& lpOverlapped ==NULL && !pServerView->m_bRunning)

        {

            bExit = TRUE;

        }      

    }

 

    // exit code = 0

    return 0;

}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值