RECV:xmls:stream=’’
RECV:id=’39ABA7D2’
RECV:xmlns=’jabber:client’
RECV:from=’jabber.org’>
SEND:
SEND:
SEND:stpeter
SEND:Gabber
SEND:file881517e9917bb815fed112d811d32b4e4b3aed
SEND:
SEND:
RECV:
(XML for user session goes here)
SEND:
RECV:
为了让服务器建立一个会话,首先必须对用户进行认证。下面的图表展示的就是认证的活动流程:
当客户端连接到主机,并发起一个XML流时,认证流程就开始了。Jabber服务器会立即在’jabber:iq:auth’的名字空间中对’iq’(info/query的简称)类型和’query’子类型的包体进行查询,该名字空间含有对用户的认证信息。认证信息必须包含一个用户名和明文密码(很明显,这是让人沮丧的),一个使用SHA1算法(这个默认的认证是设计为a.k.a的“数字认证”)加密的密码,或者是一些符合零度认证的数据。
一旦认证信息被接收到,XML解释器发送控制命令给Jabber服务器的“传送”组件,该组件将把从客户端未等待认证结果就发送过来的XML进行缓存。主机(通常,但不全是以JSM形式存在)将把认证包传送到Jabber服务器的’xdb’组件。xdb组件(’xdb’即“Xml Data Base”――XML基数据)将把认证包发送给任一注册了该认证包类型的子组件:例如,明文认证包可能通过检查文件系统中的XML文件用于’xbd_file’子组件,而数字认证包通过检查LDAP用于’xdb_ldap’子组件。传送组件不作任何处理将认证包传送给xdb组件,xdb组件将把该认证包发送给合适的子组件。另外,为了提高性能,xdb_ldap组件拥有其独立的线程池,其运作方式与会话管理器中的线程模式类似。
Xdb组件将认证查询的结果返回给主机(同样,通常是JSM)。如果认证失败,服务器将返回错误代码401给客户端而不发起一个会话。如果认证成功,JSM将开启一个会话(如果需要的话将释放XML缓存),所有在线信息,消息,以及iq基本信息在用户会话的上下文中进行来回传递,直到客户端或服务端通过发送一个关闭数据流的标志()终止。
1.6. Jabber Session Manager Jabber会话管理器
下面是Jabber会话管理器的活动流程:
前面提到,Jabber会话管理器组件(简称JSM)处理各种类型的包:消息类型、在线信息类型、查询连接到一个Jabber主机上的发起者或送达者的Jabber用户信息。同时,JSM也处理针对离线用户的数据包。比如,尽管我不在线,你还是通过我的Jabber ID()发了一条消息给我。JSM将对这条消息进行适当处理,很可能一直保存到我再次上线。
JSM通过从XML流中查找“资源”元素(所谓的“资源”是指设备、客户端、我的连接所在的位置;可能是“laptop”、“Gabber”、“home”)来判断用户是否在线。通常,如果一个数据包不包含资源元素,表明该用户不在线。但有时资源元素会因为错误而丢失,因此JSM在肯定用户真的离线后,才发送消息包给“离线”组件,“离线”组件可能(举例而言)会保存该消息或重新找回一个vCard。
如果用户在线,消息、在线信息、iq包不再发送到离线组件,而是由JSM进行处理。实际上,任何一个包只会有一到两个可能的状态:要么它被转发给用户,要么它由用户发出。因此,JSM开启两个监听,一个是“to”,一个是“from”,并将它们路由到Jabber服务器中指定的模块中。一旦指定模块处理完包体,包体将被送回监听程序,以备以后更多模块进行处理,如果所有处理完毕,包体将发送给消息源或消息目的地。
下面这个例子将有助于理解。我收到从发出的一个消息。我在线,因此消息备送达JSM。“to监听”监听到有一个包发给我,于是发出一个请求到已经注册到JSM的模块。第一个响应模块是mod_filter,该模块按用户指定的标准对进来的消息进行排序。在这个例子中(我好像从来没有从我们的朋友foobar那里很重要的批评信息),我配置mod_filter将所有从发送到我的邮箱的消息通过SMTP传输器转寄。我们说mod_filter对消息进行了重新格式化,使得指定接收端现在由smtp.jabber.org取代原来的jabber.org,然后将包体发回给“to监听者”。另一个对已注册组件的呼叫上来,单没有任何回应,因此包体被送到,使得包体直接转寄到我的电子邮箱中。
需要着重指出的是这个过程是重复的,所以许多模块都可以在包体完成发送到或来自用户动作之前对包体进行处理。这使得JSM拥有了极大的弹性和扩展性,因为这样可以在不对JSM原有模块进行任何改动的基础上,很容易地添加新地模块(只需要对服务器地配置文件进行相应修改即可)。
1.7. Threading 线程
Jabber会话管理器通过线程来提高性能。当服务启动时,一定数量地线程被指派到线程池(实际数目由配置文件决定)。当系统其他部分的装载组件反馈消息包给会话管理器时,会话管理器动态地从线程池中取出没有使用的线程,将它们指派给消息端口,这些消息端口正排队等候包体(一个“消息端口”表示支持一个客户连接的数据结构)。如果线程池中没有可用的线程,会话管理器可能(但不是必须)创建一个新的线程,并将它指派给指定的消息端口。下面是这个过程的可视化描述:
1.8. Delivery Logic 传送逻辑
传送组件是服务器的核心,因为它将数据从一个基本组件移动动另一个基本组件。这个级别的数据处理逻辑如下图:
一旦一个包体被传送到一个基本组件(接收、连接、执行、装入),它将被发送到一个子组件,类似jpolld或xdb_ldap进行进一步处理。
一个预处理的例子可能是一个xdb(比如一个数据库连接)需要被处理。一个处理条件可以是JSM中所有有用的路由名字空间的总和。一个传送包体改变的例子可以是消息格式的改变,比如加上传入地址。
1.9. Transports 传输器
虽然一个健壮的、XML基础的消息系统结构是Jabber项目的核心目标,另一个重要的目标是进行消息系统间的协同作业。幸运的是,Jabber项目通过使它的协议完全开放来实现协同作业。同时,Jabber项目通过使用Jabber世界里叫做“传输器”的东东来实现Jabber开放的XML格式与众多非Jabber格式间的通信。
当一个Jabber用户发送消息给一个外部(非Jabber)系统的用户时,消息的传送包括了一个传输器组件的工作。用户的Jabber客户端发送一个消息给Jabber服务器,并指明一个包含外部系统名的Jabber ID(如),而不是发送给外部IM系统上的一个用户。接着Jabber服务器将数据指向指定的传输器应用程序。如果传输器是本地的(在同一台机器上运行),Jabber服务器直接与它进行通信。如果传输器不在本地运行(在另一台机器上),本地服务器发送一个包给远程服务器,该远程服务器将会把包发送给指定的传输器。一旦传输器接收到XML包体,它把信息(或指示)“转变”成另一个IM网络可以识别的本地包,并把这个本地包传送到那个IM网络中。
下面是Jabber传输器工作的高级概览:
实际上,一个传输器实现了一个代理模式。大多数传输器拥有自己的小型会话管理器,这个会话管理器将在线信息、消息、(有时)查询信息进行Jabber XML协议和“外部的”(非Jabber)协议之间的转换。总的来说,当一个用户登陆到Jabber上,传输器就为和这个用户进行通信创建一个线程。
有时,进行Jabber协议的转换是很直接的,例如,当一个外部协议是很好的文档化的(比如IRC协议,即AIM协议的“奥斯卡”版本)。而另外有些时候,对于封闭的或文档的自然协议(如Yahoo! Messenger协议)进行协议转换就非常困难。人们希望IM统一化组织(( ()))能够成功开放一些现在还是封闭的消息协议,或者至少为这些封闭协议的协议转换创立一套开放的协议。
绝大多数传输器都是为了与非Jabber服务进行通信,但也有个别例外。比如,群组聊天传输器,这个传输器使得Jabber用户们可以在一个聊天室里进行聊天,或者以类似IRC界面的方式进行通信。群组传输器保留每一个房间当前所有用户的记录,并发送每条消息给该房间的所有用户,使得一个房间表现得象一个映射服务器。它根据需要创建和销毁房间,如果我象加入一个不存在得房间,传输器将创建该房间,如果我使最后一个离开房间的用户,传输器将在我离开后销毁这个房间。每一个单一的房间通过类似这样的名字进行识别,每一个参与者通过一个对其昵称的唯一描述进行识别。比如,在莎士比亚的《麦克白》中女巫们的“groupchat”可能发生在一个地址为的房间,女巫们通过类似的名字进行识别。下面使一个用户可能看到的:
1.10. Subscriptions 订阅
一个Jabber实体可以订阅其他Jabber实体(如:任何和一个Jabber ID关联的事物)的在线信息,一个订阅本质上是被订阅者同意发送在线状态改变给订阅者。这个信息同时存储在订阅者和被订阅者的名单中。当我通过认证并在服务器上创建一个会话,我的在线信息被存放到Jabber会话管理器中。当我改变我的在线状态时,
包将被服务器处理,服务器在我的名单中进行查询,并将在线信息状态包发送给所有订阅我的在线状态的Jabber实体。订阅包括一下几种类别,这些类别存放在包含实体的名单上:
? to――另一个发送在线状态信息给你的实体
? from――另一个从你这里获得在线状态信息的实体
? both――另一个发送再现信息状态给你,又从你这里获取在线信息状态的实体
? none――即不从你这里获取再现信息状态,又不发送在线信息状态给你的实体
发送在线状态信息的实体并不一定是另一个Jabber用户,它也可以是一个外部的服务,比如一个数据流或一个非Jabber的IM系统。在后面的例子中,非Jabber系统的用户订阅通过一个传输器解决,Jabber用户注册到指定传输器(如:icq.jabber.org),以便将在线状态信息传送给非Jabber系统的用户。一旦Jabber用户成功注册,传输器就需要知道该用户什么时候上线,因此,它发送一个在线状态信息订阅请求给该用户。一个特殊的带有“from”特性的在线状态信息订阅数据包从传输器产生并发送,其中的数据必须可以登录到本地协议。
Jabber服务器包含一个所有用户的订阅信息组成的名单(该名单通常直接存放与文件系统中,尽管这些信息一个可以存放在数据库中)。这个名单被命名为花名册,很像其他IM系统中的“好友列表”。Jabber的花名册存放在服务器上,这样用户就可以自由的从一个地方到另一个地方,从一台计算机到另一台计算机自由的调用它。Jabber服务器根据用户意愿对花名册上的对应订阅关系进行允许、拒绝等操作。花名册还包括一些用户特殊的其它信息,比如用户的昵称,以及用户所属的群组。这些信息可以通过客户端调用适当接口显示花名册时显现出来。
1.11. Jabber IDs Jabber代号
在Jabber里,有许多不同的实体需要进行相互通信。这些实体可以表现为传输器、群组聊天室、或者是单一的Jabber用户。Jabber IDs是内外结合的表示用户身份或路由信息。Jabber IDs的关键特性包括:
? 它们唯一确定进行即时消息和在线信息状态通信的独立对象或实体
? 用户很容易记住它们并在真实世界中进行表达
? 它们很灵活,以至于可以包容其它IM和在线信息状态表。
每一个Jabber ID(或JID)包括一套有序的元素。JIDs由域、节点、数据流格式的资源组成:
[node@]domain[/resource]
Jabber ID 元素有以下定义:
? 域名是第一标识符。它表明实体连接的Jabber服务器。每一个可用的Jabber域名都应拥有一个完整的域名。
? 节点是第二标识符。它表明“用户”本身。所有的节点都对应一个精确的域。不过,节点是可选的,一个精确的域(如conference.jabber.org)是非法Jabber ID。
? 资源是可选的第三标识符。所有资源都属于一个节点。在Jabber中,资源被用来识别属于用户的特殊对象,比如设备或位置。资源是一个单独的用户可以同时拥有几个与同一Jabber服务器的连接;如: vs. .
一个Jabber用户通常通过一个特殊的资源与服务器相连,因此在连接时有一个形式的地址(如)。由于资源时会话性的,用户的地址可以和类似(如)进行通信,就象人们使用和它相同的形式的email一样。
注意虽然在有些情况下,消息可以直接发送到一个精确资源,但总的来说,一个发往消息根据Jabber服务器上的规则进行路由,因为每一个连接实例都有它自己的优先级设定。这样,如果一条消息正好是发送给(即没有指定任一资源),该消息路由到拥有最高优先级的资源,如:。
1.12. Server Dialback 服务器回滚
1.2版的服务器增加了一个成为服务器回滚的功能。这个功能是设计用来服务器欺骗的,这样就为服务器-服务器之间的交互增加了一个额外的安全方法。关于这个功能的详细信息会在这个文档的未来版本中提供。下面网址提供了一些初步的文档:.
1.13. Conclusion 结束语
本文档提供了一个Jabber系统结构的高阶的概述。如果你对本文档有什么疑问,请直接通过以email或Jabber与文档的作者(Peter-Saint-Andre)进行联系。
1.14. Copyright Information 版权信息
This document is copyright 2001 by Peter Saint-Andre.
Permission is granted to copy, distribute and/or modify this document under the terms
of the GNU Free Documentation License (),
Version 1.1 or any later version published by the Free Software Foundation, with no
Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. You may obtain a
copy of the GNU Free Documentation License from the Free Software Foundation by
visiting or by writing to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307