作者:赖春红1 戚淮兵1 谭涛2

1.西华师范大学物理与电子信息学院   南充 637002

2.西华师范大学计算机学院               南充 637002

基于IOCP的局域网监控系统

摘 要本文介绍了一种在WINDOWS平台上比较成熟的I/O方法---完成端口,提出了通过使用IOCP机制和旁视列表技术建立网络服务器模型的方法,实现了利用为数不多的线程为成千上万的客户同时提供网络服务,解决了大多数网络服务器连接大量客户端和处理大量数据时存在的问题,获得了极好的性能和强大的扩展能力。文章给出了基于Windows 2000平台的局域网监控系统中网络服务器的设计与实现过程。

关键词 完成端口;旁视列表;网络服务器;VC++
1  引言
2  IOCP机制
3  数据库同步机制实现过程:
4  内存分配机制
5  实现过程
5.1 初始化
5.2 启动服务
5.3完成通知线程内部处理
6 结束语

1  引言

    在局域网远程监控系统中,网络管理中心需要同时监控每个客户端计算机的运行情况,将远程监控画面显示在管理中心的电脑屏幕上。网络服务器要求实现定时截获客户端计算机上的屏幕数据与正在运行程序的数据,定时截取客户端屏幕图像,并将截获的数据和图像保存在数据库中等功能。在较大型局域网中有几百上千个客户端,需要对大量的客户端数据进行处理。因此如何让网络服务器同时为多个客户端服务,但又不丧失整体的性能成为开发的难点。本文采用了I/O完成端口、旁视列表等技术,设计了一种高效的网络服务器,让此问题得到了有效的解决。

2  IOCP机制

    IOCP(I/O Completion Port输入/输出完成端口)是一种能够合理利用与管理多线程的机制。该机制使用完成端口,用一定数量的线程处理重叠I/O(Overlapped I/O)的技术,帮助处理大量客户端请求的网络服务问题,特别适合于开发网络服务器一类的应用程序,并可使系统的性能达到较佳状态。IOCP模型图如图1所示。

图1 完成端口工作模式
    完成端口模式要求创建一个Win32完成端口对象来对重叠I/O请求进行管理,并通过创建一定数量的工作者线程(WorkThread),来为已经完成的重叠I/O请求提供服务。其实,可以把完成端口看成系统维护的一个队列,操作系统把重叠I/O操作完成的事件通知放入该队列,由于是“操作完成”的事件通知,故取名为“完成端口”。一个完成端口被创建以后,可以和多个文件句柄进行关联(文件句柄可以是真正的文件句柄,也可以是Socket句柄或命名管道),并在关联后的句柄上进行重叠I/O操作。当I/O操作完成后,一个重叠I/O完成的事件通知就会被排在此端口的完成队列上,此时,某个工作者线程将会被唤醒来为完成端口服务,执行特定的处理工作。一般来说,一个应用程序可以创建多个工作者线程来处理完成端口上的通知事件,工作者线程的数量依赖于程序的具体需要。 

3  数据库同步机制实现过程:(系统结构图如图2)

图2 系统结构图
    (1)整体系统安装初始,各个同步端的数据库初始化为一致。
    (2)数据服务器维护各个同步端的SQL队列,不断检查是否有操作项,然后将各项操作依序发送至各个在线的同步端。
    (3)在线同步端收到后,置入SQL队列,将SQL队列按操作发生时间先后排序
    (4)在线同步端将SQL队列中的各项操作依序执行并通知服务器执行的结果。服务器收到后将该项操作从该同步端的SQL队列中删除。
    (5)某一在线同步端执行非读的SQL操作后,向服务器发送该操作,服务器将该操作放入除了此同步端外的其他同步端的SQL队列中,并放入历史SQL文件,然后转到第2步
    (6)有新同步端注册入系统后,将从数据服务器(数据服务器同时建立该同步端的SQL队列)下载历史SQL文件,并执行其中各项操作。
    (7)某同步端选择注销,数据服务器删除该同步端的SQL队列。

