XMPPFramework
XMPP Framework概述
XMPP Framework是一个开源的,objective-c实现的xmpp应用开发框架,一般用于开发iOS上的聊天应用。采用模块化的设计。框架分为两个部分,核心和扩展。
历史
XMPP framework启动于2008年,一开始是作为RFC3920的一个简单实现。它提供一个小型代理系统,用于接收3种xmpp节(presence, message, iq)。
为了可以容易的把xmpp framework包含进其它工程,代码被编译成一个objective-c framework。然而,因为这个framework只提供了一个最低限度的功能,
想要用它完成某个有用的东西,第3方开发者就得提供很多额外的代码。毕竟,大部分使用xmpp的应用程序都需要一些额外的功能,例如:rosters, capabilities,
或者是其它上百个XEP's中的任何一个。
显然,该项目需要提供更多通用功能的实现。Roster和一些其它的XEP‘s被添加进来。在早期阶段,这些附加功能彼此叠加在一起,整个设计显得很臃肿。此外,
第3方开发者开始提供附加的XEP实现。但是要把他们的实现添加进这个庞大的架构里面,简直是一场恶梦。而且有些额外的功能常常不是开发者想要的。对于
每一个XEP,一般都是只有特定比例的应用程序才需要。可以理解,第3方开发者想要一个简单优雅、灵活的设计(只具备开发者想要的功能),而不是一个庞大
臃肿的设计。
因此,该项目的第二版诞生了,并引入了模块化的设计。该项目被分为核心系统,和一系列增加附加功能的模块。开发者可以只插入他们想要的功能。这样,第3
方开发者想要为该项目做点贡献,就简单多了。开发者也可以通过只包含他们想要的模块来保持精简代码。
正是在这一点上,开发一个通用的objective-c框架的概念消失了。不在采用庞大的架构,但是保留了一个整体的框架。因此,第3方开发者开始只包含他们需要到
的文件,或者是定制他们自己的编译来包含他们想要的文件。此外,许多新的开发者开始在不支持non-apple框架的iPhone apps中使用该项目。最后,单一的框架
也消失了,取而代之的是一份说明如何定制你的xmpp栈的文档。然而该项目的名字定下来了,仍然称之为“XMPP Framework”。
第三版继续遵循模块化来构建,加入了核心线程安全,使之更容易与各个模块并行执行。
介绍
提示:该文档主要解释XMPPFramework的架构。假定你对xmpp协议有所了解。
该框架分为两个部分:
1 xmpp核心
2 扩展部分(roster, XEP's, 可选的支持功能等)
核心部分是xmpp规范的RFC 3920的实现
不要把xmpp等同于“聊天”。XMPP代表“可扩展消息与出席协议”。它是一个多用途的通用协议。实际上目前有很多公司把该框架用于诸如住宅自动化,和医院里给护士发警报。
扩展部分包括roster支持,自动重连,和各个xmpp扩展的实现。
XMPP核心
XMPP Framework的核心文件位于"Core"文件件下面。这些文件包括:
- XMPPStream
- XMPPParser
- XMPPJID
- XMPPElement
- XMPPIQ
- XMPPMessage
- XMPPPresence
- XMPPModule
- XMPPLogging
- XMPPInternal
来使该框架灵活、可扩展、易于做二次开发的。
XMPPParse是XMPPFramework使用的一个内部类,你或许可以猜到它是做什么的。你不需要与它交互。
XMPPJID提供了一个不可变更的JID实现。它支持JID的过滤,以及与多种形式提取JID的各个部分。它遵守NSCopying 协议, 所以JID可以用作NSDictionary的
键。它甚至还遵守NSCoding 协议。
XMPPElement 是3中XMPP元素:XMPPIQ, XMPPMessage & XMPPPresence的基类。XMPPElement扩展了NSXMLElement。
你的NSXML 基础可以派上用场了。
XMPPModule是一个基类,任何可插式扩展应该继承它。如果你正在写你的应用私有代码,你将可能创建你的类,并且注册以收到委托调用。
然而,如果你正在实现标准的XEP,或者你希望你的应用私有扩展是可插式的,那么你就要在XMPPModule之上构建。
XMPPLogging提供了一个非常快的,强大并且灵活的日志框架。
XMPPInternal是与核心和各种高级底层扩展相关的内部模块。
Elements: IQ, Message, & Presence
XMPPElement扩展自NSXMLElement.所以NSXML 的所有接口对于XMPPElement都适用。
- XMPPIQ -> XMPPElement -> NSXMLElement -> NSXMLNode -> NSObject
- XMPPMessage -> XMPPElement -> NSXMLElement -> NSXMLNode -> NSObject
- XMPPPresence -> XMPPElement -> NSXMLElement -> NSXMLNode -> NSObject
[element attributeIntValueForName:@"age"];
XMPPStream 配置
xmpp stream实例的配置可以分为多个部分:
- 配置连接xmpp服务器的方式
- 添加代理
- 添加模块
- 连接
- 验证
配置连接
对于大多数人,只需要一步 - 设置stream的JID属性。例如:
xmppStream.myJID = [XMPPJID jidWithString:@"user@gmail.com"];xmpp stream将根据XMPP RFC推测出其余部分。这涉及到一个 _xmpp-client._tcp.domain的SRV查询( SRV查询 - 查询哪台服务器提供某种服务)。
在上面的例子中,使用了gmail,google服务器可能返回“talk.google.com”这样的东西,然后xmpp stream将连接到该服务器。如果SRV查询失败,
xmpp stream就简单的连接到JID指定的domain。
如果你知道你将要连接的xmpp server没有xmpp SRV记录,你可以通过指定hostName来告诉xmpp stream跳过SRV查询。例如:
xmppStream.myJID = [XMPPJID jidWithString:@"user@myCompany.com"]; xmppStream.hostName = @"myCompany.com";如果你正在使用一个开发xmpp server,hostName也派得上用场。可能服务器是本地网络可见的,或者没有DNS地址等,例如:
xmppStream.myJID = [XMPPJID jidWithString:@"user@dev1.myCompany.com"]; xmppStream.hostName = @"192.168.2.27";另一个可选的属性是hostPort。默认情况下,几乎所有的xmpp server都运行在5222端口,xmpp规范也是这样定的。如果你的xmpp server不是运行在
一个不同的端口,你可以设置hostPort属性。
添加代理
XMPPStream有许多有趣的功能,被设计来使该框架灵活、可扩展、易于做二次开发。其中之一就是MulticastDelegate。
什么是MulticastDelegate?
xmpp frameword需要支持不限数量的扩展。这包括框架附带的官方扩展和许多的其它扩展或者你想要插入到框架中的定制代码。所以传统的代理
模式不适用。XMPP模块和扩展需要被分离到它们各自的类中,然而每一个了类都需要收到代理方法。标准的NSNotification架构也不可行,因为其中
一些代理需要返回变量。
因此MulticastDelegate 允许你使用标准的代理模式插入该框架,同时它允许多个类收到同样的代理通知。优美之处在于,你不需要把所有的xmpp处理代码放
在一个单独的类中,你可以把你的处理分离到多个类中,你看怎么合适洛。
你可以在任何时候把自己添加为XMPPStream的代理,也可以在身后时候删除代理。
[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; ... [xmppStream removeDelegate:self];
添加模块
框架附带了一些扩展,当然,你想写多少扩展就可以写多少。我们不列出所有可用的扩展,这里列举几个作为例子。
- XMPPReconnect - 意外终止时自动连接stream
- XMPPRoster - 提供标准的roster支持
- XMPPRoom -提供多人聊天支持
- XMPPPubSub - 发布与订阅
xmppReconnect = [[XMPPReconnect alloc] init]; // Optional configuration of xmppReconnect could go here. // The defaults are fine for our purposes. [xmppReconnect activate:xmppStream]; // You can also optionally add delegates to the module. [xmppReconnect addDelegate:self delegateQueue:dispatch_get_main_queue()]; // And that's all that is needed. // The module will receive any delegate methods it needs automatically // from the xmpp stream, and will continue to do its thing unless you deactivate it.
连接
准备好之后,就可以开始连接了
NSError *error = nil; if (![xmppStream connect:&error]) { NSLog(@"Oops, I probably forgot something: %@", error); }如果你忘了设置必须的属性,比如myJID,那么connect将放回NO,error消息将通知你这个问题。
在连接的过程中,xmpp客户端和xmpp服务器完成了xmpp握手。在此期间,服务器通知客户端它支持的协议和它要求的协议。
一些服务器可能需要连接时安全的(通过SSL/TSL, startTLS)。如果是这样,xmpp stream将自动采用安全连接。如果你正在
连接一个使用了不合适的X509证书的服务器,你可能需要实现xmppStream: willSecureWithSettings: 改变默认安全设置的代理
方法。
验证
所有的连接握手完成之后,xmppStreamDidConnect: 这个代理方法被调用。这一般是大部分客户端开始认证过程的地方。这很
简单,如下所示:
- (void)xmppStreamDidConnect:(XMPPStream *)sender { [xmppStream authenticateWithPassword:password error:NULL]; }
XMPP日志
xmpp framework日志需要达到如下目标
支持多个日志级别
对文件可配
对终端用户可配
XEPs
XEPs是XMPP的标准扩展
XMPPFramework支持的XEPs包括
- XEP-0009: 两个实体间传输XML-RPC编码的请求和响应
- XEP-0012: 传达与XMPP实体相关的最后活动信息
- XEP-0016: 允许或阻止与网络上其它实体的通信 -- 比如实现黑名单的功能
- XEP-0045: 多人聊天,聊天室
- XEP-0054: vCard-XML格式的规范文档 -- 与名片交换相关
- XEP-0059: 管理大量结果集,比如限定查询返回数量、前翻或后翻等
- XEP-0060: 通用发布-订阅功能
- XEP-0065: 两个实体之间建立带外字节流
- XEP-0066: 两个实体间交换URLs, 比如一个实体通知另一个实体某个文件的HTTP URL
- XEP-0082: 日期和时间的标准化表示
- XEP-0085: 在聊天过程中沟通用户的状态 -- 比如忙碌、离开等
- XEP-0100: 指定了jabber Client与传统IM服务代理网关交互的最佳实践
- XEP-0106: JID转义字符显示机制
- XEP-0115: 广播和动态发现Client, device, 或通用实体的能力
- XEP-0153: 提供了用于交换用户头像信息的vCard-based协议的历史文档
- XEP-0172: 交换用户昵称
- XEP-0174: 无服务器消息 -- 关于两个客户端怎么直接通信
- XEP-0184: 消息发送收据, 消息发送者可以请求消息已经发送到指定接收者的通知
- XEP-0199: 通过XML Stream发送应用程序级的pings
- XEP-0202: 交换实体的本地时间
- XEP-0203: 通知消息已被延迟发送(因为某个用户离线)
- XEP-0223: 定义了通过发布-订阅机制永久存储用户私有数据的最佳实践
- XEP-0224: 如何引起另一个人的注意
- XEP-0297: 转发XML节
- XEP-0308: 定义了一种方法,指明这条消息是上一条的校正