iOS的XMPPFramework简单介绍(实现及时通信)

时间:2022-09-20 09:44:39

xmppframework是一个os x/ios平台的开源项目,使用objective-c实现了xmpp协议(rfc-3920),同时还提供了用于读写xml的工具,大大简化了基于xmpp的通信应用的开发。

1. 登录和好友上下线

1.1xmpp中常用对象们

  • xmppstream:xmpp基础服务类
  • xmpproster:好友列表类
  • xmpprostercoredatastorage:好友列表(用户账号)在core data中的操作类
  • xmppvcardcoredatastorage:好友名片(昵称,签名,性别,年龄等信息)在core data中的操作类
  • xmppvcardtemp:好友名片实体类,从数据库里取出来的都是它
  • xmppvcardavatarmodule:好友头像
  • xmppreconnect:如果失去连接,自动重连
  • xmpproom:提供多用户聊天支持
  • xmpppubsub:发布订阅

1.2登录操作,也就是连接xmpp服务器

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)connect {
  if (self.xmppstream == nil) {
    self.xmppstream = [[xmppstream alloc] init];
    [self.xmppstream adddelegate:self delegatequeue:dispatch_get_main_queue()];
  }
  if (![self.xmppstream isconnected]) {
    nsstring *username = [[nsuserdefaults standarduserdefaults] objectforkey:@"username"];
    xmppjid *jid = [xmppjid jidwithuser:username domain:@"lizhen" resource:@"ework"];
    [self.xmppstream setmyjid:jid];
    [self.xmppstream sethostname:@"10.4.125.113"];
    nserror *error = nil;
    if (![self.xmppstream connect:&error]) {
      nslog(@"connect error: %@", [[error userinfo] description]);
    }
  }
}

connect成功之后会依次调用xmppstreamdelegate的方法,首先调用

?
1
- (void)xmppstream:(xmppstream *)sender socketdidconnect:(gcdasyncsocket *)socket

然后

?
1
- (void)xmppstreamdidconnect:(xmppstream *)sender

在该方法下面需要使用xmppstream 的authenticatewithpassword方法进行密码验证,成功的话会响应delegate的方法,就是下面这个

?
1
- (void)xmppstreamdidauthenticate:(xmppstream *)sender

1.3上线

实现 - (void)xmppstreamdidauthenticate:(xmppstream *)sender 委托方法

?
1
2
3
4
- (void)xmppstreamdidauthenticate:(xmppstream *)sender {
  xmpppresence *presence = [xmpppresence presencewithtype:@"available"];
  [self.xmppstream sendelement:presence];
}

1.4退出并断开连接

?
1
2
3
4
5
6
- (void)disconnect {
  xmpppresence *presence = [xmpppresence presencewithtype:@"unavailable"];
  [self.xmppstream sendelement:presence];
   
  [self.xmppstream disconnect];
}

1.5好友状态

获取好友状态,通过实现

?
1
2
3
- (void)xmppstream:(xmppstream *)sender didreceivepresence:(xmpppresence *)presence
 
...

方法,当接收到 presence 标签的内容时,xmppframework 框架回调该方法

一个 presence 标签的格式一般如下:
iOS的XMPPFramework简单介绍(实现及时通信)

presence 的状态:

  • available 上线
  • away 离开
  • do not disturb 忙碌
  • unavailable 下线
?
1
2
3
4
5
6
7
8
9
10
11
- (void)xmppstream:(xmppstream *)sender didreceivepresence:(xmpppresence *)presence {
  nsstring *presencetype = [presence type];
  nsstring *presencefromuser = [[presence from] user];
  if (![presencefromuser isequaltostring:[[sender myjid] user]]) {
    if ([presencetype isequaltostring:@"available"]) {
      //
    } else if ([presencetype isequaltostring:@"unavailable"]) {
      //
    }
  }
}

2. 接收消息和发送消息

2.1接收消息

当接收到 message 标签的内容时,xmppframework 框架回调该方法

根据 xmpp 协议,消息体的内容存储在标签 body 内

?
1
2
3
- (void)xmppstream:(xmppstream *)sender didreceivemessage:(xmppmessage *)message {
  nsstring *messagebody = [[message elementforname:@"body"] stringvalue];
}

2.2发送消息
发送消息,我们需要根据 xmpp 协议,将数据放到标签内,例如:iOS的XMPPFramework简单介绍(实现及时通信)

?
1
2
3
4
5
6
7
8
9
10
- (void)sendmessage:(nsstring *) message touser:(nsstring *) user {
  nsxmlelement *body = [nsxmlelement elementwithname:@"body"];
  [body setstringvalue:message];
  nsxmlelement *message = [nsxmlelement elementwithname:@"message"];
  [message addattributewithname:@"type" stringvalue:@"chat"];
  nsstring *to = [nsstring stringwithformat:@"%@@example.com", user];
  [message addattributewithname:@"to" stringvalue:to];
  [message addchild:body];
  [self.xmppstream sendelement:message];
}

