tigase的message流转

写在前面:之前是做openfire,现转弄tigase,对tigase管道流的作风还不是很了解。简单整理了下Message的流程,不妥之处,还请各位指正。
该地址有些tigae官网的翻译文章:https://chutianxing.wordpress.com/tag/tigase/page/2/  可能需要翻墙
 
建议学习tigae流程时多看日志
init.properties文件里的日志开关 --debug=server (表示记录tigase.server包里的日志),多个包用逗号分隔,例如:--debug=server,xmpp,net
1. tigase服务端接收处理客户端发送的message的类都在tigase.io和tigase.net。tigase.io更底层,接收网络数据,由tigase.net处理解析成xml对象,并将packet放到接收队列receivedPackets中。

此时的packet :packetFrom=null,packetTo=null

(packet的属性里面有packetFrom、packetTo 这是组件间传递用的地址,而stanzaFrom、stanzaTo是客户端或者muc的地址)

@Override
	public IOService
    
     call() throws IOException {
		writeData(null);

		boolean readLock = true;

		if (stopping) {
			stop();
		} else {
			readLock = readInProgress.tryLock();
			if (readLock) {
				try {
				    //解析成XML并将packet放入receivedPackets接收队列
					processSocketData();
					if ((receivedPackets() > 0) && (serviceListener != null)) {
					    //给packet设置一些属性,并放到被QueueListener监听的队列中
						serviceListener.packetsReady(this);
					}    // end of if (receivedPackets.size() > 0)
				} finally {
					readInProgress.unlock();
				}
			}
		}

		return readLock
				? this
				: null;
	}
 

其中serviceListener.packetsReady(this) ----> ConnectionManager.packetsReady

----> writePacketsToSocket(serv, processSocketData(serv));

---->ClientConnectionManager.processSocketData(XMPPIOService<Object>serv),

最终在processSocketData方法中设置packet的一些属性,通过addOutPacket(Packet packet)方法将packet放入out_queues队列。

@Override
	public Queue
     
     
      
       processSocketData(XMPPIOService
      
       serv) {

		JID id = serv.getConnectionId();

		Packet p = null;

		while ((p = serv.getReceivedPackets().poll()) != null) {
			...

			if (p.getAttributeStaticStr(Packet.XMLNS_ATT) == null) {
				p.setXMLNS(XMLNS);
				.....
			}

			p.setPacketFrom(id);

			JID receiver = serv.getDataReceiver();

			if (receiver != null) {
				p.setPacketTo(serv.getDataReceiver());
				addOutPacket(p);
			} else {
			    ...
			}
			// TODO: Implement sending 'req' attributes by the server too
		}    // end of while ()

		return null;
	}


将 connectionId : c2s@user-20140808fs/127.0.0.1_5222_127.0.0.1_56591 设置为packetFrom,组件sess_man的jidsess-man@user-20140808fs设置为packetTo,以及设置XMLNS=jabber:client。

此时packet: from=c2s@user-20140808fs/127.0.0.1_5222_127.0.0.1_54883 ,to=sess-man@user-20140808fs

2. packet 的进出都被AbstractMessageReceiver的内部类QueueListener监听,并加入到 queue队列中,run()方法从queue队列中取出packet,根据type分发。

private class QueueListener	extends Thread {
		...
		private PriorityQueueAbstract
           
           
            
             queue;
		...
		@Override
		public void run() {
		    ...

			while (!threadStopped) {
				try {

					// 现在处理下一个等待的packet
					packet = queue.take();
					++packetCounter;

					switch (type) {
					case IN_QUEUE :
						...
						if (task != null) {
							task.handleResponse(packet);
						} else {
						    ...
						    
							if (!processed && ((packet = filterPacket(packet, incoming_filters)) !=
									null)) {
								processPacket(packet);
							}
							...
						}

						break;

					case OUT_QUEUE :

						if ((packet = filterPacket(packet, outgoing_filters)) != null) {
							processOutPacket(packet);
						}

						break;

					default :
						log.log(Level.SEVERE, "Unknown queue element type: {0}", type);

						break;
					}    // end of switch (qel.type)
				} catch (InterruptedException e) {
                    ...
				} catch (Exception e) {
					...
				}    // end of try-catch
			}      // end of while (! threadStopped)
		}
	}

           
           