4  内存分配机制

    在该系统中内存的分配和释放比较频繁,为了比较高效分配释放数据,采用一种称作Lookaside List(旁视列表)的数据结构。在该系统中用于单句柄数据和重叠操作数据(完成键)的分配和释放。Lookaside List的原理是在分配一块数据空间前,先查看回收链表中是否有数据空间指针,如果有则不需要调用系统的内存分配,直接使用回收链表中的该指针指向的数据空间,并将回收链表中该指针节点移除,当使用完数据空间后,也不必调系统的内存释放,而将该数据空间指针移入回收链表。在进程最终结束后才将回收链表中的所有指针指向的数据空间释放。从而大大减低程序在运行期的频繁内存分配和释放产生的开销。

5  实现过程

5.1 初始化

    读取服务器配置文件,初始化客户端连接链表;建立完成端口,根据CPU个数建立等待和接受完成通知的线程;初始化WinSock接口,建立侦听SOCKET;取扩展函数AcceptEx;关联完成端口到侦听SOCKET;绑定和侦听。

5.2 启动服务

    (1)建立网络事件、设置该网络事件为侦听SOCKET的ACCEPT网络事件,这样当AcceptEx一次性分配的N个预备SOCKET由于满足不断上来的TCP连接而耗尽,并有新的TCP连接请求时,就会触发侦听SOCKET的ACCEPT事件,服务器就会在这个时机调用AcceptEx再次分配N个新的预备SOCKET以满足新的TCP连接请求。在该系统中,N=10。
    (2)分配的N个预备SOCKET
    (3)建立检查和分配线程,该线程作用:等待ACCEPT网络事件以分配新的预备SOCKET;定时检测和挂断未收发任何数据且超时连接的TCP连接,防止DoS(拒绝服务)攻击。

5.3完成通知线程内部处理

