内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

iOS XMPP快速登录

2018-03-12 17:08 出处:清屏网 人气: 评论(0

上一篇文章中,当 XMPP 需要建立起连接的时候,总共会发送 6 条数据包给 Openfire 服务器。先来回顾一下:

17:22:41.357  -[XMPPManager xmppStreamWillConnect:] socket正在连接...

17:22:42.100  -[XMPPManager xmppStream:socketDidConnect:] socket连接成功...

17:22:42.102  ①、SEND: <?xml version='1.0'?>

17:22:42.103  ②、SEND: <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' to='192.168.1.1'>

17:22:43.085  RECV: <stream:features xmlns:stream="http://etherx.jabber.org/streams"><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms><auth xmlns="http://jabber.org/features/iq-auth"/></stream:features>

17:22:43.086  -[XMPPManager xmppStreamDidConnect:] [Line 198] xmpp连接成功, 正在授权......

17:22:43.087  ③、SEND: <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN"> APINT4ANBTYg6IJ </auth>
<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN"> APINT4ANBTYg6IJ </auth><resource>iOS_V1.0</resource>

17:22:43.534  RECV: <success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>

17:22:43.534  ④、SEND: <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' to='192.168.1.1'>

17:22:43.870  RECV: <stream:features xmlns:stream="http://etherx.jabber.org/streams"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></stream:features>

17:22:43.871  ⑤、SEND: <iq type="set" id="123BF55A-C6FC-4AA8-A922-E1B869D6D731"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>iOS_V1.0</resource></bind></iq>

17:22:44.261  RECV: <iq xmlns="jabber:client" type="result" id="123BF55A-C6FC-4AA8-A922-E1B869D6D731" to="192.168.1.1/isaw95526287f"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>1024@192.168.1.1/iOS_V1.0</jid></bind></iq>

17:22:44.262  ⑥、SEND: <iq type="set" id="80BA9B44-1247-4219-9F54-0C1E63F424A0"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>

17:22:44.669  RECV: <iq xmlns="jabber:client" type="result" id="80BA9B44-1247-4219-9F54-0C1E63F424A0" to="1024@192.168.1.1/iOS_V1.0"/>

17:22:44.670  -[XMPPManager xmppStreamDidAuthenticate:] [Line 208] xmpp授权成功。

看这些数据包也发现不了什么问题,但是当产品在线上运行了很久,加上用户量达到一定的规模之后,通过数据监控发现, XMPP 连接成功率并不高。于是技术部几个同事开会讨论如何把这个指标提高。经过前后端联调发现,建立连接时客户端并不需要发送那么多条数据包,有几个操作没必要在客户端做,在服务器上操作会更方便,还省了些带宽和降低服务器压力,客户端每发一条数据都会给服务器增加一些压力。

于是一个新名词就出现了,我们把这次优化的登录叫做 快速登录 。主要的做法是:

  • 把①去掉,这个数据包貌似只是为了探测网络的,并没有什么用
  • ②中的 stream:stream 包是必须的,为了区别旧版本,多加一个字段 v=‘1.0’
  • ③中的 auth 非常重要,是用于鉴权的。这里做一些修改,把 resource 子节点作为 auth 节点的属性,并且为了兼容旧版本,这里的资源需要和旧版不一样,服务器端根据这个来判断新旧版本,把resource的值改为iOS_FL_V1.0
  • 把④去掉
  • ⑤和⑥这个步骤都只需要在服务器上做操作

修改之后,整体登录的收发数据包看起来像这样:

11:52:24:022 SEND: <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xml:lang='en' v='1.0' to='192.168.1.1'>

11:52:24:030 RECV: <stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" v="1.0" from="192.168.1.1" id="m5xmx2f493125" stream1:lang="en" version="1.0"/>

11:52:24:031 RECV: <stream:features xmlns:stream="http://etherx.jabber.org/streams"><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>

11:52:24:044 SEND: <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN" res="iOS_FL_V1.0">APINT4ANBTYg6IJ</auth>

11:52:24:054 RECV: <success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>

客户端只发送了2条数据包即可实现快速登录,是不是简洁了很多。如果客户端需要发送很多数据包才能登录,这期间也许因为网络的原因导致某条数据包没发出去,或者服务器返回的某条数据包无法收到,都会导致无法登录。而 快速登录 就是为了减少不必要的数据包,增加登录的成功率。

下面讲一下如何修改源码来达到快速登录。当然还是最重要的那个类 XMPPStream ,根据上面的做法一步步来:

  • 找到 - (void)sendOpeningNegotiation; 这个方法的实现,把

    if (![self didStartNegotiation])

    {

    // TCP connection was just opened - We need to include the opening XML stanza

    NSString *s1 = @"<?xml version='1.0'?>";

    NSData *outgoingData = [s1 dataUsingEncoding:NSUTF8StringEncoding];
      
      XMPPLogSend(@"SEND: %@", s1);
      numberOfBytesSent += [outgoingData length];
      
      [asyncSocket writeData:outgoingData
                 withTimeout:TIMEOUT_XMPP_WRITE
                         tag:TAG_XMPP_WRITE_START];
      
      [self setDidStartNegotiation:YES];
    }

这段代码 注释

  • 同样 - (void)sendOpeningNegotiation; 这个方法,

    if (myJID_setByClient)

    {

    // 修改这里

    s2 = [NSString stringWithFormat:"<stream:stream xmlns='%@' xmlns:stream='%@' version='1.0' xml:lang='en' v='1.0' to='%@'>", xmlns, xmlns_stream, [myJID_setByClient domain]];

    }

    else if ([hostName length] > 0)

    {

    temp = @"<stream:stream xmlns='%@' xmlns:stream='%@' version='1.0' to='%@'>";

    s2 = [NSString stringWithFormat:temp, xmlns, xmlns_stream, hostName];

    }

    else

    {

    temp = @"<stream:stream xmlns='%@' xmlns:stream='%@' version='1.0'>";

    s2 = [NSString stringWithFormat:temp, xmlns, xmlns_stream];

    }

  • 修改 auth 包的内容稍微复杂一些,当 Socket 连接上了之后,会在 - (void)xmppStreamDidConnect:(XMPPStream *)sender; 这个代理回调里面调用 - (BOOL)authenticateWithPassword:(NSString *)inPassword error:(NSError **)errPtr; 进行鉴权登录。跟着这个方法进去,我们app中登录会走 else if ([self supportsPlainAuthentication]) ,进入 XMPPPlainAuthentication 这个类,需要修改 auth 包的内容,找到 - (BOOL)start:(NSError **)errPtr; 这个方法,修改如下:

    ......

    NSXMLElement *auth = [NSXMLElement elementWithName:@"auth" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];

    [auth addAttributeWithName:@"mechanism" stringValue:@"PLAIN"];

    [auth addAttributeWithName:@"res" stringValue:@"iOS_FL_V1.0"];

    [auth setStringValue:base64];

    ......

  • 当客户端发送了 auth 包之后,如果成功了,服务器端则会返回 success 包,在 XMPPStream 类的 - (void)handleAuth:(NSXMLElement *)authResponse; 中处理。这个方法的实现有些怪异,明明鉴权成功了,为啥还要发 stream 包,经过与后端联调发现没有必要再发了,于是做了下判断,把 shouldRenegotiate 这个变量写死为 NO 即可,这样就不会发了

通过快速登录,客户端与服务器之间的数据包传送数量少了,经过我们线上几百万用户的使用情况上看,效果还是很明显的,没有出现什么问题,大大的提升了登录成功率。

XMPP 连接登录,一直以来坑比较的多,只有在用户量达到一定规模才会显现出来,曾面试过一些人,他们也都有了解 XMPP ,做过相关的项目,然而问其一些关于连接的问题时,基本上说没有遇到什么问题。这其实不是他们的问题。作为一款IM产品,如果不能正常的聊天,如何谈得上是IM呢。

分享给小伙伴们:
本文标签: iOSXMPP

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号