//在看本节之前一定要先了解下xmpp协议,建议仔细看下 tigase源码分析6:了解xmpp协议//在看下面代码之前,要知道,每一个用户User通过某一资源连接到服务器时,//每一个User在不同的资源上登录都各对应着一个IOService,//每一个资源对应着一个XMPPResourceConnection,//同一个用户User多个XMPPResourceConnection可能共同引用着同一个XMPPSession
public IOService<?> IOService.call() throws IOException {
......
//当socket有数据要处理的时候,进行解析
processSocketData();
if ((receivedPackets() > 0) && (serviceListener != null)) {serviceListener.packetsReady(this);
}    // end of if (receivedPackets.size() > 0)
.....
}protected void XMPPIOService.processSocketData() throws IOException {
.....  //解析socket数据parser.parse(domHandler, data, 0, data.length);
....}public final void SimpleParser.parse(SimpleHandler handler, char[] data, int off, int len) {
.....//遇到<stream:stream>
handler.startElement(parser_state.element_name, null, null);
......
}public void XMPPDomBuilderHandler.startElement(StringBuilder name, StringBuilder[] attr_names,StringBuilder[] attr_values) {
......//服务端也打开一个对应的<stream:stream>service.xmppStreamOpened(attribs);
........
}protected void XMPPIOService.xmppStreamOpened(Map<String, String> attribs) {
...
String response = serviceListener.xmppStreamOpened(this, attribs);
...
}public String ClientConnectionManager.xmppStreamOpened(XMPPIOService<Object> serv,Map<String,String> attribs) {
....if (id == null) {
//生成一些属性id = UUID.randomUUID().toString();serv.getSessionData().put(IOService.SESSION_ID_KEY, id);serv.setXMLNS(XMLNS);serv.getSessionData().put(IOService.HOSTNAME_KEY, hostname);serv.setDataReceiver(JID.jidInstanceNS(routings.computeRouting(hostname)));String streamOpenData = prepareStreamOpen(serv, id, hostname);
//给客户端回一个<stream:stream>告诉他服务端也打开了streamwriteRawData(serv, streamOpenData);
//生成一个新的iq请求packet,主要作用是通知打开session connection
Packet streamOpen = Command.STREAM_OPENED.getPacket(serv.getConnectionId(), serv.getDataReceiver(), StanzaType.set, this.newPacketId("c2s-"), Command.DataType.submit);
//设置一些属性
Command.addFieldValue(streamOpen, "session-id", id);
Command.addFieldValue(streamOpen, "hostname", hostname);
Command.addFieldValue(streamOpen, "xml:lang", lang);
//把刚新生成的packet投递到MessageRouter去路由到目的地
addOutPacketWithTimeout(streamOpen, startedHandler, 45l, TimeUnit.SECONDS);
}
//这是上面生成的一个iq command packet
<iq from="c2s@dell-pc/192.168.3.10_5222_192.168.3.10_53597" type="set" id="c2s--c2s5"to="sess-man@dell-pc"><command node="STREAM_OPENED" xmlns="http://jabber.org/protocol/commands"><x type="submit" xmlns="jabber:x:data"><field var="session-id"><value>4f0e26c9-aeac-442e-aaea-84c788ab73d2</value></field><field var="hostname"><value>192.168.3.10</value></field><field var="xml:lang"><value>en</value></field></x></command>
</iq>
//packet被路由到SessionManager后,由继承的QueueListener线程进行处理
QueueListener为内部类,所以他能访问外部类的方法public void QueueListener.run() {.........
//由于属于command,所以进入以下代码块if (packet.isCommand() && (packet.getStanzaTo() != null)&& compName.equals(packet.getStanzaTo().getLocalpart())&& isLocalDomain(packet.getStanzaTo().getDomain())) {processed = processScriptCommand(packet, results);if (processed) {Packet result = null;while ((result = results.poll()) != null) {addOutPacket(result);}}}if (!processed && ((packet = filterPacket(packet, incoming_filters)) != null)) {processPacket(packet);//此方法是执行真正的实现类的方法,}
.........
}//再执行到processCommand
public void SessionManager.processPacket(final Packet packet) {//为command,则执行processCommandif (packet.isCommand() && processCommand(packet)) {packet.processedBy("SessionManager");// No more processing is needed for command packetreturn;}    // end of if (pc.isCommand())XMPPResourceConnection conn = getXMPPResourceConnection(packet);...processPacket(packet, conn);}//private SessionOpenProc   sessionOpenProc  = null;
protected boolean SessionManager.processCommand(Packet pc) {
....
Iq      iqc= (Iq) pc;
XMPPResourceConnection connection = connectionsByFrom.get(iqc.getFrom());switch (iqc.getCommand()) {
....
case STREAM_OPENED : {      //获取session processor的处理线程集ProcessingThreads<ProcessorWorkerThread> pt = workerThreads.get(sessionOpenProc.id());if (pt == null) {pt = workerThreads.get(defPluginsThreadsPool);}//把packet交给session processor插件来进行下一步的处理,它是运行在单独的线程上的。pt.addItem(sessionOpenProc, iqc, connection);processing_result = true;}public void SessionOpenProc.process(Packet packet, XMPPResourceConnection session,NonAuthUserRepository repo, Queue<Packet> results, Map<String, Object> settings)throws XMPPException {
...//每一个客户端都会生成一个对应的XMPPResourceConnection 资源连接器,它持有XMPPSession的引用conn = createUserSession(packet.getFrom(), hostname);conn.setSessionId(Command.getFieldValue(packet, "session-id"));conn.setDefLang(Command.getFieldValue(packet, "xml:lang"));
..//回一个应答packetfastAddOutPacket(packet.okResult((String) null, 0));
}protected XMPPResourceConnection SessionManager.createUserSession(JID conn_id, String domain) throws TigaseStringprepException {XMPPResourceConnection connection = new XMPPResourceConnection(conn_id,user_repository, auth_repository, this);
//放进SessionManager.connectionsByFrom中,以便在processPacket(..)中得到相关packet的connconnectionsByFrom.put(conn_id, connection);return connection;}
<iq from="c2s@dell-pc/192.168.3.10_5222_192.168.3.10_64739" type="get" id="ead3ca01-9469-414f-9f0a-a7a52c164a72" to="sess-man@dell-pc"><command node="GETFEATURES" xmlns="http://jabber.org/protocol/commands"/>
</iq><iq from="sess-man@dell-pc" type="result" id="ead3ca01-9469-414f-9f0a-a7a52c164a72" to="c2s@dell-pc/192.168.3.10_5222_192.168.3.10_64739"><command node="GETFEATURES" xmlns="http://jabber.org/protocol/commands"><ver xmlns="urn:xmpp:features:rosterver"/></command>
</iq><iq from="sess-man@dell-pc" type="result" id="73c47636-536c-4f6b-a306-8ff912313497" to="c2s@dell-pc/192.168.3.10_5222_192.168.3.10_49257"><command node="GETFEATURES" xmlns="http://jabber.org/protocol/commands"><ver xmlns="urn:xmpp:features:rosterver"/><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism><mechanism>ANONYMOUS</mechanism></mechanisms><register xmlns="http://jabber.org/features/iq-register"/><compression xmlns="http://jabber.org/features/compress"><method>zlib</method></compression><auth xmlns="http://jabber.org/features/iq-auth"/></command>
</iq>
<!-- 客户端发来认证请求-->
<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">ADc4OTQ1NgA3ODk0NTY=</auth>
<!--服务端产生的对应的iq 作为流转命令,不返回给客户端的-->
<iq from="sess-man@dell-pc" type="set" id="tig2" to="c2s@dell-pc/192.168.3.10_5222_192.168.3.10_49257"><command node="USER_LOGIN" xmlns="http://jabber.org/protocol/commands"><x type="submit" xmlns="jabber:x:data"><field var="user-jid"><value>789456@192.168.3.10</value></field></x></command></iq>
<!-- 认证成功返回给客户端的-->
<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>

当服务端接收到<auth>认证请求后,执行SaslAuth 认证处理通过以后,则生成用户对应的XMPPSession

public void SaslAuth.process(final Packet packet, final XMPPResourceConnection session,final NonAuthUserRepository repo, final Queue<Packet> results, final Map<String, Object> settings) {
.....
if (ss.isComplete() && (ss.getAuthorizationID() != null)) {.....
//检查有没有XMPPSession,没有则创建一个新的绑定相关的属性session.authorizeJID(jid, anonymous);
//返回一个<success>成功标志的packet
results.offer(packet.swapFromTo(createReply(ElementType.success,challengeData),null,null));}public void XMPPResourceConnection.authorizeJID(BareJID jid, boolean anonymous) {authState    = Authorization.AUTHORIZED;is_anonymous = anonymous;loginHandler.handleLogin(jid, this);login();}public void SessionManager.handleLogin(BareJID userId, XMPPResourceConnection conn) {registerNewSession(userId, conn);}protected void SessionManager.registerNewSession(BareJID userId, XMPPResourceConnection conn) {.....//一个用户在不同一资源上登录,共用这个xmppsessionXMPPSession session = sessionsByNodeId.get(userId);if (session == null) {session = new XMPPSession(userId.getLocalpart());sessionsByNodeId.put(userId, session);....} else {// Check all other connections whether they are still alive....//检查session.activeResources中其它的XMPPResourceConnection是否有效的List<XMPPResourceConnection> connections = session.getActiveResources();.........}//session和connection相互关联起来,双方都持有对方的引用session.addResourceConnection(conn);if ((!"USER_STATUS".equals(conn.getSessionId())) &&!conn.isServerSession() &&!conn.isTmpSession()) {//生成一个USER_LOGIN 事件的命令packet,好让生成的jid关联到用户的IOService上      Packet user_login_cmd = Command.USER_LOGIN.getPacket(getComponentId(), conn.getConnectionId(), StanzaType.set, conn.nextStanzaId(), Command.DataType.submit);Command.addFieldValue(user_login_cmd, "user-jid", userId.toString());ddOutPacket(user_login_cmd);
}.....}
protected void ClientConnectionManager.processCommand(Packet packet) {XMPPIOService<Object> serv = getXMPPIOService(packet);
switch (iqc.getCommand()) {
case GETFEATURES :
..
break;
case USER_LOGIN :String jid = Command.getFieldValue(iqc, "user-jid");.....serv.setUserJid(jid);
}

下一步,客户端会再次打开流,服务端也会打开一个新的流,这时客户端会请求绑定资源名称

<iq type="set" id="bind_1">
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
<resource>DELL-PC</resource>
</bind>
</iq>

服务端 BindResource (绑定插件)会处理这个packet,所以只要明白,tigase都是基于插件和组件组合来处理请求的,不同的插件来处理不同的请求,我们也可以开发相关的插件来处理我们自定义的请求了。

tigase源码分析7:用户连接登录流程相关推荐

  1. Linux内核源码分析—从用户空间复制数据到内核空间

    Linux内核源码分析-从用户空间复制数据到内核空间 本文主要参考<深入理解Linux内核>,结合2.6.11.1版的内核代码,分析从用户空间复制数据到内核空间函数. 1.不描述内核同步. ...

  2. HBase源码分析之HRegion上compact流程分析(三)

    在<HBase源码分析之HRegion上compact流程分析(二)>一文中,我们没有讲解真正执行合并的CompactionContext的compact()方法.现在我们来分析下它的具体 ...

  3. Spark源码分析之Sort-Based Shuffle读写流程

    一 概述 我们知道Spark Shuffle机制总共有三种: # 未优化的Hash Shuffle:每一个ShuffleMapTask都会为每一个ReducerTask创建一个单独的文件,总的文件数是 ...

  4. 【SA8295P 源码分析】18 - Camera Bringup 流程 及 源码分析

    [SA8295P 源码分析]18 - Camera Bringup 流程 及 源码分析 一.Camera Bringup 流程 1.1 CameraConfigSA8295.c 配置文件解析 1.2 ...

  5. socket.io-client源码分析——建立socket连接

    介绍 socket.io是一种用于服务端和客户端的双向通信的js库,提供了长轮询和websocket这两种实现方式socket.io-client是其在客户端的实现.socket.io-client通 ...

  6. Nginx源码分析:master/worker工作流程概述

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> Nginx的master与worker工作模式 在生成环境中的Nginx启动模式基本都是以m ...

  7. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

  8. Android Instrumentation源码分析(附Activity启动流程)

    转载请注明出处:http://blog.csdn.net/ahence/article/details/54959235 Instrumentation概念 官方说明 Instrumentation类 ...

  9. Spark详解(七):SparkContext源码分析以及整体作业提交流程

    1. SparkContext源码分析 在任何Spark程序中,必须要创建一个SparkContext,在SparkContext中,最主要的就是创建了TaskScheduler和DAGSchedul ...

最新文章

  1. 【Docker】Ubuntu18.04国内源安装Docker-准备工作(一)
  2. CentOS7的node.js安装
  3. 电信用户流失预测案例(2)(特征工程)
  4. Grand Central Dispatch(GCD)
  5. java recoed replay_easymock教程-record-replay-verify模型
  6. Feature Scaling(特征缩放)的一些方法和使用选择
  7. HTTP 遭 Google 抛弃,开发者该如何应对?
  8. 怎么用鼠标选中java中table的某一行_为什么同事的工作效率那么高?学会这些鼠标双击技巧,你也可以的...
  9. C语言EasyX详解(小球碰撞)
  10. ExtendSim 10.0.8发布于 2021年8月7日
  11. ei加声调怎么加_微商怎么加好友找客源实操篇
  12. 阿里icon图标库使用说明
  13. matlab:快速傅里叶(反)变换 FFTIFFT
  14. 如何使用python将数据写入txt文件
  15. 《个人信息保护法》自2021年11月1日正式实施
  16. UC刘兰奇极速版制动刷金币
  17. LeetCode刷题攻略
  18. 当笑神姜涛遇上尼古拉斯赵四,低俗加上恶俗的结果是否就是封杀?
  19. 717 1比特与2比特字符
  20. 华为OD机试真题 Python 实现【检测热点字符】【2023 Q1 | 100分】

热门文章

  1. 基于layui的省市区三级联动
  2. 盘点那些令人惊艳的黑科技
  3. 巴斯夫Basonat_HI100ap固化剂TDS产品说明书
  4. 大学物理学第四版课后习题答案(赵近芳)上册
  5. Gromacsg_mmpbsa安装教程
  6. 获取微信通讯录php,php微信公众号开发之通讯录查询
  7. 时间管理系统 Java Swing ,计时器,类似于番茄时间功能
  8. 查询读者的借阅信息mysql_MySQL查询练习2
  9. TechBits | TCP 使用 WireShark 进行抓包
  10. 常用的光电模块SFP、QSFP等解析