while (TRUE) { //等待完成端口的通知 bSuccess = GetQueuedCompletionStatus( pThis->m_hCOP, &dwNumberBytes, (PULONG_PTR )&lpHandleContext, &lpOverlapped, INFINITE ); if (!bSuccess) { pThis->m_strLog.Format("GetQueuedCompletionStatus() 失败:%d", GetLastError()); Log(pThis->m_strLog); PPER_IO_CONTEXT lpPerIoContext = (PPER _IO_CONTEXT)lpOverlapped; lpPerIoContext->pNext = NULL; pThis->InsertToLookaside(lpPerIoContext, NULL); lpHandleContext->pNext = NULL; pThis->InsertToLookaside(NULL, lpHandleContext); continue; } //如果外部发送空的单句柄数据指针,则退出完成通知线程 if (NULL == lpHandleContext) { return 0; } //将完成键转换为自己格式的数据指针 PPER_IO_CONTEXT lpPerIoContext = (PPER_IO_ CONTEXT)lpOverlapped; //这种情况表示客户端自己挂断连接 if(IoAccept != lpPerIoContext->IoOperation) { if(0 == dwNumberBytes) { shutdown(lpPerIoContext->sClient, SD_BOTH); closesocket(lpPerIoContext->sClient); lpPerIoContext->pNext = NULL; pThis->InsertToLookaside(lpPerIoCon text, NULL); lpHandleContext->pNext = NULL; pThis->InsertToLookaside(NULL, lpHandleContext); pThis->DecreaseClientNum(); continue; } } HANDLE hResult; PPER_HANDLE_CONTEXT lpNewperHandleContext; //判断该通知为哪种类型的操作的结果 switch(lpPerIoContext->IoOperation) { //接受新的TCP连接 case IoAccept: pThis->IncreaseClientNum(); EnterCriticalSection(&pThis->m_ ListCriSection); pThis->ReleaseConnectionNode (lpPerIoContext); LeaveCriticalSection(&pThis-> m_ListCriSection); //将侦听SOCKET的属性复制给客户端SOCKET, 因为accept 创建的 socket 会自动继承侦听 socket 的属性,而AcceptEx不会 nResult = setsockopt( lpPerIoContext->sClient, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char *)&pThis->m_ListenSocket, sizeof(pThis->m_ListenSocket) ); if(SOCKET_ERROR == nResult) { pThis->m_strLog.Format("客户(ID=%d) SO_ UPDATE_ACCEPT_CONTEXT 失败:%d", lpPerIoContext->unId, WSAGetLastError()); Log(pThis->m_strLog); closesocket(lpPerIoContext->sClient); lpPerIoContext->pNext = NULL; pThis->InsertToLookaside(lpPerIoContext, NULL); pThis->DecreaseClientNum(); continue; } //为新的SOCKET分配单句柄数据 lpNewperHandleContext=pThis->GetHandleFrom Lookaside(); if (NULL == lpNewperHandleContext) { lpNewperHandleContext = (PPER_HANDLE_ CONTEXT)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_HANDLE_CONTEXT) ); if (NULL == lpNewperHandleContext) { pThis->m_strLog.Format("HeapAlloc() 失败"); Log(pThis->m_strLog); shutdown(lpPerIoContext->sClient, SD_BOTH); closesocket(lpPerIoContext->sClient); lpPerIoContext->pNext = NULL; pThis->InsertToLookaside(lpPerIoContext, NULL); pThis->DecreaseClientNum(); continue; } } //将新的SOCKET关联到同一完成端口上 lpNewperHandleContext->IoSocket = lpPerIo Context ->sClient; lpNewperHandleContext->pNext = NULL; hResult = CreateIoCompletionPort( (HANDLE)lpPerIoContext->sClient,/ pThis->m_hCOP, (DWORD_PTR)lpNewperHandleContext, 0 ); if (NULL == hResult) { pThis->m_strLog.Format("关联完成端口到客户套接字失败:%d", GetLastError()); Log(pThis->m_strLog); shutdown(lpPerIoContext->sClient, SD_BOTH); closesocket(lpPerIoContext->sClient); lpPerIoContext->pNext = NULL; lpNewperHandleContext->pNext = NULL; pThis->InsertToLookaside(lpPerIoContext, NULL); pThis->InsertToLookaside(NULL, lpNewperHandleContext); pThis->DecreaseClientNum(); continue; } //接收来自CLIENT的数据 if(pThis->HandleData(lpPerIoContext, IO_READ_COMPLETION, dwNumberBytes)) pThis->DataAction(lpPerIoContext, lpNewperHandleContext); continue; //数据接收完的通知 case IoRead: if(pThis->HandleData(lpPerIoContext, IO_READ_COMPLETION, dwNumberBytes)) pThis->DataAction(lpPerIoContext, lpNewperHandleContext); continue; //数据发送完的通知 case IoWrite: if(pThis->HandleData(lpPerIoContext, IO_WRITE_COMPLETION,dwNumberBytes)) pThis->DataAction(lpPerIoContext, lpNewperHandleContext); continue; default: continue; } }      数据处理都放在HandleData中实现,数据处理完后的响应代码都在DataAction中实现,在此不做详细介绍。

6 结束语

    编写网络服务器应用程序的难点在于程序的“可扩展性”,即如何开发出大容量且能处理大量并发SocketI/O请求的高性能代理应用程序。IOCP机制通过完成端口对象来对重叠I/O请求进行管理,并且利用多线程来处理重叠I/O操作完成后得到的数据,是一种与Win32 Sockets结合度较高的实现高效率I/O的有效方法。
    本文设计的网络服务器在局域网监控系统中成功使用。实际应用表明:利用IOCP机制实现的网络服务器应用程序能够针对大量的客户请求进行高效处理,在速度和性能上体现出其良好的特征,不失为一种实现网络服务器的有效技术。

参考文献

[1] 张静华,张玉明.IOCP研究及在大规模网络通信系统中的应用. 计算机与现代化,2004,9
[2] 陈和平,周静宁等.IOCP机制与网络代理服务器实现方法.计算机应用,2003,4
[3] Anthony Jones,Jim Ohlund著.Windows网络编程技术.机械工业出版社,2000,(176-206)
[4] 潘爱民,王国印译.Visual C++技术内幕(第四版).清华大学出版社,1999
[5] 官章全,韩云君等.Visual C++6.0高级编程范例.电子工业出版社,2001

基于IOCP的局域网监控系统相关推荐

  1. Linux下基于qt的视频监控系统

    目录 一.原始需求 二.环境安装 2.1 qt安装 2.2 opencv安装 三.系统设计 3.1. 整体流程设计 3.2 .数据传输交互流程 3.3 .数据库设计 四.关键代码 4.1.如何实现通信 ...

  2. 基于微博的舆情监控系统 JAVA SSM MySQL

    10040_基于微博的舆情监控系统 技术 Spring + SpringMVC + MyBatis 工具 eclipse + tomact + mysql + jdk

  3. 基于智能矿山电力监控系统的设计与应用方法

    摘要:随着煤矿建设的智能化程度越来越高,构建智能电力监控系统实现对矿山生产的有效监控至关重要.首先分析了矿山电力监控系统存在的主要问题,其次重点介绍了基于智能矿山电力监控系统的设计过程,后提出了加强智 ...

  4. (毕业设计资料)基于单片机智能输液器监控系统的设计

    026[毕设课设]基于单片机智能输液器监控系统的设计. 说明:基于单片机智能输液器监控系统的设计,目标滴速和设定滴速的调节通过使用PID算法,换页按钮可调节PID参数, 增加和减少分别在修改按钮按后进 ...

  5. 南瑞rt21系统服务器,南瑞“一种基于CORBA的综合监控系统数据定义和存取方法”等三项科技成果获专利授权...

    南瑞"一种基于CORBA的综合监控系统数据定义和存取方法"等三项科技成果获专利授权 北极星电力信息化网  来源:国网电科院    2012/5/22 9:24:13  我要投稿 北 ...

  6. 基于Java的远程监控系统

    基于Java的远程监控系统 功能点: 1.监控桌面 2.文件上传,下载 3.鼠标.键盘等功能 4.命令行控制 1.桌面 2.文件上传下载 3.鼠标.键盘等功能 略 4.命令行控制 输入命令行 cont ...

  7. 基于ssh社区医疗保健监控系统mysql

    原文链接:请点这里 项目描述 本系统实现了基于ssh社区医疗保健监控系统mysql的基本功能,主要功能如下. 技术支持 eclipse.SSH.Jdk1.8.jsp. mysql 系统提供的具体功能如 ...

  8. 基于java的局域网教学管理系统_基于jsp的局域网教学系统B-JavaEE实现局域网教学系统B - java项目源码...

    基于jsp+servlet+pojo+mysql实现一个javaee/javaweb的局域网教学系统B, 该项目可用各类java课程设计大作业中, 局域网教学系统B的系统架构分为前后台两部分, 最终实 ...

  9. 【基于机器学习的垃圾分类监控系统】

    基于机器学习的垃圾分类监控系统 一.摘要 abstract 正确处理厨余垃圾,可以有效地保护环境,并带来不错的经济效益.本文将机器学习运用到厨余垃圾的分类监控中,以识别混入其中的非厨余垃圾.本文在人工 ...

最新文章

  1. Python创建和访问字典
  2. winform上控件太多,绘制时会逐个出现,通常说双缓冲能解决但实际不能解决的问题的解决方法。
  3. Dalvik/ART(ANDROID)中的多线程机制(2)
  4. 定时器 槽函数没执行_Web服务器项目详解 07 定时器处理非活动连接(上)
  5. java web 润乾报表教程_润乾报表开发 基础教程.ppt
  6. HBase数据模型和读写原理
  7. WebKit 内核浏览器 initKeyboardEvent 函数原型
  8. JavaScript:闭包
  9. 【第三课】Arcgis软件详细介绍
  10. 科技感html页面源码,科技感十足的403html模板动态源码
  11. 英国议会上院AI报告AI in the UK-ready, willing and able附原文183页(赞赏后下载ZIP包)
  12. word的大表格中自动换页
  13. python判断密码是否正确_python密码判断是否符合要求的方法
  14. 怎么在HTML中加入热区,关于网页点击热区图
  15. 关于文案、营销、生活的15条思考!
  16. H264 profile 及帧类型介绍
  17. springSecurity+jwt中实现互踢功能
  18. 恶意驱动锁首手动处理的两种方法
  19. Java计算出生一万天的纪念日期(错误演示,未解决)-----已经解决(原因计算long型数字未加L)
  20. Android微信智能心跳方案 (转)

热门文章

  1. OpenSSL API
  2. 有什么项目工时管理工具?
  3. 考研复试资格审查政审是什么?有什么用?
  4. hfss史密斯图_教你如何在天线设计上使用HFSS仿真软件?
  5. 北斗三号卫星导航信号及接收策略
  6. 最细的 PyCharm 安装+详细使用指南
  7. java编写的获取中国所有城市名称的网络爬虫
  8. 进阶篇|全文干货!工作室游戏代理事前准备“锦集”
  9. Echarts合并多省市地图JSON文件方法
  10. 什么是Activity,详细介绍Activity