iOS—XMPP快速登录

上一篇文章中,当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呢。

吃午饭了…

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,277评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,777评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,946评论 0 245
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,271评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,636评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,767评论 1 221
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,989评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,733评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,457评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,674评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,155评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,518评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,160评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,114评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,898评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,822评论 2 280
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,705评论 2 273

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,103评论 18 139
  • 关于XMPP最权威的讲解:http://www.jabbercn.org/RFC3920(这个才是最权威的,下面文...
    随风飘荡的小逗逼阅读 1,425评论 1 5
  • XMPP简介 XMPP协议简介 XMPP协议(Extensible Messaging and PresenceP...
    不规则先生阅读 6,188评论 2 31
  • 最近一朋友正准备跳槽,就从各处搜索整理一些基础,便于朋友复习,也便于自己复习查看. 1. 回答person的ret...
    smile丽语阅读 1,663评论 0 7
  • 正如一位,减肥要放弃的朋友说的,不减啦,吃饱了再减吧。 他说的很对,吃饱之后呢才能减肥,饿是不能成功的。 因为饥饿...
    高田GT阅读 318评论 0 1