3. packet 由case OUT_QUEUE分支 的 processOutPacket(packet) 分发,交给上层组件MessageRouter处理,MessageRouter把packet塞到in_queues
packet被塞到out_queue和in_queue的动作都被QueueListener监听,并加上type放到自己的queue中,等待处理。

4. packet 被从queue取出,经CASE IN_QUEUE分支的processPacket(packet)分发。

MessageRouter.processPacket(Packet packet)部分代码如下:

@Override
	public void processPacket(Packet packet) {
	
        //getTo 方法取pcketTo的值,若为空,则取stanzaTo(
           
           中的to)
		if (packet.getTo() == null) {
			return;
		}    

        ...
        //匹配分发组件
		ServerComponent comp = getLocalComponent(packet.getTo());

		if (comp != null) {

			Queue
           
           
            
             results = new ArrayDeque
            
            
             
             ();

			if (comp == this) {
				processPacketMR(packet, results);
			} else {
			    ...
				comp.processPacket(packet, results);
			}
            ...
			return;
		}
		...

	}

            
            
           
           

根据to=sess-man@user-20140808fs匹配, Packet will be processed by:sess-man@user-20140808fs,由AbstractMessageReceiver的非阻塞性方法addPacketNB(Packet packet)加入到in_queues。

5. 组件sess-man@user-20140808fs从queue拿到packet由SessionManager.processPacket(final Packet packet)处理。
@Override
	public void processPacket(final Packet packet) {
		if (log.isLoggable(Level.FINEST)) {
			log.log(Level.FINEST, "Received packet: {0}", packet.toStringSecure());
		}
	    ...
	    
		XMPPResourceConnection conn = getXMPPResourceConnection(packet);
		...
		
		processPacket(packet, conn);
	}
其中getXMPPResourceConnection(packet)方法,返回conn。packet再由SessionManager.processPacket(packet, conn)处理。
代码:
protected void processPacket(Packet packet, XMPPResourceConnection conn) {

		...
		packet.setPacketTo(getComponentId());
        ...

		if (!stop) {
		    //授权匹配的processor处理packet
			walk(packet, conn);
			try {
				if ((conn != null) && conn.getConnectionId().equals(packet.getPacketFrom())) {
					handleLocalPacket(packet, conn);
				}
			} catch (NoConnectionIdException ex) {
                ...
			}
		}
		
		...
	}
packetTo被设置为组件ID( sess-man@user-20140808fs),其值原先也是这个。
其中walk(packet, conn)方法,匹配处理器(授权)。对于message,此处匹配到的processor是amp和message-carbons,message-carbons没有怎么处理,主要是amp在处理,packet被塞amp的队列中等待处理。
private void walk(final Packet packet, final XMPPResourceConnection connection) {

		for (XMPPProcessorIfc proc_t : processors.values()) {
			XMPPProcessorIfc processor = proc_t;
			//根据element和xmlns,授权匹配成功的processor
			Authorization    result    = processor.canHandle(packet, connection);

			if (result == Authorization.AUTHORIZED) {
        	    ....
            
				ProcessingThreads
            
            
             
              pt = workerThreads.get(processor.id());

				if (pt == null) {
					pt = workerThreads.get(defPluginsThreadsPool);
				}
				//packet 放到(addItem)授权了的processor的队列
				if (pt.addItem(processor, packet, connection)) {
					packet.processedBy(processor.id());
				} else {

					...
				}
			} else {
				...
			}
		}   
	}

            
            
