windows完成端口(四)

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

下面给出上文中使用到的对完成端口模型封装的类的全部代码:

/* 
========================================================================== 

Purpose: 

    * 这个类CIOCPModel是本代码的核心类,
      用于说明WinSock服务器端编程模型中的 
      完成端口(IOCP)的使用方法,
      并使用MFC对话框程序来调用这个类实现了基本的 
      服务器网络通信的功能。 

    * 其中的PER_IO_DATA结构体
      是封装了用于每一个重叠操作的参数 
      PER_HANDLE_DATA是封装了用于每一个Socket的参数,
      也就是用于每一个完成端口的参数 

    * 详细的文档说明请参考 http://blog.csdn.net/PiggyXP 

Notes: 

    * 具体讲明了服务器端建立完成端口、建立工作者线程、
      投递Recv请求、投递Accept请求的方法, 
      所有的客户端连入的Socket都需要绑定到IOCP上,
      所有从客户端发来的数据,都会实时显示到主界面中去。 

Author: 

    * PiggyXP【小猪】 

Date: 

    * 2009/10/04 

========================================================================== 
*/  

#pragma once  

// winsock 2 的头文件和库  
#include <winsock2.h>  
#include <MSWSock.h>  
#pragma comment(lib,"ws2_32.lib")  

// 缓冲区长度 (1024*8)  
// 之所以为什么设置8K,也是一个江湖上的经验值  
// 如果确实客户端发来的每组数据都比较少,
//那么就设置得小一些,省内存  
#define MAX_BUFFER_LEN        8192    
// 默认端口  
#define DEFAULT_PORT          12345      
// 默认IP地址  
#define DEFAULT_IP            _T("127.0.0.1")  


//////////////////////////////////////////////////////////////////  
// 在完成端口上投递的I/O操作的类型  
typedef enum _OPERATION_TYPE    
{    
    ACCEPT_POSTED,
    // 标志投递的Accept操作  
    SEND_POSTED,
    // 标志投递的是发送操作  
    RECV_POSTED,
   // 标志投递的是接收操作  
    NULL_POSTED         
   // 用于初始化,无意义  

}OPERATION_TYPE;  

//====================================================================================  
//  
//              单IO数据结构体定义(用于每一个重叠操作的参数)  
//  
//====================================================================================  

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;
   // 标识网络操作的类型(对应上面的枚举)  

    // 初始化  
    _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;  
    }  
    // 释放掉Socket  
    ~_PER_IO_CONTEXT()  
    {  
        if( m_sockAccept!=INVALID_SOCKET )  
        {  
            closesocket(m_sockAccept);  
            m_sockAccept = INVALID_SOCKET;  
        }  
    }  
    // 重置缓冲区内容  
    void ResetBuffer()  
    {  
        ZeroMemory( m_szBuffer,MAX_BUFFER_LEN );  
    }  

} PER_IO_CONTEXT, *PPER_IO_CONTEXT;  


//====================================================================================  
//  
//              单句柄数据结构体定义(用于每一个完成端口,也就是每一个Socket的参数)  
//  
//====================================================================================  

typedef struct _PER_SOCKET_CONTEXT  
{    
    SOCKET      m_Socket;
    // 每一个客户端连接的Socket  
    SOCKADDR_IN m_ClientAddr;
    // 客户端的地址  
    CArray<_PER_IO_CONTEXT*> m_arrayIoContext;
    // 客户端网络操作的上下文数据,  

    // 也就是说对于每一个客户端Socket,是可以在上面同时投递多个IO请求的  

    // 初始化  
    _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.GetCount();i++ )  
        {  
            delete m_arrayIoContext.GetAt(i);  
        }  
        m_arrayIoContext.RemoveAll();  
    }  

    // 获取一个新的IoContext  
    _PER_IO_CONTEXT* GetNewIoContext()  
    {  
        _PER_IO_CONTEXT* p = new _PER_IO_CONTEXT;  

        m_arrayIoContext.Add( p );  

        return p;  
    }  

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

        for( int i=0;i<m_arrayIoContext.GetCount();i++ )  
        {  
            if( pContext==m_arrayIoContext.GetAt(i) )  
            {  
                delete pContext;  
                pContext = NULL;  
                m_arrayIoContext.RemoveAt(i);                 
                break;  
            }  
        }  
    }  

} PER_SOCKET_CONTEXT, *PPER_SOCKET_CONTEXT;  




//====================================================================================  
//  
//              CIOCPModel类定义  
//  
//====================================================================================  

// 工作者线程的线程参数  
class CIOCPModel;  
typedef struct _tagThreadParams_WORKER  
{  
    CIOCPModel* pIOCPModel;
        // 类指针,用于调用类中的函数  
    int         nThreadNo;           
        // 线程编号  

} THREADPARAMS_WORKER,*PTHREADPARAM_WORKER;   

// CIOCPModel类  
class CIOCPModel  
{  
public:  
    CIOCPModel(void);  
    ~CIOCPModel(void);  

public:  

    // 启动服务器  
    bool Start();  

    //  停止服务器  
    void Stop();  

    // 加载Socket库  
    bool LoadSocketLib();  

    // 卸载Socket库,彻底完事  
    void UnloadSocketLib() { WSACleanup(); }  

    // 获得本机的IP地址  
    CString GetLocalIP();  

    // 设置监听端口  
    void SetPort( const int& nPort ) { m_nPort=nPort; }  

    // 设置主界面的指针,用于调用显示信息到界面中  
    void SetMainDlg( CDialog* p ) { m_pMain=p; }  

protected:  

    // 初始化IOCP  
    bool _InitializeIOCP();  

