聊 天
Jabber的群组聊天或会议机制允许多人同时进行交流。
这种多人交流的方式在客户端实现时是比较复杂的,这是大家所公认的,因为有两套聊天协议在使用。群组聊天是最早采用的,而会议机制是新的,也更灵活(注意,现在只有jabber 1.4服务器版本才支持它——做为一个外接模块)。实际上,协议本身仍在不段变化,还没有最终形成标准。
Creating a chat room
在产生一个聊天室前,你需要有一个聊天室名和一个会议服务。服务可以由用户来制定,或者通过发送jabber:iq:browse请求来检索。聊天室名称可以自己输入,或者编程产生(比如,产生一个随机的数字作为名称)。
为确认聊天室名称是否已被使用,可以发送<iq type=”get”>(含xmlns=”jabber:iq:browse”的命名空间)到聊天室,如果它不存在,你会收到error 404(没有找到)错误,反之,如果其已存在,你就得重新为聊天室取个名称。QQ中体现在自建聊天室这个版块。
对于如何生成一个聊天室,有着不同的异议。编程者的实践经验是先发送presence到聊天室,如果已存在就加入它,没有则发送set请求来建立它(发送包含xmlns=”jabber:iq:browse”的<iq type=”set”>命令)
Joining a chat room
需要加入一个聊天室时(它的ID已经由用户指定或在接到聊天邀请时确定),首先发送一个<presence>元素。注意不要添加 resource名在发送中,这是老的groupchat的做法,现在的conference已经不采用了。如果你需要向下兼容性,可以发送 resource name。
接下来,发送包含xmlns=”jabber:iq:browse”的<iq type=”set”>,这个请求包含了一个或多个<nick>元素,它指明了你希望加入的会议的别名。一旦你接到一个成功回应,也就意味着你已经加入这个聊天室。
The chat’s roster
每个聊天室都有个人员列表,表明当前在聊天室中的人员。它会随着人员加入或离开而改变。
通知客户端聊天室人员的方法有很多种。首先,发给每个成员<presence>元素,在你加入这个聊天室或有其他成员改变在线状态时(更新状态、信息或是离开)。
此外,当成员列表改变时,自己会收到一个含有jabber:iq:conference命名空间的<iq>元素,它具体包括代理服务器上的 jabber ID以及当前成员的昵称。描述conference本身的是包含多个属性的<conference>元素,如果包含< user>子元素,则标识了当前的成员们,这时通常带有”jid” 属性和”name”属性。
又如,当一个成员加入、离开或是改变其昵称时,你就会收到一个类似的请求,它包含一个单独的<user>元素。
最后,服务器会发送一个类似“某某加入了”或“某某离开了”样式的消息。
随便说一下,如果你希望在聊天室查找某人,可以使用包含jabber:iq:borowse命名空间的<iq>元素来发送他/她的proxy JID。
Chat invitations
聊天邀请使用包含<xmlns=”jabber:x:conference”>的请求来实现。
Sending and receiving messages
要发送一个信息到聊天室,可以发送”type”为”groupchat”的<message>元素到聊天室地址。发送一个私人信息,可以到他们的proxy ID。
收到的聊天信息依靠”groupchat”类型来辨识,可以从”from”地址去处掉resource ID,此时剩下来的就是聊天室ID了。
Jabber还支持IRC的“表情”聊天方式,这使聊天者能做出类似舞台动作的行为。(这个在QQ中是很常见,也很有趣的)客户端通过前缀”/me”来辨识这样的信息,通常在显示前应该插入使用对象的昵称。
比如,可以发送这样的信息”/me 笑眯眯的望着大家”,则客户端就显示为:
Huwell 笑眯眯的望着大家
Leaving the conference
要离开聊天室时,你只需要简单的发送一个<presence type=”unavailable”>元素即可。
File Transfer
Jabber不直接支持文件的传送,而是依靠“带外数据”(out-of-band)即OOB机制通过URL来超链接文件。这种解决方案使得发送者的客户端要么上传文件到一个特定的FTP/HTTP/WebDAV服务器,要么打开另一个端口,运行一个常规服务在上面。这两种方法下URL都会发送给接受者,他们便使用这个超链接来下载文件。注意,后一种机制在发送者隐藏在防火墙或NAT Server后时会失效,接收者不受影响。这种P2P的文件传送功能非常有用。
OOB不是仅为文件转送来设计的,它可用来传送任何URL,比如一个到心爱站点的链接,尽管一个HTML消息也可以支持它。
有两种相似的方法来传送URL,一个就是将<x>嵌套在<message>元素中,并使用jabber:x:oob命名空间 [JPG p.92,JPO 1.6.23];第二种方法就是使用jabber:x:oob命名空间的<iq>元素请求[JPG p.53, JPO 1.6.9]后一种方法允许通过iq回应来确认。
[*}注册新用户
Jabber协议允许客户端登记一个新的用户,而不用通过web界面来或系统管理员来申请(当然,任何服务器都允许这样做)。登记新的帐户有多种方法。
登记时,首先连接到服务器并打开一个<stream>元素,这就好象是正常登录。只是发送的是使用jabber:iq:register命名空间的<iq type=”get”>元素。[JPG p57-62]
如果服务器不允许登记新用户的话,会回应一个错误。
下列资料可以做为编程者设计登记新用户界面的参考:
<key>,这是一个需要随着登记命令发送回服务器的认证字符串。
<instructions>,包含一个呈现给用户的介绍。
<username><nick><password><name><first><last><email>
<addtress><city><state><zip><phone><url> <date><misc><text>,这些都是用户的资料。属于jabber Server1.4所需要的,如果开发出自己服务器版本,就可以自己定义这些用户资料选项了。
当用户一切就绪后,就可以发送包含上述资料的<iq type=”set”>回应了,然后等待服务器的响应。
如果注册新用户成功,你会收到一个包含空的请求的回应。你需要关闭连接,这个连接不能再做为登录的连接重复使用了,你得另外打开一条新的。
如果注册失败了,你将得到错误的回应,如果错误代码是409(冲突),这意味着你注册的用户名是无效的。
Updating registration
如果要更新注册信息(如密码或电子邮件)可以通过发送<iq type=”set”>元素来完成。
Canceling an account
如果你需要终止一个帐号,你先得通过发送一个<iq type=”get”>元素来获得服务器的<key>,然后再发送包含<remove>子元素的<iq type=”set”>来达成。
[*}WellJabber功能模块
开发一个jabber的客户端不是一件简单的事,事实上标准的客户端应该包括:P2P聊天,聊天室,好友列表管理,资源管理,相关资料修改,信息加密处理,MSN网关(或者还包括Yahoo!,AOL等的信息转换),在线搜索,认证控制,离线信息转发,文件互传等。
一个比较成功的jabber客户端是由jabber.com提供的JIM,此外还有很多优秀的jabber客户端,由于jabber的通讯协议是建立在 XML基础上,而且是开放式的,所以任何语言都可以用来编写客户端,最常见的是Delphi,Java,C/C ,Perl,此外还有PHP, Python,JavaScript,甚至是Flash ActionScript都可以拿来编写这样的客户端,我觉得一个开放式的应用模式才是成功的,才会有长足的进步,这就象电子邮件,我们很难想象如果电子邮件的格式被一家所垄断,只能使用一家所编写的邮件程序去接收,那它还会这么普及、全球通用吗?在这方面国内的腾讯公司做的就很不好,显然易见,QQ的代码也是从jabber中剥离出来的,但是QQ却不肯公开它的通讯协议,一心只想做国内即时通信的老大,这会造成两个后果:一个是处在竞争压力小的情况下,它会停止不前,没有进步。
我们看到,现在的QQ客户端同以前的没有什么太大的区别,不过界面更花哨些而已,没有利益的驱动,腾讯现在连Linux,Palm等平台都没有推出,实际上很可悲。另一个是它只能在国内发展,走不出国门,一旦即时通讯的国际标准制定,那它再这么固步自封,就会成为不合格的产品,下场也好不到哪去。所以希望腾讯公司能赶快觉醒,不要垄断这个现在看起来很壮大的行业。QQ虽然很火,但比起Flash,Netscape又如何?这些世界闻名的软件都是公开格式,甚至是代码的,腾讯还不该向这些软件学习吗?当年的Netsacape几乎是独步天下,可还是被IE后来居上了,现在微软又在XP中捆绑了MSN,腾讯再不当心,可真要尴尬了。
WellJabber是使用PHP写的,是典型的B/S结构程序,之所以这样考虑,是想使WellJabber具有跨平台的特性,能够在win32, Linux,Unix,Mac等系统上都顺畅的运行,因为它是利用浏览器做为承载平台的。而且PHP发展到今天(最新的版本是Version 4)已经很强大了,它具有多种函数库,如同C语言一样,甚至融合的比C更体贴,就Jabber来看,PHP拥有必不可少的XML解析函数,还有网络连接函数,以及加密函数(Hash散列,Base_64等),总之使用PHP来写jabber的演示程序的确很方便,但PHP也不是十全十美的,毕竟,通过HTTP端口,很多有用的功能都实现不了,而且调用的不是系统级的函数(如connect),效率有所下降。
本程序只预计包含五个功能模块:用户注册,用户登录,获取好友列表,发送信息,用户注销。
考虑到面向对象的特性,WellJabber程序采用了类的定义,以方便脚本调用,支持类是PHP中很有用的特点,特别是数据库操作,如果能把一般SQL行为用类来封装,那么在修改数据库类型时将会很方便,做到以做少的改动支持最大的兼容性。
WellJabber中的类定义放在jabber.inc中,一共有6个行为,分别是:
jabber->connect (server[, port])
jabber->Login (username, password, resource,server[, port])
jabber->messages (recipient, subject, body, type)
jabber->register(username, password, email, resource, server[, port])
jabber->GetRoster()
jabber->_display_error_message()
在定义好这几个类以后,脚本就可以很方便的实现jabber的通讯功能,而不必重复代码。配合模版的设计,使得PHP版的Jabber更为方便灵活。
根据jabber文档中的描述,WellJabber类中定义了以下的成员变量,其值与特定含义分别是:
name——用户的真实姓名;
email——用户的电子邮件;
password——用户选用的密码;
username——用户登录名称;
resource——用户的Location辨识名;
sid——本次session的唯一标识
server——登录服务器名称;
port——登录服务器端口名称;
error_code——发送错误的代码;
error——发送的错误(描述)
connect——本次连接的文件指针
roster——好友资料数组
首先,看一看内部的出错处理函数:_display_error_message():
这里error的错误描述实际上就是[JPO]的附录所指明的错误描述代码,这是标准的,由服务器发回的。
Jabber类中要处理的错误列表为:
Bad Request
这个表明jabber客户端发送的数据不能为server端理解,通
常是由于数据流不符合jabber协议而引起的。(譬如,jabber客户端发送了一个subscriptioj给自己,或者是发送了一个不含to属性的数据流。
Unauthorized
这个表明客户端的身份请求验证失败,当客户端发出错误的密
码或者是不存在的用户名时会发生这种情况。
Service Unavailable
这个错误主要发生在服务器无法处理客户端的请求时,譬如,
当我们要发送一个消息给离线好友,但接收者的服务器不支持离线信息存储的机制,就会返回这个错误。
Remote Server Timeout
当试图连接一个服务器而超时时就会发生这个错误。比如说一
个不正确的服务器名称被指定时。
Payment Required
这个错误是为未来使用制定的,现在不会发生。
Forbidden
这个错误发生时表明,服务器理解客户端的请求,但拒绝处理
它。现在主要发生在当注册时密码存储错误时。
Not Found
这个主要是在服务器无法找到与这个客户端送来的数据包匹
配的JabberID时发生的。
Not Allowed
本错误主要是当服务器根据该数据包中的JabberID判定本次
Jabber行为无效时产生的,譬如当非管理员用户向服务器发送一个管理员数据命令时。
Registration Required
这个错误现在尚未开始使用。
Internal Server Error
当服务器发生了未知错误时,就会返回这个error,要防止这
种情况发生主要是从客户端入手,要保证发送的数据包的正确性。
Invalid Parameter
无效的参数错误。
在发生上述错误时,_display_error_message()都会做出正确的处理,实在有未知的错误发生时,也会提示与管理员联系。
下面一一分析成员函数,先看用户登录时所用的:
function connect ($server, $port = "5222")
注意这里默认的端口为5222。如果你使用了SSL登录也可以改为5223。函数里首先是验证传递过来的参数的合法性。也就是server,username,password及resource不能为空,否则就报告错误。
接着是与server端的5222端口相连接,这里使用了fsockopen函数,这个函数功能很强大,它与服务器做了一个TCP连接,并且它返回了一个文件指针,可用于其他的文件函数(如fgets、fgetss、fputs、fclose或feof等)。可以说没有它,jabber的功能就实现不了,因为jabber主要是依靠与server的连接,交互数据流来实现的,用其他语言如C/C 可以很方便的调用connect函数(以及之后的 send、receive函数),同样PHP有fsockopen()也很不错。
登录时使用Login ($username, $ password, $resource, $server, $port = "5222"),当jabber->connect()打开连接后,开始向server端发送数据,譬如登录时发送的XML数据包,随后要读入返回的流,这时会产生一个错误,因为使用fread或fgets等PHP文件操作函数,都要求读入一定的量字符数(按照参数),或者是读到行尾或文件尾,但是由服务器返回的数据是一个完整XML流的一部分,没有所谓的行分隔,我们预先也无法知道此次返回多少字节,如果写成fgets($fp, 1024)这样的,就会使该脚本陷入延时,因为fgets行为就是想读入1024个字节或者是到行尾/文件尾,等如果本次数据量小于1024字节,就会陷入这个函数,不能正确返回值。
在查阅了php.net的最新函数后发现,我们可以依靠socket_get_status()函数的unread_bytes特性来间接处理,说实话,这个方法有点勉强,但由于PHP语言的限制,实在没有其他方法来很好的处理它,如果是C/C ,那就很方便了,recv()函数自己知道收回多少数据,再不然配合Peek参数也可以预知本次数据量。
而使用socket_get_status()方法,就要分两步做,首先使用fgets()类型的函数读取一次数据(可以读一个字节),然后再用unread_bytes得知本次未读数据,依据这个准确的字节数,再调用fgets()一次就可以全部读取了。由于要分两步做,所以效率不是很高的。然后拼接两次得到的字符串,就有了本次回应的数据流了。
收到XML数据后就要来解析它,PHP有很强大的XML解析函数,因为它是依靠expat做后台模块的。首先要创建一个XML解析器,就好象与MySql数据库做连接一样,都是准备工作:
xml_parser_create();//使用缺省编码ISO=8859-1
在下面的函数中都要用到这个解析器,然后调用xml_set_element_handler()来设置起始及结束元素的处理,第一个参数就是上面说到的解析器,第二个和第三个是XML特有规定的函数处理格式的名称,主要是:
StartElementHandler(int parser, string name, string attr)
第一个参数也是解析器,第二个用于保存XML元素名称,缺省情况下,它们会以大写形式出现。第三个是数组,用以保存当前元素的属性及对应值。有了它,可以利用PHP特有的each逐个读出来。
我们在StartElementHandler中将本次要用到的元素属性赋值,以便下面的调用判断,如登录中就是要对$jabber_type值是否为result进行判断,如果是表明登录成功,如果不是那就是登录失败了。
接下来是GetRoster()行为,使用它可以获得当前用户的好友列表,我们发送:
<iq type="get"><query xmlns="jabber:iq:roster"/></iq>
给服务器,即索要当前会话用户的好友列表,然后服务器会返回一系列数据流,里面包括了好友的名称,JID(jabber唯一标识,就好象是QQ中的数字号码)以及认证状态,如果还没有通过好友的认证,那subscription属性就会为none,WellJabber中采用了$jabber-> roster成员变量来接收这一系列的值。需要注意的是每次成员函数调用时都使用同一个连接,i.e.$jabber->connect,所以单个行为不要调用fclose来关闭它,可以在类的析构函数中调用。
SenMessage()发送消息给好友,这里比较简单,当获取好友的JID时,发送相应的数据流即可,这里要注意的是,发送人不需要自己填写,在经过服务器处理后,会由服务器来添加“from”属性,这个是为了防止发送垃圾信息,前面已经说过了。
最后是登记新的用户帐号,这里分四步:
首先,要向服务器发送一个连接请求,就如同登录时所发送的一样;
接着,客户端会收到回应的数据流,这里包含了重要的id,是标识本次会话的唯一值;这时,我们要发送本次想注册的用户名,resource名及密码,注意这里的<iq>请求要包含上面得到的id,而且密码应该采用加密的形式,但WellJabber只是一个演示程序,所以采用了明文发送的形式;
最后,服务器返回<iq di=’sesseion id’type=’result’>
代表本次登记注册成功。这样就完成了一个新用户的注册。
然后就可以使用该帐号进行登录了。注意,这里要重新与服务器打开一个连接,原先的连接已经不能用来登录了。
PHP版的WellJabber所拥有的功能已经描述完了。当然,从它来看Jabber工程只能是管中窥豹,Jabber中许多有用的思想和特点它都没有体现,譬如说实时接收、文件交换、邮件转发、聊天室系统甚至是跨平台交流(如mobile)。但由于Jabber开放和易用的特性,我们看到,任何人都可以用自己喜欢的语言去处理jabber、去理解jabber,这么博大包容的特性也许就是它最吸引人的地方,Jabber的前途将无可限量。
转载自:http://www.cnblogs.com/xiaoliepower/articles/1497336.html