6. WorkerThread.run() 从队列中取出packet,由SessionManager.process(QueueItem item)给amp处理。

SessionManager.pocess(QueueItem item) 如下:

@Override
	public void process(QueueItem item) {
	    
		XMPPProcessorIfc processor = item.getProcessor();

		try {
		    //由授权的 processor 处理 packet
			processor.process(item.getPacket(), item.getConn(), naUserRepository,
					local_results, plugin_config.get(processor.id()));
			if (item.getConn() != null) {
				setPermissions(item.getConn(), local_results);
			}
			addOutPackets(item.getPacket(), item.getConn(), local_results);
		} catch (PacketErrorTypeException e) {
			...
		} catch (XMPPException e) {
			...
		}
	}
其中processor.process( , , , ,)------> MessageAmp.process(),如下:
@Override
	public void process(Packet packet, XMPPResourceConnection session,
			NonAuthUserRepository repo, Queue
           
           
            
             results, Map
            
            
             
              settings)
					throws XMPPException {
		if (packet.getElemName() == "presence") {
			...
			
		} else {
			Element amp = packet.getElement().getChild("amp", XMLNS);

			if ((amp == null) || (amp.getAttributeStaticStr("status") != null)) {
				messageProcessor.process(packet, session, repo, results, settings);
			} else {
				...
		}
	}

            
            
           
           

其中messageProcessor.process(, , , ,) --------> Message.process(),如下:

@Override
	public void process(Packet packet, XMPPResourceConnection session,
			NonAuthUserRepository repo, Queue
           
           
            
             results, Map
            
            
             
              settings)
					throws XMPPException {

		...
		try {
			...

			// Remember to cut the resource part off before comparing JIDs
			id = (packet.getStanzaFrom() != null)
					? packet.getStanzaFrom().getBareJID()
					: null;

			// Checking if this is maybe packet FROM the client
			if (session.isUserId(id)) {

				// This is a packet FROM this client, the simplest action is
				// to forward it to is't destination:
				// Simple clone the XML element and....
				// ... putting it to results queue is enough
				results.offer(packet.copyElementOnly());

				return;
			}

			
		} catch (NotAuthorizedException e) {
			...
		}    // end of try-catch
	}

            
            
           
           
检查stanzaFfrom与session匹配通过后,将packet.copyElementOnly()放到results中,作后续投递,原来的packet 就丢弃了。
此时投递的packet : packetFrom=null,packetTo=null
packet在SessionManager.addOutPacket(Packet packet)中判断packetFrom是否为空,为空则将其设置为ComponentId(此处为 sess-man@user-20140808fs),然后调用父类(AbstractMessageReceiver.java) 的addOutPacket(packet)方法塞到 out_queue 队列中。
此时packet:: packetFrom= sess-man@user-20140808fs ,packetTo=mull
7. 如同步骤3,上层组件MessageRouter处理,把packet塞到in_queues.
8. 如同步骤4,不同的是 PacketTo为空,packet.getTo()的返回值是stanzaTo。

getLocalComponent(packet.getTo());方法根据stanzaTo与compId、comp name、Component都匹配不到。

此时packet会给组件SessionManager处理,Packet will be processed by: sess-man@user-20140808fs,由AbstractMessageReceiver的非阻塞性方法addPacketNB(Packet packet)加入到in_queues。

9. 如同步骤5,

不同的是在getXMPPResourceConnection(packet)方法中,

conn = connectionsByFrom.get(from)返回值是null,所以是根据stanzaTo取获取接收方的session,返回接收方连接的Connection。

protected XMPPResourceConnection getXMPPResourceConnection(Packet p) {
		XMPPResourceConnection conn = null;
		JID                    from = p.getPacketFrom();

		if (from != null) {
			conn = connectionsByFrom.get(from);
			if (conn != null) {
				return conn;
			}
		}

		// It might be a message _to_ some user on this server
		// so let's look for established session for this user...
		JID to = p.getStanzaTo();

		if (to != null) {
			...
			conn = getResourceConnection(to);
		} else {

			...
		}    // end of else

		return conn;
	}
然后packetTo被设置为组件ID( sess-man@user-20140808fs)。
此时packet:: packetFrom = sess-man@user-20140808fs ,packetTo = sess-man@user-20140808fs
之后packet又经walk(packet, conn)方法,匹配处理器(授权),扔给amp处理。
10.如同步骤6,直到Message.process(),如下:
@Override
	public void process(Packet packet, XMPPResourceConnection session,
			NonAuthUserRepository repo, Queue
           
           
            
             results, Map
            
            
             
              settings)
					throws XMPPException {

	    ...
		try {

			// Remember to cut the resource part off before comparing JIDs
			BareJID id = (packet.getStanzaTo() != null)
					? packet.getStanzaTo().getBareJID()
					: null;

			// Checking if this is a packet TO the owner of the session
			if (session.isUserId(id)) {
				..

				// Yes this is message to 'this' client
				List
             
             
              
               conns = new ArrayList
              
              
               
               (5);

				// This is where and how we set the address of the component
				// which should rceive the result packet for the final delivery
				// to the end-user. In most cases this is a c2s or Bosh component
				// which keep the user connection.
				String resource = packet.getStanzaTo().getResource();

				if (resource == null) {

					// If the message is sent to BareJID then the message is delivered to
					// all resources
					conns.addAll(session.getActiveSessions());
				} else {

					// Otherwise only to the given resource or sent back as error.
					XMPPResourceConnection con = session.getParentSession().getResourceForResource(
							resource);

					if (con != null) {
						conns.add(con);
					}
				}

				// MessageCarbons: message cloned to all resources? why? it should be copied only
				// to resources with non negative priority!!
				
				if (conns.size() > 0) {
					for (XMPPResourceConnection con : conns) {
						Packet result = packet.copyElementOnly();

						result.setPacketTo(con.getConnectionId());

						// In most cases this might be skept, however if there is a
						// problem during packet delivery an error might be sent back
						result.setPacketFrom(packet.getTo());

						// Don't forget to add the packet to the results queue or it
						// will be lost.
						results.offer(result);
					    ...
					}
				} else {
					....
				}

				return;
			}    // end of else

			...
			
		} catch (NotAuthorizedException e) {
			...
		}    // end of try-catch
	}

              
              
             
             
            
            
           
           

检查stanzaTo与session匹配通过后,根据session拿到接收方所有的连接(可能多端登陆),然后Packet result = packet.copyElementOnly()生成新的packet(原packet丢弃了),并将packetTo设置为接收方连接的ConnectionId(例如:c2s@user-20140808fs/127.0.0.1_5222_127.0.0.1_50536),通过addOutPacket()方法塞到out_queue队列。

此时packet:packetFrom = sess-man@user-20140808fs,packetTo =c2s@user-20140808fs/127.0.0.1_5222_127.0.0.1_50536

11. 如同步骤3、4 。不同的是 根据packetTo匹配到组件 c2s@user-20140808fs
12. 组件 c2s@user-20140808fs 从queue中取出packet,分发
public void processPacket(final Packet packet) {
		
		...
		
		if (packet.isCommand() && (packet.getCommand() != Command.OTHER)) {
			...
		} else {
		    // 把packet 发送给客户端
			if (!writePacketToSocket(packet)) {

				...
				
			}
		}    // end of else
	}
ps:muc的message与此类似,一直到步骤8匹配到的分发组件是 muc@user-20140808fs,MUC组件分发的时候给每个组员生成一个packet(packetFrom=null, packetTo=null),扔到队列,接下来又是步骤6中的判断packetFrom是否为空,为空则将其设置为ComponentId()......
附草图一张:
 
潜水N久,第一次写博客。。。鞠躬下台 再见
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值