1.什么是XMPP(Extensible Messaging and Presence Protocol)
XMPP是一种基于标准通用标记语言的子集XML的协议,它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。经过扩展以后的XMPP可以通过发送扩展的信息来处理用户的需求,以及在XMPP的顶端建立如内容发布系统和基于地址的服务等应用程序。而且,XMPP包含了针对服务器端的软件协议,使之能与另一个进行通话,这使得开发者更容易建立客户应用程序或给一个配好系统添加功能。
可扩展通讯和表示协议 (XMPP) 可用于服务类实时通讯、表示和需求响应服务中的XML数据元流式传输。XMPP以Jabber协议为基础,而Jabber是即时通讯中常用的开放式协议
XMPP的前身是Jabber,一个开源形式组织产生的网络即时通信协议
XMPP配置
1.下载第三方库
gitHub下载
2.导入对应的库文件

3.需要在Header Search Paths中配置路径/usr/include/libxml2
XMPP使用
1.注册
2.登录
3.登录状态
presence 的状态:
available 上线
away 离开
do not disturb 忙碌
unavailable 下线
XMPPPresence presenceWithType:@”available” 为上线状态
XMPPPresence *presence = [XMPPPresence presenceWithType:@”unavailable”]; 下线状态
4.得到好友列表 (具体实现见代码).
好友列表,在 XMPP 中被称为 roster,花名册?
要像服务器发送 一个 IQ 请求:
iq type=”get”
from=”xiaoming@example.com”
to=”example.com”
id=”1234567”>
>query xmlns=”jabber:iq:roster”/>
iq />
type 属性,说明了该 iq 的类型为 get,与 HTTP 类似,向服务器端请求信息
from 属性,消息来源,这里是你的 JID
to 属性,消息目标,这里是服务器域名
id 属性,标记该请求 ID,当服务器处理完毕请求 get 类型的 iq 后,响应的 result 类型 iq 的 ID 与 请求 iq 的 ID 相同
服务器返回的一个 IQ 响应:
iq type=”result”
id=”1234567”
to=”xiaoming@example.com”>
>query xmlns=”jabber:iq:roster”>
>item jid=”xiaoyan@example.com” name=“哈哈“ />
>item jid=”xiaoqiang@example.com” name=“呵呵”/>
>query />
iq />
解析数据取得所有好友
5.接收消息
当接收到 >message /> 标签的内容时,XMPPFramework 框架回调该方法
根据 XMPP 协议,消息体的内容存储在标签 >body /> 内
6.发送消息
发送消息
发送消息,我们需要根据 XMPP 协议,将数据放到 >message /> 标签内,例如:
message type=”chat” to=”xiaoming@example.com”>
>body>Hello World!>body />
message />
7.添加好友
XMPP Demo
TQXMPPManager.h
import “XMPPFramework.h”
//ret default is NO 如果 为 NO说明注册或者登录失败
typedef void (^CheckUserBlock)(BOOL ret,NSError *error);
//获取好友列表的Block
typedef void (^GetAllFirendBlock)(NSArray *friendArray);
//接受到消息
typedef void (^RecvMsgBlock)(NSData *data);
@interface TQXMPPManager : NSObject
//xmpp管理的单例对象
+(TQXMPPManager *)sharedXMPPManager;
//注册和登录的接口
//state 登录 或者 注册的状态
-(void)checkUserInfo:(NSString )userName passwd:(NSString )userPasswd state:(BOOL)state success:(CheckUserBlock)checkBlock;
//上线
-(void)OnLine;
//获取好友列表
-(void)getAllFrends:(GetAllFirendBlock)friendBlock;
//发送消息
-(void)sendMsg:(NSString )userJid content:(NSString )content;
@end
TQXMPPManager.m
@interface TQXMPPManager () >XMPPStreamDelegate>
//NO register YES is login default is NO
@property (nonatomic) BOOL isLogin;
@property (nonatomic,strong) XMPPStream *streamManager;
//回调的block
@property (nonatomic,strong) CheckUserBlock checkBlock;
//保存用户的信息
@property (nonatomic,strong) TQUserModel *userModel;
//保存当前获得好友列表的block
@property (nonatomic,strong) GetAllFirendBlock friendBlock;
//保存当前所有好友
@property (nonatomic,strong) NSMutableArray *friendArray;
@end
@implementation TQXMPPManager
(instancetype)init
{
self = [super init];
if (self) {
self.streamManager = [[XMPPStream alloc] init];self.friendArray = [[NSMutableArray alloc] init]; //实例化用户模型的信息. self.userModel = [[TQUserModel alloc] init]; //设置代理 [self.streamManager addDelegate:self delegateQueue:dispatch_get_main_queue()];
}
return self;
}
+(TQXMPPManager *)sharedXMPPManager
{
static TQXMPPManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[TQXMPPManager alloc] init];
});
return manager;
}
-(void)checkUserInfo:(NSString )userName passwd:(NSString )userPasswd state:(BOOL)state success:(CheckUserBlock)checkBlock
{
//断开连接
[_streamManager disconnect];
//记录当前状态 是登录还是注册
self.isLogin = state;
self.checkBlock = checkBlock;
//注册
self.userModel.userJID = userName;
self.userModel.userPasswd = userPasswd;
if(state)
{
//使用当前用户名和密码登录
XMPPJID *newJID = [XMPPJID jidWithString:self.userModel.userJID];
self.streamManager.myJID = newJID;
}
else
{
//使用千锋的XMPP服务器 注册用户
XMPPJID *jid = [XMPPJID jidWithString:@"anonymous@1000phone.net"];
_streamManager.myJID = jid;
}
NSError *error = nil;
BOOL ret = [self.streamManager connectWithTimeout:-1 error:&error];
//如果连接超时,注册失败
if(ret == NO)
{
if(self.checkBlock)
{
self.checkBlock(NO,error);
}
}
}
//连接成功后 要重新设置用户名和发送密码
-(void)xmppStreamDidConnect:(XMPPStream *)sender
{
NSError *err = nil;
//重新设置用户名 使用当前用户名登录 或者 注册
XMPPJID *newJID = [XMPPJID jidWithString:self.userModel.userJID];
self.streamManager.myJID = newJID;
BOOL ret = NO;
//输入密码 ---> 判断是注册还是登陆
if(self.isLogin){
ret = [self.streamManager authenticateWithPassword:self.userModel.userPasswd error:&err];
}else{
ret = [self.streamManager registerWithPassword:self.userModel.userPasswd error:&err];
}
//实现回调
if(ret == NO){
if(self.checkBlock){
self.checkBlock(NO,err);
}
}
}
pragma mark - 注册完成的相关代理方法
//注册成功
-(void)xmppStreamDidRegister:(XMPPStream *)sender
{
if(self.checkBlock){
self.checkBlock(YES,nil);
}
}
//注册失败
-(void)xmppStream:(XMPPStream )sender didNotRegister:(DDXMLElement )error
{
if(self.checkBlock)
{
NSError *err = [NSError errorWithDomain:error.description code:0 userInfo:nil];
self.checkBlock(NO,err);
}
}
pragma mark - 登录完成的相关代理方法
//授权密码失败
-(void)xmppStream:(XMPPStream )sender didNotAuthenticate:(DDXMLElement )error
{
if(self.checkBlock)
{
NSError *err = [NSError errorWithDomain:error.description code:0 userInfo:nil];
self.checkBlock(NO,err);
}
}
//授权密码成功
-(void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{
if(self.checkBlock){
self.checkBlock(YES,nil);
}
}
pragma mark - 上线
//上线
-(void)OnLine
{
XMPPPresence *presence = [XMPPPresence presence];
[self.streamManager sendElement:presence];
}
pragma mark - 获取好友列表
//获取好友列表
-(void)getAllFrends:(GetAllFirendBlock)friendBlock
{
self.friendBlock = friendBlock;
NSXMLElement *element = [NSXMLElement elementWithName:@"query"];
[element addAttributeWithName:@"xmlns" stringValue:@"jabber:iq:roster"];
NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"];
[iq addAttributeWithName:@"type" stringValue:@"get"];
[iq addChild:element];
[self.streamManager sendElement:iq];
}
-(BOOL)xmppStream:(XMPPStream )sender didReceiveIQ:(XMPPIQ )iq
{
//解析好友列表
//elementForName 取出一个节点
NSXMLElement *query = [iq elementForName:@”query”];
//elementsForName 取出所有同名的节点
//以下数组中的每个元素都为NSXMLElement对象
NSArray *items = [query elementsForName:@”item”];
for (NSXMLElement *it in items)
{
//每个it对象就是一个item
NSString *jid = [[it attributeForName:@”jid”] stringValue];
TQUserModel *friend = [[TQUserModel alloc] init];
friend.userJID = jid;
[self.friendArray addObject:friend];
}
if(self.friendBlock)
{
self.friendBlock(self.friendArray);
}
return YES;
}
-(void)sendMsg:(NSString )userJid content:(NSString )content
{
//目标好友的id
XMPPJID *toJid = [XMPPJID jidWithString:userJid];
//消息对象
XMPPMessage *msg = [XMPPMessage messageWithType:@”chat” to:toJid];
//设置消息内容
[msg addBody:content];
NSLog(@”msg = %@”, msg);
//发送消息
[self.streamManager sendElement:msg];
}
-(void)xmppStream:(XMPPStream )sender didReceiveMessage:(XMPPMessage )message
{
if ([message isChatMessageWithBody])
{
NSLog(@”%@,%@”,message.body,message.from.bare);
}
}
@end
添加好友Controller
@interface TQNewFriendViewController ()
@property (weak, nonatomic) IBOutlet UITextField *friendName;
@end
@implementation TQNewFriendViewController
- (IBAction)addFriend:(id)sender {
//[[TQXMPPManager sharedXMPPManager] addFriend:self.friendName.text];
}
(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.self.navigationItem.title = @”添加好友”;
}(void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)touchesBegan:(NSSet>UITouch > )touches withEvent:(UIEvent *)event
{
[self.view endEditing:YES];
}
主界面Controller
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addFriend)];
self.navigationItem.title = @"好友列表";
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
//上线
[[TQXMPPManager sharedXMPPManager] OnLine];
//获取好友列表
[[TQXMPPManager sharedXMPPManager] getAllFrends:^(NSArray *friendArray) {
self.friendArray = friendArray;
[self.tableView reloadData];
}];
}
-(void)addFriend
{
[[TQXMPPManager sharedXMPPManager] sendMsg:@”lTQ123456@1000phone.net” content:@”哈哈哈哈”];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
pragma mark - Table view data source
(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
warning Incomplete implementation, return the number of sections
return 1;
}(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
warning Incomplete implementation, return the number of rows
return self.friendArray.count;
}(UITableViewCell )tableView:(UITableView )tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@”cell”];TQUserModel *userModel = self.friendArray[indexPath.row];
cell.textLabel.text = userModel.userJID;return cell;
}
登录Controller
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *userName;
@property (weak, nonatomic) IBOutlet UITextField *userPasswd;
- (IBAction)login:(id)sender;
@end
@implementation ViewController
(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.title = @”登录”;
}(void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}(IBAction)login:(id)sender {
[[TQXMPPManager sharedXMPPManager] checkUserInfo:self.userName.text passwd:self.userPasswd.text state:YES success:^(BOOL ret, NSError *error) {
if(ret)
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@”提示” message:@”登录成功” preferredStyle:UIAlertControllerStyleAlert];UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; id vc = [sb instantiateViewControllerWithIdentifier:@"MainVC"]; [self.navigationController pushViewController:vc animated:YES]; }]; [alertController addAction:action]; [self presentViewController:alertController animated:YES completion:nil]; } else { NSLog(@"%@",error); UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"登录失败" preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }]; [alertController addAction:action]; [self presentViewController:alertController animated:YES completion:nil]; }
}];
}
@end
注册
@interface TQRegisterViewController ()
@property (weak, nonatomic) IBOutlet UITextField *userName;
@property (weak, nonatomic) IBOutlet UITextField *userPasswd;
- (IBAction)registerUser:(id)sender;
@end
@implementation TQRegisterViewController
(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.self.navigationItem.title = @”注册”;
}(void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
(IBAction)registerUser:(id)sender {
//注册用户
[[TQXMPPManager sharedXMPPManager] checkUserInfo:self.userName.text passwd:self.userPasswd.text state:NO success:^(BOOL ret, NSError *error) {
if(ret)
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@”提示” message:@”注册成功” preferredStyle:UIAlertControllerStyleAlert];UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [self.navigationController popViewControllerAnimated:YES]; }]; [alertController addAction:action]; [self presentViewController:alertController animated:YES completion:nil]; } else { NSLog(@"%@",error); UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"注册失败" preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }]; [alertController addAction:action]; [self presentViewController:alertController animated:YES completion:nil]; }
}];
}
@end
TQUserModel.h
@interface TQUserModel : NSObject
//保存用户名
@property (nonatomic,strong) NSString *userJID;
//保存用户名的密码
@property (nonatomic,strong) NSString *userPasswd;
//保存用户的登录状态
@property (nonatomic,strong) NSString *state;
@end