完成端口编程模型介绍

来源:互联网 发布:python 稀疏矩阵 编辑:程序博客网 时间:2024/06/03 18:50
到了公司作VNETOO运行平台服务器的开发,看了一下同行的代码,才感觉以前对完成端口掌握还不到火候。今天我们来谈一谈Win32下的完成端口模型的编程设计。

1。恒珈科技的网上阅卷系统分析
-----------------------------
        以前用完成端口主要是使用从网络上下载的一个类:CompletionPortModel,应该说这个类封装
得很好,也很好使用。但当时对完成端口看得很高深很神秘,所以也就没有大胆的放下手去编写程序了。
在CompletionPortModel的使用中,最重要的可能就是下面三个数据结构了:
//
//自定义枚举数据类型,用来标识套接字IO动作类型
//
typedef enum _IO_OPERATION
{
  IoAccept,  //AcceptEx/accept
  IoRead,    //WSARecv/recv/ReadFile
  IoWrite,   //WSASend/send/WriteFile
  IoEnd
}IO_OPERATION, *PIO_OPERATION;

//
//自定义结构,即"完成键"(单句柄数据)
//
typedef struct _PER_HANDLE_CONTEXT
{
 SOCKET                           IoSocket;

 struct _PER_HANDLE_CONTEXT*       pNext;
}PER_HANDLE_CONTEXT, *PPER_HANDLE_CONTEXT;

//
//单IO数据,扩展的WSAOVERLAPPED
//
typedef struct _PER_IO_CONTEXT
{
 WSAOVERLAPPED              ol;
 char                           szBuffer[PACKET_BUF_SIZE]; //??
 WSABUF                     wsaBuffer;
 SOCKET                     sClient;

 unsigned int               unId;//?????
   
 IO_OPERATION           IoOperation;

 struct _PER_IO_CONTEXT*    pNext;
}PER_IO_CONTEXT, *PPER_IO_CONTEXT;
       当时在作恒珈科技的网上阅卷系统的时候,就是在工作线程和PER_IO_CONTEXT的内存管理上
花了很大的功夫,可是感觉解决方案还不是很好。我当时参考网络游戏服务器的编程设计思想,想把事务处理放在线程池中,但当时由于对每一个用户的请求处理本身必须要串行化,比如用户注销就应该放在试卷提交和试卷请求的处理之后。我当时的处理是简单的为试卷请求,试卷提交分别开了一个线程来处理。
在PER_IO_CONTEXT的内存管理基于采用LookAside链表的管理机制。现在到公司看同行们的代码发现
他们也是采用类似的处理方式,呵呵:) 不过我当时设计的内存管理机制在早期内存分配,内存泄露跟踪
上的处理他们却并没有。我比他们的还先进一点? ^_^


2。一个较完美的完成端口网络解决方案
-----------------------------------
           由于公司的代码保密做得很严,所以到现在该方案中最核心的代码还没看到。只好简单讲讲
接口和编程模型啦!公司研发部把完成端口封装在lib和dll中,并提供了一个头文件来调用。前天我看了
看同事的代码发现了一个我认为很好的完成端口网络解决方案,基于公司的完成端口封装库Vnetsvr.lib
,整个编程模型如下:
   +--------+             +-----------+            +----------+
    | Client   | <====>| 子服务器  | <===> | 中央处理 |
    +--------+              +-----------+            |  服务器  |
                                                                 +----------+
(I). 子服务器的启动分析
     子服务器启动的时候首先读取服务器配置文件,建立对象缓冲池,启动完成端口并创建完成线程。
同时,还会创建一个EVENT对象来实现对子服务器跟中央处理服务器连接的监控,以下我将详细介绍。
然后,子服务器去向中央处理服务器(CCServ)发送服务器注册请求包,子服务器在收到CCServ发送回来的服务器注册请求响应包之后,在指定的端口开始Listen,接受用户的连接和服务请求。

(II).子服务器线程分析
     子线程的线程结构如下:
   ----------------------------------------------------------------
   | 线程                                  说明
   -----------------------------------------------------------------
   +--------+
   | 主线程 |                    主线程在完成子服务器的启动之后便进入
  +--------+            一个状态监控状态。其结构如下:
                        int main()
                        {
                               CPortalServ * pPortalServ = new CPortalServ;
                         pPortalServ->Init();
                         pPortalServ->StartService();
                               
                               while (TRUE)
                         {
                            if (!pPortalServ->ThreadLoop())
                            {
                                pPortalServ->Release();
                                while (!Restart(pPortalServ))
                                 {
                                      Sleep(5000);
                                  }
                             }
                   else
                   {
                       IOCOMP_GetFrameInfo(&lConnectionTotal,
                                                        &lRecvTimesTotal,
                                                        &lSentTimesTotal,
                                                        &lRecvBytes,
                                                        &lSentBytes
                                                        );
                    }

              }

   -----------------------------------------------------------------
   +--------+          通过调用IOCOMP_GetNextPacket不断获取完成端口状态
   |完成线程|       然后QueueUserWorkItem(ThreadPool, (PVOID)pBufferObj,      WT_EXECUTEDEFAULT);     
   +--------+       将对网络包的具体处理丢给系统提供的线程池去完成。QueueUserWorkItem
                    这个API的具体使用可以看看MSDN的相关文档。
   ------------------------------------------------------------------
    +--------+          根据完成端口邦定的私有数据,进行数据包的分发。
   |系统线程|      我们可以很简单的判别是客户端发来的数据包还是中央
   |   池    |      服务器发来的数据包通过设定特殊的私有数据。
   +--------+
   ------------------------------------------------------------------

(III).数据包分发机制
                                                                                            +----....                         
                         DATATYPE_RECV_PACKET                       |
                        +------ ---------------- HandleRecv(...)--->+----.....
                        | DATATYPE_SEND_COMPLETE
  DefMessage-->+----------------------- HandleSent(...)
                        | DATATYPE_ERROR
                        +----------------------- HandleError(...)

(IV).与中央服务器连接的监控
      由于与中央服务器之间的连接至关重要,所以系统设计中一旦发现与中央服务器之间的连接出现
   错误,立即自动重启子服务器。在中央服务器的消息处理中HandleError将设定指定的EVENT为Signal  状态。从而主线程可以立刻知道,然后调用Restart来重新启动子服务器。
     

     Okey!写到这里吧,用我的笔记本写汉字真是受罪。我贴相关的代码上来,有兴趣的兄弟们可以分析一下。
完成端口封装
子服务器代码

 
原创粉丝点击