    // 初始化Socket  
    bool _InitializeListenSocket();  

    // 最后释放资源  
    void _DeInitialize();  

    // 投递Accept请求  
    bool _PostAccept( PER_IO_CONTEXT* pAcceptIoContext );   

    // 投递接收数据请求  
    bool _PostRecv( PER_IO_CONTEXT* pIoContext );  

    // 在有客户端连入的时候,进行处理  
    bool _DoAccpet( PER_SOCKET_CONTEXT* pSocketContext,
                    PER_IO_CONTEXT* pIoContext );  

    // 在有接收的数据到达的时候,进行处理  
    bool _DoRecv( PER_SOCKET_CONTEXT* pSocketContext,
                 PER_IO_CONTEXT* pIoContext );  

    // 将客户端的相关信息存储到数组中  
    void _AddToContextList( PER_SOCKET_CONTEXT *pSocketContext );  

    // 将客户端的信息从数组中移除  
    void _RemoveContext( PER_SOCKET_CONTEXT *pSocketContext );  

    // 清空客户端信息  
    void _ClearContextList();  

    // 将句柄绑定到完成端口中  
    bool _AssociateWithIOCP( PER_SOCKET_CONTEXT *pContext);  

    // 处理完成端口上的错误  
    bool HandleError( PER_SOCKET_CONTEXT *pContext,
                      const DWORD& dwErr );  

    // 线程函数,为IOCP请求服务的工作者线程  
    static DWORD WINAPI _WorkerThread(LPVOID lpParam);  

    // 获得本机的处理器数量  
    int _GetNoOfProcessors();  

    // 判断客户端Socket是否已经断开  
    bool _IsSocketAlive(SOCKET s);  

    // 在主界面中显示信息  
    void _ShowMessage( const CString szFormat,...) const;  

private:  

    HANDLE                       m_hShutdownEvent;
        // 用来通知线程系统退出的事件,为了能够更好的退出线程  

    HANDLE                       m_hIOCompletionPort;
        // 完成端口的句柄  

    HANDLE*                      m_phWorkerThreads;
        // 工作者线程的句柄指针  

    int                          m_nThreads;
        // 生成的线程数量  

    CString                      m_strIP;
        // 服务器端的IP地址  
    int                          m_nPort;
        // 服务器端的监听端口  

    CDialog*                     m_pMain;
        // 主界面的界面指针,用于在主界面中显示消息  

    CRITICAL_SECTION             m_csContextList;
        // 用于Worker线程同步的互斥量  

    CArray<PER_SOCKET_CONTEXT*>  m_arrayClientContext;
        // 客户端Socket的Context信息          

    PER_SOCKET_CONTEXT*          m_pListenContext;
        // 用于监听的Socket的Context信息  

    LPFN_ACCEPTEX                m_lpfnAcceptEx;
        // AcceptEx 和 GetAcceptExSockaddrs 的函数指针,用于调用这两个扩展函数  
    LPFN_GETACCEPTEXSOCKADDRS    m_lpfnGetAcceptExSockAddrs;   

};  

由于公众号文章字数有限,您可以接着阅读下一篇:《windows完成端口(五)》 系列目录 windows完成端口(一) windows完成端口(二) windows完成端口(三) windows完成端口(四) windows完成端口(五) windows完成端口(六)

原文发布于微信公众号 - 高性能服务器开发(easyserverdev)

原文发表时间:2018-04-18

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java架构沉思录

你真的懂Mybatis缓存机制吗

Mybatis的一级缓存是指Session缓存。一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。 也就是在同一个SqlSessi...

1.5K5
来自专栏python3

python3--尝试写一个三级菜单

902
来自专栏Coding01

说一说 Laravel 邮件发送流程

我们使用阿里云提供的免费邮,和采用「smtp」驱动,作为测试,参考 .env 配置:

1915
来自专栏遊俠扎彪

如何在命令行中处理CSV文件

CSV,全称Comma-Separated Values。CSV文件是每一行都是以逗号分隔的纯文本文件。

3130
来自专栏张善友的专栏

Contact Manager Web API 示例[3] 分页和查询(Paging and Querying)

联系人管理器web API是一个Asp.net web api示例程序,演示了通过ASP.NET Web API 公开联系信息,并允许您添加和删除联系人,示例地...

2076
来自专栏me的随笔

.NET Core中的包、元包与框架

.NET Core是一个由NuGet包组成的平台。一些产品受益于细粒度包的定义,也有一些受益于粗粒度包的定义。为了适应这种二重性,.NET Core平台被分为一...

1094
来自专栏杨建荣的学习笔记

纠结的paste格式问题(未解决) (r5笔记第26天)

在使用paste命令的时候,会发现输出的结果会有一些问题,比如我们存在一个文件,内容为: ###################################...

3555
来自专栏Seebug漏洞平台

Exim Off-by-one(CVE-2018-6789)漏洞复现分析

前段时间meh又挖了一个Exim的RCE漏洞[1],而且这次RCE的漏洞的约束更少了,就算开启了PIE仍然能被利用。虽然去年我研究过Exim,但是时间过去这么久...

1092
来自专栏林德熙的博客

WPF Process.Start 出现 Win32Exception 异常

如果使用下面的代码启动另一个软件,那么在启动的软件路径不存在时,就会出现异常System.ComponentModel.Win32Exception,没有其他信...

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

关于windows完成端口(IOCP)的一些理解(一)

系列目录 关于windows完成端口(IOCP)的一些理解(一) 关于windows完成端口(IOCP)的一些理解(二) 关于windows完成端口(IOCP)...

8529

扫码关注云+社区

领取腾讯云代金券