Tigase8.0 源代码分析:二、MUC源码分析

发布时间: 5年前 (2020-04-14)浏览: 762评论: 0

Tigase8.0 源代码分析:二、MUC源码分析

原文链接: https://www.cnblogs.com/eyecool/p/10451981.html

XMPP在其XEP-0045扩展中定义了一个用于多用户文本会议(群聊)的协议,类似于聊天室、QQ群等。由于它作为一个标准协议在定义模型上力求完备,涵盖了现实中的绝大部分IM产品模型,而现实中的IM产品基本都只实现了XMPP定义的模型中的一个子集。


XMPP定义的一些基本概念:

房间:房间的JID标识 <room@service> (例如, <jdev@conference.jabber.org>), 这里 "room" 是房间的名称而 "service" 是多用户聊天服务运行所在的主机名

房客:房客的JID标识<room@service/nick>,nick是房客在房间的昵称

岗位:表达了用户和房间的长期关系。XMPP定义的岗位有:所有者(owner)、管理者(admin)、成员(member)、排斥者(outcast)

角色:表达了用户和房间的临时联系,它只存在与一次访问期间。XMPP定义的角色有:主持人(moderator)、与会者(paticipant)、游客(visitor)

有关岗位、角色及其权限详细描述,参考协议规范描述(角色、岗位和权限)

 

pom.xml里加入 依赖引用

1
2
3
4
5
<dependency>
      <groupId>tigase</groupId>
      <artifactId>tigase-muc</artifactId>
      <version>3.0.0-SNAPSHOT</version>
  </dependency>

 

config.tdsl 配置文件中加入配置:

1
2
3
4
5
6
muc(class: tigase.muc.MUCComponent) {
  defaultRoomConfig {
       'tigase#presence_delivery_logic' = 'PREFERE_LAST'
       'muc#roomconfig_persistentroom' = 'true'
    }
}

  

 MUC源码首先从 MUCComponent 这个组件开始分析 :

1
2
3
4
5
@Bean(name = "muc", parent = Kernel.class, active = true)
@ConfigType(ConfigTypeEnum.DefaultMode)
@ClusterModeRequired(active = false)
public class MUCComponent
        extends AbstractKernelBasedComponent {}在

 在前一节已说明怎么扫描加载相关的Class,类似这里,加载Kernel.class的时候,会把parent=Kernel.class的Bean全部加载到容器中

MUCComponent 也实现了RegistrarBean,在初始化的时候,会被调用 public void register(Kernel kernel) {} 函数进行注册工作

1
2
3
public abstract class AbstractKernelBasedComponent
        extends AbstractMessageReceiver
        implements XMPPService, DisableDisco, RegistrarBean {}

  

由于用户客户端发来的MUC消息协议为:to=<room@service/nick> 例如 llooper@muc.llooper/spark ,目的地上直接表示到muc.组件上,所以MUC模块入口就是在MUCComponent 类processPacket(Packet packet) 方法上

1
2
3
4
@Override
public void processPacket(Packet packet) {
    stanzaProcessor.processPacket(packet);
}

  

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void processPacket(Packet packet) {
        if (log.isLoggable(Level.FINER)) {
            log.finer("Received: " + packet.getElement());
        }
                   //查找是否为Response模式,则由Handler来处理
            Runnable responseHandler = responseManager.getResponseHandler(packet);
 
            boolean handled;
            if (responseHandler != null) {
                handled = true;
                responseHandler.run();
            } else {
            //否则调用处理包的函数
                handled = this.process(packet);
            }
 
            if (!handled) {
                final String t = packet.getElement().getAttributeStaticStr(Packet.TYPE_ATT);
                final StanzaType type = (t == null) ? null : StanzaType.valueof(t);
 
                if (type != StanzaType.error) {
                    throw new ComponentException(Authorization.FEATURE_NOT_IMPLEMENTED);
                } else {
                    if (log.isLoggable(Level.FINER)) {
                        log.finer(packet.getElemName() + " stanza with type='error' ignored");
                    }
                }
            }
。。。。。。
}

  

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private boolean process(final Packet packet) throws ComponentException, TigaseStringprepException {
    boolean handled = false;
    if (log.isLoggable(Level.FINER)) {
        log.finest("Processing packet: " + packet.toString());
    }
            //找到匹配的Module来处理
    for (Module module : this.modules) {
        if (module.canHandle(packet)) {
            handled = true;
            if (log.isLoggable(Level.FINER)) {
                log.finer("Handled by module " + module.getClass());
            }
            module.process(packet);
            if (log.isLoggable(Level.FINEST)) {
                log.finest("Finished " + module.getClass());
            }
        }
    }
    return handled;
}

 例如MUC中实现的Module有如下:

 

这里的逻辑比较简单,也就是根据包的节类型等信息,来匹配感兴趣的处理Module,来进行处理

 再来看下 module.canHandle(packet) 是怎么样找到匹配的Module来处理的:

1
2
3
4
default boolean canHandle(Packet packet) {
    Criteria criteria = getModuleCriteria();
    return criteria != null && criteria.match(packet.getElement());
}

  

private final String[] names;
private final String[] xmlns;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<br>@Override
    public boolean ElemPathCriteria.match(Element element) {
          //判断<iq> packet的名字如iq是否和本module中定义name一致
        boolean match = element.getName().equals(names[0]);
        if (match && xmlns[0] != null) {<br>               //并且判断命名空间是否一致
            match &= xmlns[0].equals(element.getXMLNS());
        }
 
        Element child = element;
        int i = 1;<br>          //例如packet <iq><set>xxx</set></iq> 则name为数组{"iq","set"},前面判断了iq,这里判断set是否一致,如果还存在子节点,则依次比较是否一致
        for (; i < names.length; i++) {
            String n = names[i];
            String x = xmlns[i];
               //
            child = child.getChildStaticStr(n, x);
 
            match &= child != null;
 
            if (!match) {
                return match;
            }
 
        }
 
        // TODO Auto-generated method stub
        return match;
    }

  

例如针对出muc的 <presence> 由 PresenceModuleImpl 进行业务逻辑处理

1
2
3
4
5
6
7
8
9
@Bean(name = PresenceModuleImpl.ID, parent = MUCComponent.class, active = true)
public class PresenceModuleImpl
        extends AbstractMucModule
        implements PresenceModule {
 
    protected static final Logger log = Logger.getLogger(PresenceModule.class.getName());
    private static final Criteria CRIT = ElementCriteria.name("presence");
 
}


标签:

上一篇: Tigase8.0 源代码分析:四、集群配置篇
下一篇: Tigase8.0 源代码分析: 三、消息流转篇

相关文章暂无相关
评论列表暂无评论
发表评论
验证码

«   2024年4月   »
1234567
891011121314
15161718192021
22232425262728
2930
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
搜索
最新留言
    文章归档
    网站收藏
    友情链接
    • RainbowSoft Studio Z-Blog
    • 订阅本站的 RSS 2.0 新闻聚合
    ︿
    Top