OpenFire源码学习之八:MUC用户聊天室

MUC

房间属性设置

以上属性存储在MUCPersistenceManager

private staticConcurrentHashMap<String,MUCServiceProperties> propertyMaps =newConcurrentHashMap<String,MUCServiceProperties>();

创建房间

客户端创建房间案例


第一:客户端发出查询请求

<iq id="wcCqI-57" to="room1@qqgroup.8ntmorv1ep4wgcy" type="get">
  <query xmlns="http://jabber.org/protocol/disco#info"/>
</iq>

服务器将数据包发送到托管在该服务器组件来处理。

routed = routeToComponent(jid,packet, routed);

服务器需要在内存中判断房间是否存在,其次呢,返回外部组件的配置。为确切请求子域的查询将会作出修改。如果没有被发现和使用通配符请求,然后再查询将被提出,在使用通配符这个时候。

然后检查组件是否被托管在此JVM

获取MUC组件的信息

该MUC服务将接收的域MUC的域相匹配的所有数据包服务。这意味着,例如,disco 请求应该由服务本身作出回应,而不是依赖在服务器上处理请求。

根据命名空间找到相应处理——>IQDiscoInfoHandler。

http://jabber.org/protocol/disco#info

 

寻找与所请求的实体相关的DiscoInfoProvider。
我们认为该数据包为单位的接收者的JID的主机。这是DiscoInfoProvider责任提供有关的JID的姓名信息一起用任何可能的请求节点。

所查询的房间节点不存在,按照正常的流程服务器返回错误信息

<iq type="error" id="wcCqI-57" from="room1@qqgroup.8ntmorv1ep4wgcy" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">
  <query xmlns="http://jabber.org/protocol/disco#info"/>
  <error code="404" type="cancel">
    <item-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
  </error>
</iq>

客户端第二轮发送:

<presence id="wcCqI-59" to="room1@qqgroup.8ntmorv1ep4wgcy/test2">
  <x xmlns="http://jabber.org/protocol/muc"/>
</presence>

服务器处理:

1.将用户发送的定向存在的实体

  (通知方式发送到该处理程序,当用户发送了一个指向存在的实体。如果存在的发件人是本地的(这个服务器)和目标实体不属于用户的花名册,然后发送更新派驻执导的用户注册表。)
2.广播到所有连接的资源

  (获得由XMPPAddress聊天的用户。仅返回已连接到该JVM的用户。)

服务器返回消息:

<message type="groupchat" from="room1@qqgroup.8ntmorv1ep4wgcy">
  <body>确认配置之前已锁住该房间,禁止进入。</body>
</message>

配置钱锁定房间,一面别的用创建一样的,或者申请加入这个房间

<presence id="wcCqI-59" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android" 
          from="room1@qqgroup.8ntmorv1ep4wgcy/test2">
<x xmlns="http://jabber.org/protocol/muc#user">
   <item jid="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android" 
          affiliation="owner" role="moderator"/>
         <status code="201"/>
</x>
</presence>

客户端发送IQ:

<iq id="wcCqI-60" to="room1@qqgroup.8ntmorv1ep4wgcy" type="get">
  <query xmlns="http://jabber.org/protocol/muc#owner"/>
</iq>

查询房间拥有者。

服务器返回1:

<message type="groupchat" from="room1@qqgroup.8ntmorv1ep4wgcy" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">
<body>确认配置之前已锁住该房间,禁止进入。</body>
</message>

服务器返回2:

<iq type="error" id="wcCqI-60" from="room1@qqgroup.8ntmorv1ep4wgcy" 
                         to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">
   <query xmlns="http://jabber.org/protocol/muc#owner"/>
       <error code="401" type="auth">
       <not-authorized xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
   </error>
</iq>

======================================================================

总的对话

客户端发送C2S - RECV (32671720): 

<iq id="wcCqI-61" to="room2@qqgroup.8ntmorv1ep4wgcy" type="get">
    <query xmlns="http://jabber.org/protocol/disco#info">
    </query>
</iq>

服务器返回

<iq type="error" id="wcCqI-61" 
                             from="room2@qqgroup.8ntmorv1ep4wgcy" 
                             to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">
     <query xmlns="http://jabber.org/protocol/disco#info"/>
         <error code="404" type="cancel">
             <item-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
         </error>
</iq>

以上循环两次对话,这可能由于debug超时原因,消息重复发送。

客户端发送

<presence id="wcCqI-63" to="room2@qqgroup.8ntmorv1ep4wgcy/test2">
<x xmlns="http://jabber.org/protocol/muc">
</x>
</presence>
出席消息。

服务器处理:

