友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
聚奇塔 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

软件工程思想-第14部分

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




};
一、客户程序的“发布”协议
客户机向服务器发布的每个数据报均含有数据类型、工作组名称、数据名称、生命期和数据长度的信息。报文格式如图5。16所示,数据结构见 DataPublish :
struct DataPublish
  {
BYTE iDataType;// 2 个字节数据类型,宏定义为DATA_PUBLISH
charstrGroupName'16';// 16个字节的工作组名字
charstrDataName'16';// 16个字节的数据名字
DWORDdwLifeTime;// 4 个字节的数据生命期,以秒为单位
DWORDdwLength;// 4 个字节的数据内容的长度
char*pchContent;// 数据内容
  };

     2字节      16字节          16字节        4字节      4字节

图5。16  用于发布的报文格式

二、客户程序的“订阅”协议
  客户机向服务器订阅数据分两步实现:
(1)先调用函数QueryData向服务器发送一个 DataQuery格式的报文,用于查询要订阅的数据是否存在。
struct DataQuery
  {
  BYTEiDataType;// 2 个字节数据类型,宏定义为DATA_QUERY
char strGroupName'16';// 16个字节的工作组名字
  char strDataName'16'; // 16个字节的数据名字
};
(2)服务器接收到查询时,按照 DataQuery结构中的strGroupName和strDataName进行搜索。如果该数据不存在,Server向Client发送一个FALSE标志。如果该数据存在,服务器先向客户机发送一个TRUE标志,之后立即再向客户机发送该数据(DataPublish格式)。 
  如果客户机得到TRUE标志的查询结果,就调用函数SubscribeData来接收服务器发送过来的数据。

三、客户程序的“组播”协议
客户机先调用函数QueryGroupIP向服务器发送一个GroupAddress格式的报文,用于查询组播地址。服务器返回相应的十进制点分式的IP地址。
struct GroupAddress
  {
  BYTEiDataType;// 2 个字节数据类型,宏定义为GROUP_ADDRESS
char strGroupName'16';// 16个字节的工作组名字
};
  客户机调用函数MulticastMessage向指定的组(根据组播地址)播放消息。组播的数据报结构 DataMulticast定义如下:
  struct DataMulticast
  {
DWORDdwContentType;// 组播的数据报类型,由用户定义
char*pchContent;// 组播的数据报内容,由用户定义
  };
  如果客户机接收到组播的消息,将自动调用函数MessageResponse来响应该消息。MessageResponse是虚函数,它将根据dwContentType信息决定如何处理到来的组播消息,具体功能由用户定义。

5。5。3。2  CNC 服务器的设计
一、数据结构
CNC 服务器的数据结构主要由三部分组成:
(1)一张用于管理组播地址的链表。组播地址由服务器动态生成,客户机可以向服务器查询任意组的组播地址。
(2)一张用于管理线程指针的链表。服务器采用多线程并发处理技术,使客户机获得最快的响应。
(3)每个组都有一张用于管理“发布—订阅”的数据的Hash表。由于同一时刻,系统可能存在多个生产者与消费者,数据的存入、取出速度成为服务器性能的重要指标。Hash表可以提供比链表更快的数据检索速度。Hash表中的数据项结构见DataElement :
struct DataElement
  {
charstrGroupName'16';// 工作组的名称
charstrDataName'16';// 数据的名称
BYTEiStorageType;// 存储类型: STORAGE_FILE 或 STORAGE_MEMORY
ColeDateTimeTimeToDie;// 作废时刻
BOOLbLock;// 锁定标志: TRUE 或 FALSE
DWORDdwLength;// 数据的长度
char*pchContent;// 数据内容
  };

存储类型(iStorageType)的用途:把数据全部保存在内存中将非常消耗服务器的内存资源,在很多情况下是没有必要的。为了提高内存的使用效率,服务器仅把生命期较短或者长度较短的数据保存在内存中(即为STORAGE_MEMORY类型),而把生命期较长或者长度较长的数据保存在文件中(即为STORAGE_FILE类型)。
作废时刻(TimeToDie)的用途:客户机发布的数据均指定了生命期,服务器在接收到数据时即可计算出作废时刻。服务器将定期扫描Hash表,若发现有数据超出作废时刻(并且没有被锁定),即可删除此数据。
锁定标志(bLock)的用途:很多客户机可能同时订阅某个数据,而该数据可能已超出作废时刻即将被删除。为避免冲突,规定只要有客户机订阅数据,就用iLock标志来锁定此数据,直到订阅完成后才消除锁定。

二、多线程并发技术
服务器有一个主线程和多个子线程。主线程负责客户机的入连接请求,然后创建一个子线程来处理这个TCP连接。每个子线程按照CNC API的协议与客户机通讯。由于有多个子线程共享服务器中的数据,多线程对共享资源的同步访问成为实现的难点。CNC 主要采用了关键区、互斥对象等同步手段解决这个问题。

三、Winsock的使用
CNC 1。0运行于Windows 9x/NT系统下,底层的网络通讯程序用Winsock编写。Winsock有两种工作方式:阻塞方式和非阻塞方式。阻塞方式的优点是编程简单,可靠性好。缺点是容易使应用程序阻塞住,不能处理其它事务。非阻塞方式是利用Windows 消息机制实现的。优点是在数据到来的时候,系统向应用程序窗口发送消息,使得应用程序不必总在等待数据,提高了工作效率。缺点是在发送和接收数据时,应用程序并不将事情做完(不阻塞),以至于应用程序要维护复杂的状态机。
鉴于阻塞方式和非阻塞方式各有优缺点,CNC 服务器采用了混合方式。主线程采用非阻塞的消息驱动方式,可以快速响应客户机的入连接。在子线程中,仍采用非阻塞的消息驱动方式接受客户机的请求,只有在响应请求时,采用阻塞的方式一次性地完成数据的发送或接收。

