XMPP(可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测。它在促进服务器之间的准即时操作。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。
XMPP的前身是Jabber,一个开源形式组织产生的网络即时通信协议。
XMPP的地址叫做JabberID(简写为JID),它用来标示XMPP网络中的各个XMPP实体。--(可以理解为在一个用户,都有一个JID--唯一标示)
JID由三部分组成:JID=user@domain/resource,
其中resource可以省略不写。
例如:
room@service:一个用来提供多用户聊天服务的特定的聊天室。这里 “room“ 是聊天室的名字, ”service“ 是多用户聊天服务的主机名。
room@service/nick:加入了聊天室的用户nick的地址。这里 “room“ 是聊天室的名字, ”service“ 是多用户聊天服务的主机名,”nick“ 是用户在聊天室的昵称。
XMPP通信,传递数据是利用 -- 通信原语
XMPP通信原语有3种:presence、iq、message;
编者简记--文章末尾有详细的三种原语格式( 书本关于XMPP的介绍 ):
它主要由三个结点:
1:<presence> -- 出席 结点,用来表明用户的状态,如:online、away、dnd(请勿打扰)等。当改变自己的状态时,就会在stream的上下文中插入一个Presence元素,来表明自身的状态。
还有可以利用它来-- 请求加对方为好友,同意加为好友等。
<presence>的xml格式有:
<presence type="available" /> -- 在线,默认创建type为空时,就是这个,所以可以直接创建一个presence结点
<presence type="unavailable"/> -- 下线
请求添加好友 -- 使用presence节点
xml格式:<presence type="subscribe" to="希望加为好友的JID">
同意添加好友 -- 还是使用presence节点
只不过它的 xml格式:<presence type="subscribed" to="对方的JID">
2:<iq> -- 请求/响应
<iq> -- XML格式有:
获取好友列表 使用iq结点
<ip type="get" id="roster">
<query xmlns="jabber:iq:roster"/>
</iq>
返回结果集 使用iq结点:
<iq>
<query>
<item jid="好友1">
<item jid="好友2">
</query>
</iq>
3:<message> -- 一种基本推送消息方法,它不要求响应。
<message>的xml格式有:
<message to="jid" type="chat">
<body>
<text>
聊天消息
</text>
<image>
图片
</image>
</body>
</message>
如果简单的我们可以直接在body里写入数据
<message to="jid" type="chat">
<body>
聊天信息
</body>
</message>
在iOS客户端,使用XMPP时, 使用第三方库 XMPPFramework ,需要导入系统类库有:libxml2、CFNetWork、SystemConfiguration、Security、libresolv
因为使用到了libxml2,所以 还需要到 Search Path 中的 Header Search Path 添加 /usr/include/libxml2
#import "AZViewController.h"
#import "XMPPFramework.h"
#define HOST @"1000phone.net"
@interface AZViewController ()<XMPPStreamDelegate>
{
XMPPStream *_stream;
}
@end
@implementation AZViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
/*
XMPP 使用第三方库 XMPPFramework ,需要导入系统类库有:libxml2、CFNetWork、SystemConfiguration、Security、libresolv
因为使用到了libxml2,所以 还需要到 Search Path 中的 Header Search Path 添加 /usr/include/libxml2
*/
//01 创建XMPPStream
_stream =[[XMPPStream alloc] init];
//02 设置服务器
[_stream setHostName:HOST];
//03 设置代理 [_stream addDelegate:<#(id)#> delegateQueue:<#(dispatch_queue_t)#>] -- 使用线程,当操作任务不多的时候,我们使用主线程执行协议方法,如果操作任务多,使用子线程执行协议方法,但是刷新UI操作,必须到主线程中执行。遵从 XMPPStreamDelegate 的协议
[_stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//上线
-(void)goOnline
{
// 创建 <presence type="available" /> -- 在线,默认创建type为空时,就是这个,所以可以直接创建一个presence结点
XMPPPresence *presence=[XMPPPresence presence];
//将上线节点发送出去
[_stream sendElement:presence];
}
//下线
-(void)goOffline
{
//创建 <presence type="unavailable"/> -- 下线
XMPPPresence *presence=[XMPPPresence presenceWithType:@"unavailable"];
//将下线节点发送出去
[_stream sendElement:presence];
//断开服务器
[_stream disconnect];
}
#pragma mark -- 注册账号
- (IBAction)registerBtnClick:(id)sender
{
//如果stream连接服务器,先断开,再连接
if (_stream.isConnected)
{
[self goOffline];
}
//希望注册的账号
NSString *jid= [NSString stringWithFormat:@"%@@%@",self.nickName.text,HOST];
_stream.tag=@"注册";
//设置 jid
[_stream setMyJID:[XMPPJID jidWithString:jid]];
//连接
[_stream connectWithTimeout:20 error:nil];
}
- (IBAction)login:(id)sender
{
if (_stream.isConnected)
{
[self goOffline];
}
//设置账号
NSString *jid= [NSString stringWithFormat:@"%@@%@",self.nickName.text,HOST];
//设置jid
[_stream setMyJID:[XMPPJID jidWithString:jid]];
_stream.tag=@"登录";
//连接
[_stream connectWithTimeout:20 error:nil];
}
- (IBAction)addFriend:(id)sender
{
/*
请求添加好友 -- 使用presence节点
xml格式:<presence type="subscribe" to="希望加为好友的JID">
*/
NSString *jid=[NSString stringWithFormat:@"%@@%@",self.friendNum.text,HOST];
XMPPPresence* presence = [XMPPPresence presenceWithType:@"subscribe" to:[XMPPJID jidWithString:jid]];
[_stream sendElement:presence];
}
- (IBAction)firendsList:(id)sender
{
/**
获取好友列表 使用iq结点
<ip type="get" id="roster">
<query xmlns="jabber:iq:roster"/>
</iq>
*/
XMPPIQ *iq=[XMPPIQ iqWithType:@"get"];
//添加iq的属性
[iq addAttributeWithName:@"id" stringValue:@"roster"];
//添加<query>子结点
NSXMLElement *query=[NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:roster"];
[iq addChild:query];
[_stream sendElement:iq];
}
//发送消息
- (IBAction)send:(id)sender
{
/**
使用message结点
xml格式一般为:
<message to="jid" type="chat">
<body>
<text>
聊天消息
</text>
<image>
图片
</image>
</body>
</message>
如果简单的我们可以直接在body里写入数据
<message to="jid" type="chat">
<body>
聊天信息
</body>
</message>
*/
XMPPMessage* message = [XMPPMessage messageWithType:@"chat" to:[XMPPJID jidWithString:[NSString stringWithFormat:@"%@@%@", self.friendNum.text, HOST]]];
NSXMLElement* body = [NSXMLElement elementWithName:@"body" stringValue:self.message.text];
[message addChild:body];
[_stream sendElement:message];
}
#pragma mark -- XMPPStreamDelegate
//连接成功的时候调用
-(void)xmppStreamDidConnect:(XMPPStream *)sender
{
NSLog(@"连接成功");
if ([sender.tag isEqualToString:@"注册"]) {
[_stream registerWithPassword:self.password.text error:nil];
}else if ([sender.tag isEqualToString:@"登录"])
{
//验证密码
[_stream authenticateWithPassword:self.password.text error:nil];
}
}
//注册成功
-(void)xmppStreamDidRegister:(XMPPStream *)sender
{
NSLog(@"注册成功");
}
//注册失败
-(void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error
{
NSLog(@"注册失败--%@",error);
}
//登录成功
-(void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{
[self goOnline];
NSLog(@"登录成功");
}
//登录失败
-(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error
{
NSLog(@"登录失败 -- %@",error);
}
//接受到一个presence
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence
{
/*
同意添加好友 -- 还是使用presence节点
只不过它的 xml格式:<presence type="subscribed" to="对方的JID">
*/
if ([presence.type isEqualToString:@"subscribe"])//-- 当有人相要加我为好友的时候,就要接受到这个presence
{
XMPPPresence *pre=[XMPPPresence presenceWithType:@"subscribed" to:presence.from];
[_stream sendElement:pre];
NSLog(@"同意%@加为好友",presence.fromStr);
}
if ( [presence.type isEqualToString:@"subscribed"]) //当我要加对方为好友,对方返回一个presence。
{
NSLog(@"添加好友%@成功",presence.fromStr);
}
}
//接受到一个iq -- 服务器返回查询结果
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
{
/*
<iq>
<query>
<item jid="好友1">
<item jid="好友2">
</query>
</iq>
*/
NSXMLElement *query=iq.children[0];
NSLog(@"---------好友列表------------");
for (NSXMLElement *elm in [query children])
{
NSXMLElement *jid=elm.attributes[0];
NSLog(@"%@",jid.stringValue);
}
return YES;
}
//接受到一个Message
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
NSLog(@"%@:%@",message.fromStr,message.stringValue);
}
@end
图:XMPP
————————————————————————————
书本关于XMPP的介绍:
————————————————————————————
一、定义
XMPP 是一种很类似于http协议的一种数据传输协议,它的过程就如同“解包装--〉包装”的过程,用户只需要明白它接受的类型,并理解它返回的类型,就可以很好的利用xmpp来进行数据通讯。
XMPP(可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测。它在促进服务器之间的准即时操作。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。
XMPP的前身是Jabber,一个开源形式组织产生的网络即时通信协议。XMPP目前被IETF国际标准组织完成了标准化工作。标准化的核心结果分为两部分;
- 核心协议
- 扩展协议(XEP: XMPP Extension Protocol)
XMPP的扩展协议是根据IETF在这之前对即时通讯的一个抽象定义的,与其他业已得到广泛使用的即时通讯协议,诸如AIM,QQ等有功能完整,完善等先进性。
XMPP的扩展协议Jingle使得其支持语音和视频。
XMPP的官方文档是RFC 3920。
二、网络结构
XMPP中定义了三个角色,客户端,服务器,网关。通信能够在这三者的任意两个之间双向发生。服务器同时承担了客户端信息记录,连接管理和信息的路由功能。网关承担着与异构即时通信系统的互联互通,异构系统可以包括SMS(短信),MSN,ICQ等。基本的网络形式是单客户端通过TCP/IP连接到单服务器,然后在之上传输XML。
注意,分属于不同server的client之间要通信的话,中间不能再经过其他server,这2个server必须直接通信。对于XMPP来说,server不能象email server那样,中间可以经过若干个server才能把邮件发送到目的地。
2.1 XMPP客户端
XMPP 系统的一个设计标准是必须支持简单的客户端。事实上,XMPP 系统架构对客户端只有很少的几个限制。一个XMPP 客户端必须支持的功能有:
- 通过 TCP 套接字与XMPP 服务器进行通信;
- 解析组织好的 XML 信息包;
- 理解消息数据类型。
基本的XMPP 客户端必须实现以下标准协议(XEP-0211):
- RFC3920 核心协议Core
- RFC3921 即时消息和出席协议Instant Messaging and Presence
- XEP-0030 服务发现Service Discovery
- XEP-0115 实体能力Entity Capabilities
2.2 XMPP服务器
XMPP 服务器遵循两个主要法则:
- 监听客户端连接,并直接与客户端应用程序通信;
- 与其他 XMPP 服务器通信;
基本的XMPP 服务器必须实现以下标准协议
- RFC3920 核心协议Core
- RFC3921 即时消息和出席协议Instant Messaging and Presence
- XEP-0030 服务发现Service Discovery
2.3 XMPP网关
XMPP 突出的特点是可以和其他即时通信系统交换信息和用户在线状况。由于协议不同,XMPP 和其他系统交换信息必须通过协议的转换来实现,目前几种主流即时通信协议都没有公开,所以XMPP 服务器本身并没有实现和其他协议的转换,但它的架构允许转换的实现。实现这个特殊功能的服务端在XMPP 架构里叫做网关(gateway)。目前,XMPP 实现了和AIM、ICQ、IRC、MSN Massager、RSS0.9 和Yahoo Massager 的协议转换。由于网关的存在,XMPP 架构事实上兼容所有其他即时通信网络,这无疑大大提高了XMPP 的灵活性和可扩展性。
三、系统特点
- 客户机/服务器通信模式;
- 分布式网络;
- 简单的客户端;
- XML的数据格式;
jid = [ node "@" ] domain [ "/" resource ]domain = fqdn / address-literalfqdn = (sub-domain 1*("." sub-domain))sub-domain = (internationalized domain label)address-literal = IPv4address / IPv6address
- bare JID:user@domain.tld
- full JID:user@domain.tld/resource
- normal:类似于email,主要特点是不要求响应;
- chat:类似于qq里的好友即时聊天,主要特点是实时通讯;
- groupchat:类似于聊天室里的群聊;
- headline:用于发送alert和notification;
- error:如果发送message出错,发现错误的实体会用这个类别来通知发送者出错了;
to="lily@jabber.org/contact"
type="chat" >
<body> 你好,在忙吗</body>
</message>
- subscribe:订阅其他用户的状态
- probe:请求获取其他用户的状态
- unavailable:不可用,离线(offline)状态
- chat:聊天中
- away:暂时离开
- xa:eXtend Away,长时间离开
- dnd:勿打扰
<show>xa</show>
<status>down the rabbit hole!</status>
</presence>
主要的属性是type。包括:
- Get :获取当前域值。类似于http get方法。
- Set :设置或替换get查询的值。类似于http put方法。
- Result :说明成功的响应了先前的查询。类似于http状态码200。
- Error: 查询和响应中出现的错误。
id="rr82a1z7"
to="alice@wonderland.lit"
type="get">
<query xmlns="jabber:iq:roster"/>
</iq>