1.当用户发送一个 directed presence的时候将发送给directedPresenceSent()来处理。如果存在的发件人是本地的(这个服务器)和目标实体不属于用户的花名册,然后发送更新派驻执导的用户注册表。

跟踪所有指示派驻人员名册,如果服务被禁用

这里有两块内存记录消息:

private Cache<String,Collection<DirectedPresence>>directedPresencesCache;

跟踪发送指向派驻到其他实体。
在这个Cache上我们跟踪每一个 directed presence存在,无论发送者是否托管在这个JVM或其他群集节点。

另一个

private Map<String,Collection<DirectedPresence>>localDirectedPresences;

发送相同directedPresencesCache但只有不断派驻指导
用户连接到该JVM。

在方法directedPresenceSent()中主要对两个变量开始操作,这里有一个开锁和解锁的过程。

updateHandler.directedPresenceSent(packet, jid, recipientJID.toString());


2.路由消息包

被发送到XMPP域的组件路由数据包(这是XMPP域的子域)

首先检查组件是否被托管在此JVM

存在,交由component.processPacket(packet);出数据包

该MUC服务将接收的域MUC服务的域相匹配的所有数据包。
这意味着,例如,disco请求应该由服务本身作出回应,而不是依赖在服务器上处理请求。

在getChatRoom()方法中会从数据库中加载了房间的配置(如果房间是持久性的,但被添加到数据库服务器启动或房间可能是旧的房间,这是不存在于记忆体后)

这里OF服务器检查到房间需要重新创建的情况下,它没有预先创建(或已被删除不知何故,预计委托存在)。

因为房间不存在,所以接下来就该检测拥有者的创建权限了。依次添加room到内存中,以免其他创建者冲突。

开始创建房间事件——>通知其他集群节点,一个新的空间可用.

检查客户端创建密码或客户端对MUC的支持

(注:获取房间组件的基本信息

Long serviceID = XMPPServer.getInstance().getMultiUserChatManager().

getMultiUserChatServiceID(room.getMUCService().getServiceName());)

服务器返回1:

<presence id="wcCqI-63" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android" 
                       from="room2@qqgroup.8ntmorv1ep4wgcy/test2">
    <x xmlns="http://jabber.org/protocol/muc#user">
         <item jid="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android" 
                                 affiliation="owner" role="moderator"/>
                <status code="201"/>
    </x>
</presence>
给自己发送出席

服务器返回2:

<message type="groupchat"    from="room2@qqgroup.8ntmorv1ep4wgcy" 
                     to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">
<body>确认配置之前已锁住该房间,禁止进入。</body>
</message>

客户端发送:

<iq id="wcCqI-64" to="room2@qqgroup.8ntmorv1ep4wgcy" type="get">
   <query xmlns="http://jabber.org/protocol/muc#owner">
   </query>
</iq >

根据namespace服务器将有IQOwnerHandler来处理

refreshConfigurationFormValues()房间配置信息

服务器返回:

<iq type="result" id="wcCqI-64" from="room2@qqgroup.8ntmorv1ep4wgcy"       to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">
    <query xmlns="http://jabber.org/protocol/muc#owner">
    <x xmlns="jabber:x:data" type="form">
    <title>房间配置</title>
<instructions>
     已创建房间“room2”。要接受缺省配置,请单击“确定”按钮。
     或填写以下表单以完成设置:
</instructions>
<field var="FORM_TYPE" type="hidden">
   <value>http://jabber.org/protocol/muc#roomconfig</value>
</field>

<field var="muc#roomconfig_roomname" type="text-single" 
                        label="房间名称">
    <value>room2</value>
</field>

<field var="muc#roomconfig_roomdesc" type="text-single" 
                            label="描述">
    <value>room2</value>
</field>

<field var="muc#roomconfig_changesubject" type="boolean" 
                 label="允许占有者更改主题">
    <value>1</value>
</field>

<field var="muc#roomconfig_maxusers" type="list-single" 
                                       label="最大房间占有者人数">
    <option label="10">
        <value>10</value>
    </option>
    <option label="20">
        <value>20</value>
    </option>
    <option label="30">
        <value>30</value>
    </option>
    <option label="40">
        <value>40</value>
    </option>
    <option label="50">
        <value>50</value>
    </option>
    <option label="无">
        <value>0</value>
    </option>
     <value>30</value>
</field>

<field var="muc#roomconfig_presencebroadcast" type="list-multi" 
                label="其 Presence 是 Broadcast 的角色">
    <option label="主持者">
        <value>moderator</value>
    </option>
    <option label="参与者">
        <value>participant</value>
    </option>
    <option label="访客">
        <value>visitor</value>
    </option>
    <value>moderator</value>
    <value>participant</value>
    <value>visitor</value>
 </field>
  