5。5。4 应用示例
图5。17、图5。18是参加协同工作的两个客户程序示例,这两个程序均用Intra3D 2。0 和CNC 1。0开发。图5。17的客户程序向CNC 服务器订阅 。3ds和 。obj格式的多边形模型数据并执行交互式绘制。图5。18的客户程序向CNC 服务器订阅商业统计图形数据并执行交互式绘制。另有一个客户机(数据源)向CNC 服务器发布各种数据,并用组播来通知各个客户机当前发布了什么数据(短消息)。
  Intra3D 2。0和CNC 1。0目前已经可以实用,请与作者联系,免费索取软件。

5。6  小 结

让我们用著名3D游戏软件Quake的设计师Michael Abrash 的话总结本章:“所有真正杰出的设计一旦被设计好,看起来都是那么的简单和显而易见。但是在获得杰出设计的过程中,需要付出令人难以置信的努力。”'Abrash 1998'

图5。17  绘制。3ds和。obj模型的客户程序

图5。18  绘制商业统计图形的客户程序

第六章  C++面向对象程序设计

  六年前,我刚热恋“面向对象”(Object…Oriented)时,一口气记住了近十个定义。六年后,我从几十万行程序中滚爬出来准备写点心得体会时,却无法解释什么是“面向对象”,就象说不清楚什么是数学那样。
  软件工程中的时髦术语“面向对象分析”和“面向对象设计”,通常是针对“需求分析”和“系统设计”环节的。“面向对象”有几大学派,就象如来佛、上帝和真主用各自的方式定义了这个世界,并留下一堆经书来解释这个世界。
  有些学者建议这样找“对象”:分析一个句子的语法,找出名词和动词,名词就是对象,动词则是对象的方法(即函数)。
  当年国民党的文人为了对抗毛泽东的《沁园春·雪》,特意请清朝遗老们写了一些对仗工整的诗,请蒋介石过目。老蒋看了气得大骂:“娘希匹,全都有一股棺材里腐尸的气味。”
  我看了几千页的软件工程资料,终于发现自己有些“弱智”,无法理解“面向对象”的理论,同时醒悟到“编程是硬道理。”
  面向对象程序设计语言很多,如Smalltalk、Ada、Eiffel、Object Pascal、Visual Basic、C++等等。C++语言最讨人喜欢,因为它兼容C语言,并且具备C语言的性能。近几年,一种叫Java的纯面向对象语言红极一时,不少人叫喊着要用Java革C++的命。我认为Java好比是C++的外甥,虽然不是直接遗传的,但也几分象样。外甥在舅舅身上玩耍时洒了一泡尿,俩人不该为此而争吵。
  关于C++程序设计的书藉非常多,本章不讲C++的语法,只讲一些小小的编程道理。如果我能早几年明白这些小道理,就可以大大改善数十万行程序的质量了。

6。1  C++面向对象程序设计的重要概念

  早期革命影片里有这样一个角色,他说:“我是党代表,我代表党,我就是党。”后来他给同志们带来了灾难。
  会用C++的程序员一定懂得面向对象程序设计吗?
  不会用C++的程序员一定不懂得面向对象程序设计吗?
  两者都未必。就象坏蛋入党后未必能成为好人,好人不入党未必变成坏蛋那样。
  我不怕触犯众怒地说句大话:“C++没有高手,C语言才有高手。”在用C和C++编程8年之后,我深深地遗憾自己不是C语言的高手,更遗憾没有人点拨我如何进行面向对象程序设计。我和很多C++程序员一样,在享用到C++语法的好处时便以为自己已经明白了面向对象程序设计。就象挤掉牙膏卖牙膏皮那样,真是暴殄天物呀。
  人们不懂拼音也会讲普通话,如果懂得拼音则会把普通话讲得更好。不懂面向对象程序设计也可以用C++编程,如果懂得面向对象程序设计则会把C++程序编得更好。本节讲述三个非常基础的概念:“类与对象”、“继承与组合”、“虚函数与多态”。理解这些概念,有助于提高程序的质量,特别是提高“可复用性”与“可扩充性”。

6。1。1 类与对象
  对象(Object)是类(Class)的一个实例(Instance)。如果将对象比作房子,那么类就是房子的设计图纸。所以面向对象程序设计的重点是类的设计,而不是对象的设计。
  类可以将数据和函数封装在一起,其中函数表示了类的行为(或称服务)。类提供关键字public、protected和private用于声明哪些数据和函数是公有的、受保护的或者是私有的。这样可以达到信息隐藏的目的,即让类仅仅公开必须要让外界知道的内容,而隐藏其它一切内容(参见5。2。1节“信息隐藏”)。我们不可以滥用类的封装功能,不要把它当成火锅,什么东西都往里扔。
  类的设计是以数据为中心,还是以行为为中心?
  主张“以数据为中心”的那一派人关注类的内部数据结构,他们习惯上将private类型的数据写在前面,而将public类型的函数写在后面,如表8。1(a)所示。
  主张“以行为为中心”的那一派人关注类应该提供什么样的服务和接口,他们习惯上将public类型的函数写在前面,而将private类型的数据写在后面,如表8。1(b)所示。

Class A

  private:
  int    i; 
返回目录 上一页 下一页 回到顶部 0 0
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!