ios 接入xmpp XMPPFramework XEP-0198: Stream Management

demo https://github.com/gwh111/testxmpp

xmpp介绍

中文官方http://wiki.jabbercn.org/%E9%A6%96%E9%A1%B5

ios库下载https://github.com/robbiehanson/XMPPFramework

重连问题https://www.jianshu.com/p/d9de0267c52a

推荐使用pod

pod 'XMPPFramework', '~> 3.7.0'
导入
import XMPPFramework      // swift
@import XMPPFramework;   //objective-c


Module 'XMPPFramework' not found

The “Swift Language Version” (SWIFT_VERSION) build setting must be set to a supported value for targets which use Swift. This setting can be set in the build settings editor.
解决办法:在pod工程中的kissxml buildsetting 搜索swift 选择kissxml 的swift版本


之后报错  Use of '@import' when modules are disabled
的话将target中的build setting 设置为enable modules 为YES 


XEP-0198: Stream Management

是xmpp的流管理协议  https://xmpp.org/extensions/xep-0198.html
开启流管理后可以保证连接断开重连是同一个流,可有效防止数据丢失

1、Client enables stream management
在建立验证之后客户端发起流的开启
客户端发起<enable xmlns='urn:xmpp:sm:3'/>
对应的xmpp封装的方法是
[_xmppStreamManagement enableStreamManagementWithResumption:YES maxTimeout:10];

2、Server enables stream management
服务端验证流管理开启 发回给客户端
<enabled xmlns='urn:xmpp:sm:3'/>

3、Server enables stream management with session resumption
<enabled xmlns='urn:xmpp:sm:3' id='some-long-sm-id' resume='true’/>

Acks

Acks 重连 启动流管理

启动流管理(stream management)之后,客户端或者服务端可以在任意时间通过流发送ack元素。ack元素可以是:
    <a/>元素用于回答一个确认收到的请求或者发送一个未被请求的ack。
    <r/>元素用于请求收到节(stanzas)的确认信息。
属性定义如下:
    'h'属性标识最后处理的节(即,服务器确认收到最后一节)。
    一个<a/>元素必须具备一个”h“属性。
    <r/>元素没有已定义属性。
定义:确认一条之前收到的ack元素,节是被服务器处理后再发送出去的。对于服务端的处理,我们是指服务端接管一条或多条节(如,直接处理节,将节传给本地的一个实体,比如相同服务器下的另一个客户端,或者将节发送给其他服务器的远程实体);一个节在确认被服务端处理之前,发送方都要对节负责(例如,如果没有被服务端确认处理,客户端要重发这个节或者生成一个错误)。
       收到一个<r/>元素的回执并不意味着新的节已经传送给对方,只有收到一个<a/>元素的回执,并且包含“h”属性已经增长了,才说明这个新的节已经被处理过。
      “h”值在流管理中被启动或被请求启动时,是从零开始的。当第一个节被处理的时候,“h”值增加为1,并随着接下来新的节被处理,“h”值不断增加。在一些极端情况下,在流管理对话中被处理的节的数量,超出了无符号整型数所表示的最大数(2的32次方)XML Schema Part 2  [10],“h”值应该被置为0,而不是2的32次方。
       注意:任何给定的流,都有两个“h”值,一个被客户端用于跟踪节是否被服务端处理,另一个被服务端用于跟踪节是否被处理自客户端。当客户端向服务端发送<enable/>时,客户端初始化它的“h”值为零,同样服务端发送<enable/>给客户端时,也初始化它的“h”值为零(服务端会立即回复<enable/>,同时设置它的计数器为零)。初始化后,客户端根据处理的节的数目不断增加“h”值,服务端也相应不断增加“h”值。

在开启流管理之后,服务端和客户端可以通过acks来相互传递消息
它的元素包括
  • The <a/> element is used to answer a request for acknowledgement or to send an unrequested ack.
  • The <r/> element is used to request acknowledgement of received stanzas
  • The 'h' attribute identifies the last handled stanza (i.e., the last stanza that the server will acknowledge as having received).
An <a/> element MUST possess an 'h' attribute.
The <r/> element has no defined attributes.
<a/>带有一个h来标识最后一个节点

举个例子
<!-- Client -->
<r xmlns='urn:xmpp:sm:3'/>

<!-- Server -->
<a xmlns='urn:xmpp:sm:3' h='1’/>

Resumption

恢复
客户端流恢复发送
<enable xmlns='urn:xmpp:sm:3' resume='true’/>
服务端流恢复
<enabled xmlns='urn:xmpp:sm:3' id='some-long-sm-id' resume='true’/>

在xmpp封装的方法中
使用- (void)sendElement:(NSXMLElement *)element发送元素
首先验证通过会发送<presence/>

之后开启流管理会发送<enable xmlns='urn:xmpp:sm:3'/>

对于元素的监听可以通过
- (void)xmppStream:(XMPPStream *)sender didSendCustomElement:(NSXMLElement *)element{
    NSLog(@"didSendCustomElement%@",element);
}
- (void)xmppStream:(XMPPStream *)sender didReceiveCustomElement:(NSXMLElement *)element{
    NSLog(@"didReceiveCustomElement%@",element);
}
这两个方法实现


参考https://blog.csdn.net/yuedong56/article/details/38120101