<field var="muc#roomconfig_publicroom" type="boolean" 
              label="列出目录中的房间">
<value>1</value>
</field>

<field var="muc#roomconfig_persistentroom" type="boolean" 
label="房间是持久的">
<value>1</value></field>

<field var="muc#roomconfig_moderatedroom" type="boolean" 
label="房间是适度的">
<value>1</value>
</field>

<field var="muc#roomconfig_membersonly" type="boolean" 
label="房间仅对成员开放">
<value>1</value>
</field>

<field type="fixed">
<value>注意:缺省情况下,只有管理员才可以在仅用于邀请的房间中发送邀请。</value>
</field>

<field var="muc#roomconfig_allowinvites" type="boolean" 
                       label="允许占有者邀请其他人">
<value>1</value>
</field>

<field var="muc#roomconfig_passwordprotectedroom" type="boolean" 
                       label="需要密码才能进入房间">
<value>0</value>
</field>

<field type="fixed">
<value>如果需要密码才能进入房间,则您必须在下面指定密码。</value>
</field>

<field var="muc#roomconfig_roomsecret" type="text-private" 
                       label="密码"/>
<field var="muc#roomconfig_whois" type="list-single" 
                       label="能够发现占有者真实 JID 的角色">
<option label="主持者">
     <value>moderators</value>
</option>

<option label="任何人">
     <value>anyone</value>
</option>
<value>anyone</value>
</field>

<field var="muc#roomconfig_enablelogging" type="boolean" 
                       label="登录房间对话">
     <value>1</value>
</field>

<field var="x-muc#roomconfig_reservednick" type="boolean" 
                       label="仅允许注册的昵称登录">
<value>1</value>
    </field>

<field var="x-muc#roomconfig_canchangenick" type="boolean" 
                       label="允许使用者修改昵称">
<value>1</value>
</field>

<field type="fixed">
<value>允许用户注册房间</value>
</field>

<field var="x-muc#roomconfig_registration" type="boolean" 
                       label="允许用户注册房间">
<value>1</value>
</field>

<field type="fixed">
<value>您可以指定该房间的管理员。请在每行提供一个 JID。</value>
</field>

<field var="muc#roomconfig_roomadmins" type="jid-multi" 
                       label="房间管理员"/>

<field type="fixed">
<value>您可以指定该房间的其他拥有者。请在每行提供一个 JID。</value>
</field>

<field var="muc#roomconfig_roomowners" type="jid-multi" 
label="房间拥有者">
<value>test2@8ntmorv1ep4wgcy</value>
</field>
</x>
</query>
</iq>

客户端发送1

<iq id="wcCqI-65" to="room2@qqgroup.8ntmorv1ep4wgcy" type="set">
      <query xmlns="http://jabber.org/protocol/muc#owner">
      <x xmlns="jabber:x:data" type="submit">
      <field var="FORM_TYPE" type="hidden">
          <value>http://jabber.org/protocol/muc#roomconfig</value>
      </field>
      <field var="muc#roomconfig_roomname" type="text-single">
           <value>room2</value>
      </field>
      <field var="muc#roomconfig_roomdesc" type="text-single">
          <value>测试2</value>
      </field>
      <field var="muc#roomconfig_roomowners" type="jid-multi">
          <value>test2@8ntmorv1ep4wgcy</value>
      </field>
      </x>
      </query>
</iq>

在这一步操作,是客户端来设置房间的一些配置信息,并且保存到DB(在类LoaclMUCRomm.saveToDB()方法中)

然后保存用户(普通用户,管理员).

服务端返回1

<message type="groupchat" from="room2@qqgroup.8ntmorv1ep4wgcy" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">
<body>该房间现在已解锁。
</body>
</message>

服务端返回2

<iq type="result" id="wcCqI-65" from="room2@qqgroup.8ntmorv1ep4wgcy" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android"/>

客户端发送

<iq id="wcCqI-66" to="room2@qqgroup.8ntmorv1ep4wgcy" type="get">
<query xmlns="http://jabber.org/protocol/disco#info">
</query>
</iq>

处理类:IQDiscoInfoHandler

服务端返回

<iq type="result" id="wcCqI-66" from="room2@qqgroup.8ntmorv1ep4wgcy"     
                   to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">
