




已阅读5页,还剩6页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
ICE实例学习:Lets Chat! 节译时间:2010-02-01 19:42来源: 作者:秩名 点击: 399次注意,对于ICE3.3, PHP和Ruby只提供了客户端的Run Time。我们在考虑当连接的客户如何从服务器获得消息时,必须要想到这一点。对于消息发布,有两种通信模型:推模型和拉模型收藏到: TAG: 服务器编程ICE聊天室节译,原文地址:/articles/index.html需求 1)一个典型的聊天室应用,使用客户器/服务器架构,客户发送消息到中心服务器,然后,消息发送给其它客户。 2)尽量减少服务器管理,甚至可以不需要。 3)通信必须安全,通过公共网络时必须要保护个人隐私。 4)当客户端和服务器有防火墙保护时也能正常运行,客户端不用修改它的网络或者防火墙设置。 5)客户端可以在各种平台上利用多种语言实现,比如说利用Web浏览器作为客户端。 6)客户端可能网络带宽有限,所以应该尽量减少网络流量。 只支持单个聊天室。(多个聊天室只是代码多了点,并没有增加任何难度)。设计 在本文中,将会演示多种客户端的设计和实现。包括: 1)C+命令行客户端; 2)JAVA SWing GUI客户端; 3).NET WPF客户端; 4)PHP网页客户端; 5)Silverlight 网页客户端; 注意,对于ICE3.3, PHP和Ruby只提供了客户端的Run Time。我们在考虑当连接的客户如何从服务器获得消息时,必须要想到这一点。对于消息发布,有两种通信模型: 1)推模型:略 2)拉模型:略 推模型比较简单,容易实现,我们的Chat 客户端中,C+,Java, .Net, Silverlight(0.3)都使用该模型。PHP客户端使用拉模型。推模型定义 每个客户端中提供一个 ChatRoomCallback 类型的ICE对象到服务器。当发生事件时,服务器调用该对象的操作通知客户。SLICE定义如下:/ Slice module Chat / Implemented by clients interface ChatRoomCallback ami void init(Ice:StringSeq users); ami void join(long timestamp, string name); ami void leave(long timestamp, string name); ami void send(long timestamp, string name, string message); ; ; 1)当用户首次连接到聊天室时,服务器调用 init 操作. users参数告诉用户目前连接到聊天室的所有用户信息。 2)有用户连接到聊天室时,服务器调用 join 操作。 3)有用户断开连接时,服务器调用 leave 操作。 4)有用户发送消息时,服务器调用 send 操作。 注意设计使用异步事件。元数据指令 ami 标明服务器异步调用回调操作。当客户端行为异常时,这对服务器是一个保护:客户端可能长时间阻塞,服务器调用期间不会因此失去对线程的控制。与防火墙协作 略. Glacer2是ICE针对这种情况的预建的解决方案,它扮演一个服务器前端。Glacer2具有以下特征: 1)支持会话概念,API支持认证机制,可实现自定义的会话创建和认证。 2)单个Glacer2可进行任意数量的服务器和客户端转发。服务器只要有一个端口接受外来连接,而不用管具体服务器个数。 3)对于具有防火墙的客户端,服务器也可调用其提供的回调。 因为 Glacer2会话概念是面向连接的,只有当客户端同Glacer2的连接打开时,更精确的说,同Glacer2保持一个激活的会话时,服务器才可以对客户进行回调。换句话说,当客户端同Glacer2失去连接,Glacer2自动销毁会话。为了阻止客户端到Glacer2的连接被意外关闭,客户端必须要禁用ACM(Automic Connection Management, 自动连接管理)。而且,Glacer2通常对长时间空闲的会话设置超时。当聊天室长时间没有动作时,为了防止Glacer2销毁会话,客户端必须周期性进行激活,比如,调用 ice_ping, 来对Glacer2的会话超时进行重置。 Chat客户端通过服务器提供的 ChatSession接口来和服务器通信。 ChatSession 从Glacer2:Session派生。/ Slice module Chat exception InvalidMessageException string reason; ; interface ChatSession extends Glacier2:Session void setCallback(ChatRoomCallback* cb); ami long send(string message) throws InvalidMessageException; ; ; 这就是推模型,Chat客户端调用ChatSession的send来发送消息,服务器调用每一个客户的ChatRoomCallback的send操作进行分发。拉模型定义.略服务器实现: 服务器使用C+。注意它的结构:类 ChatRoom 实现了大部分的应用逻辑。为了支持推模型与拉模型,服务器实现了类ChatSession 和类 PollingChatSession。 ChatRoom 调用 ChatRoomCallbackAdapter 对象的 send 函数来传递客户消息,该对象隐藏了两种模型之间的差异。ChatRoom 实现: ChatRoom是一个普通的C+对象,而不是一个Servant./ C+ class ChatRoomCallbackAdapter /* */ ; typedef IceUtil:Handle ChatRoomCallbackAdapterPtr; class ChatRoom : public IceUtil:Shared public: void reserve(const string&); void unreserve(const string&); void join(const string&, const ChatRoomCallbackAdapterPtr&); void leave(const string&); Ice:Long send(const string&, const string&); private: typedef map ChatRoomCallbackMap; ChatRoomCallbackMap _members; set _reserved; IceUtil:Mutex _mutex; ; typedef IceUtil:Handle ChatRoomPtr; 成员_reserverd是一个字符串集合,它存储已经建立回话,但是还没有加入聊天室的客户名。_members存储当前聊天室的所有用户(已经调用过join函数的用户)。 成员函数 reserve 和 unreserve 维护 _reserved 集合。/ C+ void ChatRoom:reserve(const string& name) IceUtil:Mutex:Lock sync(_mutex); if(_reserved.find(name) != _reserved.end() | _members.find(name) != _members.end() throw string(The name + name + is already in use.); _reserved.insert(name); void ChatRoom:unreserve(const string& name) IceUtil:Mutex:Lock sync(_mutex); _reserved.erase(name); join操作添加用户到聊天室。/ C+ void ChatRoom:join(const string& name, const ChatRoomCallbackAdapterPtr& callback) IceUtil:Mutex:Lock sync(_mutex); IceUtil:Int64 timestamp = IceUtil:Time:now().toMilliSeconds(); _reserved.erase(name); Ice:StringSeq names; ChatRoomCallbackMap:const_iterator q; for(q = _members.begin(); q != _members.end(); +q) names.push_back(*q).first); callback-init(names); _membersname = callback; UserJoinedEventPtr e = new UserJoinedEvent(timestamp, name); for(q = _members.begin(); q != _members.end(); +q) q-second-join(e); send实现,同join实现非常类似:/ C+ Ice:Long ChatRoom:send(const string& name, const string& message) IceUtil:Mutex:Lock sync(_mutex); IceUtil:Int64 timestamp = IceUtil:Time:now().toMilliSeconds(); MessageEventPtr e = new MessageEvent(timestamp, name, message); for(ChatRoomCallbackMap:iterator q = _members.begin(); q != _members.end(); +q) q-second-send(e); return timestamp; 类 ChatRoomCallbackAdapter/ C+ class ChatRoomCallbackAdapter : public IceUtil:Shared public: virtual void init(const Ice:StringSeq&) = 0; virtual void join(const UserJoinedEventPtr&) = 0; virtual void leave(const UserLeftEventPtr&) = 0; virtual void send(const MessageEventPtr&) = 0; ;推模式 CallbackAdapter 实现: class SessionCallbackAdapter : public ChatRoomCallbackAdapter public: SessionCallbackAdapter(const ChatRoomCallbackPrx& callback, const ChatSessionPrx& session) : _callback(callback), _session(session) void init(const Ice:StringSeq& users) _callback-init_async(new AMICallback(_session), users); void join(const UserJoinedEventPtr& e) _callback-join_async(new AMICallback(_session), e-timestamp, e-name); void leave(const UserLeftEventPtr& e) _callback-leave_async(new AMICallback(_session), e-timestamp, e-name); void send(const MessageEventPtr& e) _callback-send_async(new AMICallback(_session), e-timestamp, e-name, e-message); private: const ChatRoomCallbackPrx _callback; const ChatSessionPrx _session; ; 看一下SessionCallbackAdapter的四个成员函数,当异步调用完成时,都使用类AMICallback来接收通知。它的定义如下:template class AMICallback : public T public: AMICallback(const ChatSessionPrx& session) : _session(session) virtual void ice_response() virtual void ice_exception(const Ice:Exception&) try _session-destroy(); / Collocated catch(const Ice:LocalException&) private: const ChatSessionPrx _session; ; 当用户回调操作抛出异常,服务器立即销毁客户会话,即把该用户赶出聊天室。这是因为,一旦客户的回调对象出现了一次异常,它以后也就不可能再正常。推模式会话创建: 现在来看一下会话创建。推模式的客户使用Glacier2,所以要使用Glacier2的会话创建机制。Glacier2 允许用户通过提供一个Glacier2:SessionManager对象的代理来自定义会话创建机制。通过设置Glacier2.SessionManager属性来配置Gloacier2,就可以使用自己的会话管理器。会话管理器除了一个trivial构造函数(设置聊天室指针),只有一个操作,create,Glacier2调用它来代理应用的会话创建。 create 操作必须返回一个会话代理(类型为Glacier2:Session*)。实现如下:Glacier2:SessionPrx ChatSessionManagerI:create(const string& name, const Glacier2:SessionControlPrx&, const Ice:Current& c) string vname; try vname = validateName(name); _chatRoom-reserve(vname); catch(const string& reason) throw CannotCreateSessionException(reason); Glacier2:SessionPrx proxy; try ChatSessionIPtr session = new ChatSessionI(_chatRoom, vname); proxy = SessionPrx:uncheckedCast(c.adapter-addWithUUID(session); Ice:IdentitySeq ids; ids.push_back(proxy-ice_getIdentity(); sessionControl-identities()-add(ids); catch(const Ice:LocalException&) if(proxy) proxy-destroy(); throw CannotCreateSessionException(Internal server error); return proxy; 首先调用一个简单的帮助函数 validateName, 来检查传递的用户名是否包含非法字符,并把它转为大写,然后调用 reserver函数把它加到聊天室的_reserved集合中。我们要监视这些操作抛出的消息,并把它转化为Glacide2:CannotCreateSessionException异常,即在create操作的异常规范声明的异常。 接着实例化一个ChatSessionI对象(见下面)来创建会话。注意这个会话使用UUID作为对象标识,所以保证标识符唯一。 最后,添加这个新创建的会话标识,Gllacier2只通过它来转发经过这个会话的请求。实际上,“只转发经过这个会话的并且只到这个会话的请求”,这是一种安全的办法:如果有恶意客户能猜出另一个客户会话的标识,它也不能向别的对象发送请求(可能在除了聊天服务器之外的服务器上)。如果出错,就销毁刚创建的会话对象,这样避免了资源泄露。 这就是利用Glacier2创建会话的全部。如果你希望使用Glacier2的认证机制,可以设置属性Glacier2.PermissionsVerifier为执行认证的对象代理。(Glacier2提供一个内置的权限验证器,NullPermissionsVerifier,可以检查用户名和密码)。 图:会话创建交互图(略) ChatSessionI类实现了ChatSession接口。class ChatSessionI : public ChatSession public: ChatSessionI(const ChatRoomPtr&, const string&); virtual void setCallback(const ChatRoomCallbackPrx&, const Ice:Current&); virtual Ice:Long send(const string&, const Ice:Current&); virtual void destroy(const Ice:Current&); private: const ChatRoomPtr _chatRoom; const string _name; ChatRoomCallbackAdapterPtr _callback; bool _destroy; IceUtil:Mutex _mutex; ; typedef IceUtil:Handle ChatSessionIPtr; 构造函数设置聊天室和用户名,并把_destroy设置为False. 由于Glacier2:create操作不允许传递代理,必须把创建会话和设置回调分成两步。这是setCallback的实现;void ChatSessionI:setCallback(const ChatRoomCallbackPrx& callback, const Ice:Current& c) IceUtil:Mutex:Lock sync(_mutex); if(_destroy) throw Ice:ObjectNotExistException(_FILE_, _LINE_); if(_callback | !callback) return; Ice:Context ctx; ctx_fwd = o; _callback = new SessionCallbackAdapter(callback-ice_context(ctx), ChatSessionPrx:uncheckedCast( c.adapter-createProxy(c.id); _chatRoom-join(_name, _callback); 注意,在使用join传递代理之前,向客户代理添加了一个值为 o 的_fwd上下文。它提示Glacier使用单向调用来转发客户回调。这样比双向调用更加有效。因为所有的回调操
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 留学预备课程与心理辅导合同轻松入门留学生涯
- 离婚房产子女继承权确认及过户服务协议
- 离婚协议中宠物权益保护及抚养责任分配样本
- AGV与仓储管理系统集成方案
- 传媒类院校专业课程改革路径分析与实践
- 新能源行业安全管理现状分析及2025年安全防护技术报告
- 送姜糖水活动方案策划
- 美术建筑课程导入方案设计
- 居然之家简单活动策划方案
- 嘉峪关古式茶楼施工方案
- 抽水蓄能电站项项目立项报告
- 餐饮行业部SOP运营管理手册
- 健康跑活动安全免责协议书
- DB11∕T 2000-2022 建筑工程消防施工质量验收规范
- 护理学科建设
- 1《中国人民站起来了》课堂实录2024-2025学年高中语文选择性必修上册
- 3银行出纳3支票
- 第二单元(教学课件)-【大单元教学】三年级语文上册同步备课系列(统编版)
- 铝加工(深井铸造)企业事故隐患排查清单
- 中国盐业集团有限公司招聘笔试题库2024
- 人教版培智一年级(上)生活语文教案
评论
0/150
提交评论