XEP-0198: Stream Management

h文件

#import <Foundation/Foundation.h>

@interface XMPPHelper : NSObject

//@property(nonatomic,assign) BOOL isConnecting;

+ (instancetype)getInstance;

- (BOOL)initWithUserName:(NSString *)userName andPassword:(NSString *)password andHostName:(NSString *)hostName andDomain:(NSString*)domain andHostPort:(UInt16)hostPort andInfoDic:(NSDictionary *)infoDic;

- (BOOL)connect;

- (void)disconnect;

- (BOOL)isConnected;

@end

初始化参数说明

userName 用户名

password 密码

hostName 主机名 如10.5.190.1

domain 域名 如qpmjcore-98-1/smack

hostPort 端口号 如5222

如果回调不到 xmppStreamDidConnect
把servicename中的@去掉 


类说明

@property(nonatomic,retain) XMPPJID *jid;
@property(nonatomic,retain) XMPPStream *xmppStream;
@property(nonatomic,retain) XMPPStreamManagement *xmppStreamManagement;
@property(nonatomic,retain) XMPPStreamManagementMemoryStorage *storage;
@property(nonatomic,retain) XMPPReconnect *xmppReconnect;  

XMPPJID 根据初始化数据构造的发送地址

XMPPStream 输入输出流

XMPPStreamManagement 输入输出流的管理

XMPPStreamManagementMemoryStorage 数据永久存储

XMPPReconnect 重连模块


接收消息和发送消息通过

- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message{
    NSString *messageBody = [[message elementForName:@"body"] stringValue];
//    [ShareCallback xmppPushMsg:messageBody];
//    int su=[_xmppStream supportsStreamManagement];
//    [self sendMessage:@"aaa" to:_jid];
//
//    NSXMLElement *enable = [NSXMLElement elementWithName:@"r" xmlns:@"urn:xmpp:sm:3"];
//    [[self xmppStream] sendElement:enable];
}
- (void)sendMessage:(NSString *)message to:(XMPPJID *)jid
{
    XMPPMessage* newMessage = [[XMPPMessage alloc] initWithType:@"chat" to:jid];
    [newMessage addBody:message]; //消息内容
    [_xmppStream sendElement:newMessage];
}

- (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message{
    
}


在建立连接,验证通过后开启流管理enableStreamManagementWithResumption

这里有一个XEP-0198协议 https://xmpp.org/extensions/xep-0198.html

If YES, the resume attribute will be included. E.g. <enable resume='true'/>

在客户端就是通过发送一个element来开启,开启后在每接收到一定消息后会通过上面提到的Acks /r和/a的标签来验证消息

可以通过

- (void)xmppStream:(XMPPStream *)sender didSendCustomElement:(NSXMLElement *)element{
    NSLog(@"didSendCustomElement%@",element);
}
- (void)xmppStream:(XMPPStream *)sender didReceiveCustomElement:(NSXMLElement *)element{
    NSLog(@"didReceiveCustomElement%@",element);
}

来查看/r和/a的标签

demo https://github.com/gwh111/testxmpp


更新:

xmpp断线重连模块

//接入断线重连模块
    _xmppReconnect = [[XMPPReconnect alloc] init];
    _xmppReconnect.reconnectTimerInterval=5;
    _xmppReconnect.reconnectDelay=0;
    [_xmppReconnect setAutoReconnect:YES];
    [_xmppReconnect activate:self.xmppStream];
    [_xmppReconnect addDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
#pragma mark - XMPPReconnectDelegate
- (void)xmppReconnect:(XMPPReconnect *)sender didDetectAccidentalDisconnect:(SCNetworkConnectionFlags)connectionFlags {
    NSLog(@"xmpp意外断开连接。");
    [ShareCallback xmppCallback:@"disconnect"];
    [self disconnect];
}
- (BOOL)xmppReconnect:(XMPPReconnect *)sender shouldAttemptAutoReconnect:(SCNetworkConnectionFlags)connectionFlags {
    return YES;
}
xmpp顶号 在另一台设备登录
- (void)xmppStream:(XMPPStream *)sender didReceiveError:(id)error
{
    NSLog(@"收到错误消息%@",error);
    if ([error isKindOfClass:[DDXMLElement class]]) {
        DDXMLElement *er=error;
        if ([er.description containsString:@"conflict"]) {
            [ShareCallback xmppCallback:@"resign"];
            [self disconnect];
            return;
        }
    }
    [ShareCallback xmppCallback:@"fail"];
    //<stream:error xmlns:stream="http://etherx.jabber.org/streams"><conflict xmlns="urn:ietf:params:xml:ns:xmpp-streams"/></stream:error>
    
//    [ShareCallback xmppPushError:error];
}
xmpp的ping
XMPPAutoPing *xmppAutoPing =  [[XMPPAutoPing alloc] init];
    xmppAutoPing.pingInterval = 10.0;
    [xmppAutoPing activate:_xmppStream];
    [xmppAutoPing addDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
#pragma mark- XMPPAutoPingDelegate
- (void)xmppAutoPingDidReceivePong:(XMPPAutoPing *)sender{
    
}
- (void)xmppAutoPingDidTimeout:(XMPPAutoPing *)sender {
    [ShareCallback xmppCallback:@"fail"];
    [self disconnect];
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值