<query xmlns="http://jabber.org/protocol/disco#info">
<identity category="conference" name="room2" type="text"/>
<feature var="http://jabber.org/protocol/muc"/>
<feature var="muc_public"/><feature var="muc_membersonly"/>
<feature var="muc_moderated"/>
<feature var="muc_nonanonymous"/>
<feature var="muc_unsecured"/>
<feature var="muc_persistent"/>
<feature var="http://jabber.org/protocol/disco#info"/>
      <x xmlns="jabber:x:data" type="result">
      <field var="FORM_TYPE" type="hidden">
           <value>http://jabber.org/protocol/muc#roominfo</value>
      </field>
      <field var="muc#roominfo_description" label="描述">
          <value>测试2</value>
      </field>
      <field var="muc#roominfo_subject" label="主题">
          <value></value>
      </field>
      <field var="muc#roominfo_occupants" label="占有者人数">
          <value>1</value>
      </field>
      <field var="x-muc#roominfo_creationdate" label="创建日期">
         <value>20131202T02:22:08</value>
     </field>
</x>
</query>
</iq>

加入房间

客户端加入房间,首先获取房间信息

<iq id="BfI3V-47" to="room2@conference.8ntmorv1ep4wgcy" type="get">
  <query xmlns="http://jabber.org/protocol/disco#info"/>
</iq>

服务端通过查找服务器组件获取房间信息并返回如下报文

<iq type="result" id="BfI3V-55" from="room2@conference.8ntmorv1ep4wgcy" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">
  <query xmlns="http://jabber.org/protocol/disco#info">
    <identity category="conference" name="room2" type="text"/>
    <feature var="http://jabber.org/protocol/muc"/>
    <feature var="muc_public"/>
    <feature var="muc_open"/>
    <feature var="muc_unmoderated"/>
    <feature var="muc_nonanonymous"/>
    <feature var="muc_unsecured"/>
    <feature var="muc_persistent"/>
    <feature var="http://jabber.org/protocol/disco#info"/>
    <x xmlns="jabber:x:data" type="result">
      <field var="FORM_TYPE" type="hidden">
        <value>http://jabber.org/protocol/muc#roominfo</value>
      </field>
      <field var="muc#roominfo_description" label="描述">
        <value>测试房间2</value>
      </field>
      <field var="muc#roominfo_subject" label="主题">
        <value></value>
      </field>
      <field var="muc#roominfo_occupants" label="占有者人数">
        <value>0</value>
      </field>
      <field var="x-muc#roominfo_creationdate" label="创建日期">
        <value>20131202T07:08:32</value>
      </field>
    </x>
  </query>
</iq>

客户端再次发送状态

<presence id="BfI3V-57" to="room2@conference.8ntmorv1ep4wgcy/test2"><x xmlns="http://jabber.org/protocol/muc"></x></presence>

服务端返回:

<presence id="BfI3V-57" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android" from="room2@conference.8ntmorv1ep4wgcy/test2"><x xmlns="http://jabber.org/protocol/muc#user"><item jid="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android" affiliation="owner" role="moderator"/></x></presence>
<message type="groupchat" from="room2@conference.8ntmorv1ep4wgcy" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android"><body>该房间不是匿名的。</body><x xmlns="http://jabber.org/protocol/muc#user"><status code="100"/></x></message>

邀请用户

请求用户发送消息内容

<message id="BfI3V-64" to="room2@conference.8ntmorv1ep4wgcy">
  <x xmlns="http://jabber.org/protocol/muc#user">
    <invite to="test1@8ntmorv1ep4wgcy">
      <reason>请把我加入会议中。</reason>
    </invite>
  </x>
</message>

组件将消息发送给客户端test1,如图:


Test1接收邀请

发送消息:

<presence id="6808K-48" to="room2@conference.8ntmorv1ep4wgcy/test1">
  <x xmlns="http://jabber.org/protocol/muc"/>
</presence>

服务端将发送如下消息

<presence id="6808K-48" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android" from="room2@conference.8ntmorv1ep4wgcy/test1">
<x xmlns="http://jabber.org/protocol/muc#user">
<item jid="test1@8ntmorv1ep4wgcy/Spark 2.6.3#android" affiliation="none" role="participant"/>
</x>
</presence>
<presence id="BfI3V-57" to="test1@8ntmorv1ep4wgcy/Spark 2.6.3#android" from="room2@conference.8ntmorv1ep4wgcy/test2">
<x xmlns="http://jabber.org/protocol/muc#user">
<item jid="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android" affiliation="owner" role="moderator"/>
</x>
</presence>

OK,关于会议室这块就到次结束。这里读起来很难理解很正常。基于xmpp协议的通讯消息太繁琐了。但是只要读者细心debug调试,还是不难的。

我在上面中的jid,如:jid="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android,这里面有个#号。而实际上在openfire正常的通讯是没的

这是本人调试测试多加了个jid属性。关于jid部分,本人会单独拿出来写博文的。欢迎阅读,不对之处请联系本人指正。本人邮箱:

624308915@qq.com






























原文地址:https://www.cnblogs.com/huwf/p/4273360.html