3. 获取好友信息和删除好友

3.1好友列表和好友名片

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[_xmpproster fetchroster];//获取好友列表
//获取到一个好友节点
- (void)xmpproster:(xmpproster *)sender didrecieverosteritem:(nsxmlelement *)item
//获取完好友列表
- (void)xmpprosterdidendpopulating:(xmpproster *)sender
//到服务器上请求联系人名片信息
- (void)fetchvcardtempforjid:(xmppjid *)jid;
//请求联系人的名片,如果数据库有就不请求,没有就发送名片请求
- (void)fetchvcardtempforjid:(xmppjid *)jid ignorestorage:(bool)ignorestorage;
//获取联系人的名片,如果数据库有就返回,没有返回空,并到服务器上抓取
- (xmppvcardtemp *)vcardtempforjid:(xmppjid *)jid shouldfetch:(bool)shouldfetch;
//更新自己的名片信息
- (void)updatemyvcardtemp:(xmppvcardtemp *)vcardtemp;
//获取到一盒联系人的名片信息的回调
- (void)xmppvcardtempmodule:(xmppvcardtempmodule *)vcardtempmodule
    didreceivevcardtemp:(xmppvcardtemp *)vcardtemp
           forjid:(xmppjid *)jid

3.2添加好友

?
1
2
3
4
5
6
7
8
9
//name为用户账号
 - (void)xmppaddfriendsubscribe:(nsstring *)name
 {
   //xmpphost 就是服务器名, 主机名
   xmppjid *jid = [xmppjid jidwithstring:[nsstring stringwithformat:@"%@@%@",name,xmpphost]];
   //[presence addattributewithname:@"subscription" stringvalue:@"好友"];
   [xmpproster subscribepresencetouser:jid];
    
 }

3.3收到添加好友的请求

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)xmpproster:(xmpproster *)sender didreceivepresencesubscriptionrequest:(xmpppresence *)presence
 {
   //取得好友状态
   nsstring *presencetype = [nsstring stringwithformat:@"%@", [presence type]]; //online/offline
   //请求的用户
   nsstring *presencefromuser =[nsstring stringwithformat:@"%@", [[presence from] user]];
   nslog(@"presencetype:%@",presencetype);
    
   nslog(@"presence2:%@ sender2:%@",presence,sender);
    
   xmppjid *jid = [xmppjid jidwithstring:presencefromuser];
   //接收添加好友请求
   [xmpproster acceptpresencesubscriptionrequestfrom:jid andaddtoroster:yes];
    
 }

3.4删除好友

?
1
2
3
4
5
6
7
//删除好友,name为好友账号
- (void)removebuddy:(nsstring *)name
{
  xmppjid *jid = [xmppjid jidwithstring:[nsstring stringwithformat:@"%@@%@",name,xmpphost]];
    
  [self xmpproster] removeuser:jid];
}

4. 聊天室

初始化聊天室

?
1
2
3
4
5
6
xmppjid *roomjid = [xmppjid jidwithstring:room_jid];
   
  xmpproom = [[xmpproom alloc] initwithroomstorage:self jid:roomjid];
   
  [xmpproom activate:xmppstream];
  [xmpproom adddelegate:self delegatequeue:dispatch_get_main_queue()];

创建聊天室成功

?
1
2
3
4
- (void)xmpproomdidcreate:(xmpproom *)sender
  {
    ddloginfo(@"%@: %@", this_file, this_method);
  }

加入聊天室,使用昵称

?
1
[xmpproom joinroomusingnickname:@"quack" history:nil];

获取聊天室信息

?
1
2
3
4
5
6
7
- (void)xmpproomdidjoin:(xmpproom *)sender
{
  [xmpproom fetchconfigurationform];
  [xmpproom fetchbanlist];
  [xmpproom fetchmemberslist];
  [xmpproom fetchmoderatorslist];
}

如果房间存在,会调用委托

?
1
2
3
4
5
6
// 收到禁止名单列表
 - (void)xmpproom:(xmpproom *)sender didfetchbanlist:(nsarray *)items;
 // 收到好友名单列表
 - (void)xmpproom:(xmpproom *)sender didfetchmemberslist:(nsarray *)items;
 // 收到主持人名单列表
 - (void)xmpproom:(xmpproom *)sender didfetchmoderatorslist:(nsarray *)items;

房间不存在,调用委托

?
1
2
3
- (void)xmpproom:(xmpproom *)sender didnotfetchbanlist:(xmppiq *)iqerror;
 - (void)xmpproom:(xmpproom *)sender didnotfetchmemberslist:(xmppiq *)iqerror;
 - (void)xmpproom:(xmpproom *)sender didnotfetchmoderatorslist:(xmppiq *)iqerror;

离开房间

?
1
[xmpproom deactivate:xmppstream];

xmpproomdelegate的其他代理方法:

离开聊天室

?
1
2
3
4
- (void)xmpproomdidleave:(xmpproom *)sender
 {
   ddlogverbose(@"%@: %@", this_file, this_method);
 }

新人加入群聊

?
1
2
3
4
- (void)xmpproom:(xmpproom *)sender occupantdidjoin:(xmppjid *)occupantjid
 {
   ddlogverbose(@"%@: %@", this_file, this_method);
 }

有人退出群聊

?
1
2
3
4
- (void)xmpproom:(xmpproom *)sender occupantdidleave:(xmppjid *)occupantjid
 {
   ddlogverbose(@"%@: %@", this_file, this_method);
 }

有人在群里发言

?
1
2
3
4
- (void)xmpproom:(xmpproom *)sender didreceivemessage:(xmppmessage *)message fromoccupant:(xmppjid *)occupantjid
 {
   ddlogverbose(@"%@: %@", this_file, this_method);
 }

5. 消息回执

这个是xep-0184协议的内容。协议内容:

发送消息时附加回执请求
iOS的XMPPFramework简单介绍(实现及时通信)

代码实现

?
1
2
3
4
5
6
7
nsstring *siid = [xmppstream generateuuid];
  //发送消息
  xmppmessage *message = [xmppmessage messagewithtype:@"chat" to:jid elementid:siid];
  nsxmlelement *receipt = [nsxmlelement elementwithname:@"request" xmlns:@"urn:xmpp:receipts"];
  [message addchild:receipt];
  [message addbody:@"测试"];
  [self.xmppstream sendelement:message];

收到回执请求的消息,发送回执
iOS的XMPPFramework简单介绍(实现及时通信)

代码实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
- (void)xmppstream:(xmppstream *)sender didreceivemessage:(xmppmessage *)message
 {
   //回执判断
   nsxmlelement *request = [message elementforname:@"request"];
   if (request)
   {
     if ([request.xmlns isequaltostring:@"urn:xmpp:receipts"])//消息回执
     {
       //组装消息回执
       xmppmessage *msg = [xmppmessage messagewithtype:[message attributestringvalueforname:@"type"] to:message.from elementid:[message attributestringvalueforname:@"id"]];
       nsxmlelement *recieved = [nsxmlelement elementwithname:@"received" xmlns:@"urn:xmpp:receipts"];
       [msg addchild:recieved];
        
       //发送回执
       [self.xmppstream sendelement:msg];
     }
   }else
   {
     nsxmlelement *received = [message elementforname:@"received"];
     if (received)
     {
       if ([received.xmlns isequaltostring:@"urn:xmpp:receipts"])//消息回执
       {
         //发送成功
         nslog(@"message send success!");
       }
     }
   }
    
   //消息处理
   //...
 }

6. 添加autoping

为了监听服务器是否有效,增加心跳监听。用xep-0199协议,在xmppframework框架下,封装了 xmppautoping 和 xmppping两个类都可以使用,因为xmppautoping已经组合进了xmppping类,所以xmppautoping使用起来更方便。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//初始化并启动ping
-(void)autopingproxyserver:(nsstring*)strproxyserver
{
  _xmppautoping = [[xmppautopingalloc] init];
  [_xmppautopingactivate:_xmppstream];
  [_xmppautopingadddelegate:selfdelegatequeue: dispatch_get_main_queue()];
  _xmppautoping.respondstoqueries = yes;
  _xmppautoping.pinginterval=2;//ping 间隔时间
  if (nil != strproxyserver)
  {
    _xmppautoping.targetjid = [xmppjid jidwithstring: strproxyserver ];//设置ping目标服务器,如果为nil,则监听socketstream当前连接上的那个服务器
  }
}
//卸载监听
 [_xmppautoping  deactivate];
 [_xmppautoping  removedelegate:self];
  _xmppautoping = nil;
//ping xmppautopingdelegate的委托方法:
- (void)xmppautopingdidsendping:(xmppautoping *)sender
{
  nslog(@"- (void)xmppautopingdidsendping:(xmppautoping *)sender");
}
- (void)xmppautopingdidreceivepong:(xmppautoping *)sender
{
  nslog(@"- (void)xmppautopingdidreceivepong:(xmppautoping *)sender");
}
  
- (void)xmppautopingdidtimeout:(xmppautoping *)sender
{
  nslog(@"- (void)xmppautopingdidtimeout:(xmppautoping *)sender");
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。