• (转)OpenFire源码学习之八:MUC用户聊天室


    转:http://blog.csdn.net/huwenfeng_2011/article/details/43413817

    MUC

    房间属性设置

    以上属性存储在MUCPersistenceManager

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

    创建房间

    客户端创建房间案例

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

    [html] view plain copy
     
    1. <iq id="wcCqI-57" to="room1@qqgroup.8ntmorv1ep4wgcy" type="get">  
    2.   <query xmlns="http://jabber.org/protocol/disco#info"/>  
    3. </iq>  

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

    routed = routeToComponent(jid,packet, routed);

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

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

    获取MUC组件的信息

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

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

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

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

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

    [html] view plain copy
     
    1. <iq type="error" id="wcCqI-57" from="room1@qqgroup.8ntmorv1ep4wgcy" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">  
    2.   <query xmlns="http://jabber.org/protocol/disco#info"/>  
    3.   <error code="404" type="cancel">  
    4.     <item-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>  
    5.   </error>  
    6. </iq>  

    客户端第二轮发送:

    [html] view plain copy
     
    1. <presence id="wcCqI-59" to="room1@qqgroup.8ntmorv1ep4wgcy/test2">  
    2.   <xmlns="http://jabber.org/protocol/muc"/>  
    3. </presence>  

    服务器处理:

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

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

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

    服务器返回消息:

    [html] view plain copy
     
    1. <message type="groupchat" from="room1@qqgroup.8ntmorv1ep4wgcy">  
    2.   <body>确认配置之前已锁住该房间,禁止进入。</body>  
    3. </message>  

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

    [html] view plain copy
     
    1. <presence id="wcCqI-59" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android"   
    2.           from="room1@qqgroup.8ntmorv1ep4wgcy/test2">  
    3. <xmlns="http://jabber.org/protocol/muc#user">  
    4.    <item jid="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android"   
    5.           affiliation="owner" role="moderator"/>  
    6.          <status code="201"/>  
    7. </x>  
    8. </presence>  

    客户端发送IQ:

    [html] view plain copy
     
    1. <iq id="wcCqI-60" to="room1@qqgroup.8ntmorv1ep4wgcy" type="get">  
    2.   <query xmlns="http://jabber.org/protocol/muc#owner"/>  
    3. </iq>  

    查询房间拥有者。

    服务器返回1:

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

    服务器返回2:

    [html] view plain copy
     
    1. <iq type="error" id="wcCqI-60" from="room1@qqgroup.8ntmorv1ep4wgcy"   
    2.                          to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">  
    3.    <query xmlns="http://jabber.org/protocol/muc#owner"/>  
    4.        <error code="401" type="auth">  
    5.        <not-authorized xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>  
    6.    </error>  
    7. </iq>  

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

    总的对话

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

    [html] view plain copy
     
    1. <iq id="wcCqI-61" to="room2@qqgroup.8ntmorv1ep4wgcy" type="get">  
    2.     <query xmlns="http://jabber.org/protocol/disco#info">  
    3.     </query>  
    4. </iq>  

    服务器返回

    [html] view plain copy
     
    1. <iq type="error" id="wcCqI-61"   
    2.                              from="room2@qqgroup.8ntmorv1ep4wgcy"   
    3.                              to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">  
    4.      <query xmlns="http://jabber.org/protocol/disco#info"/>  
    5.          <error code="404" type="cancel">  
    6.              <item-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>  
    7.          </error>  
    8. </iq>  

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

    客户端发送

    [html] view plain copy
     
    1. <presence id="wcCqI-63" to="room2@qqgroup.8ntmorv1ep4wgcy/test2">  
    2. <xmlns="http://jabber.org/protocol/muc">  
    3. </x>  
    4. </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:

    [html] view plain copy
     
    1. <presence id="wcCqI-63" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android"   
    2.                        from="room2@qqgroup.8ntmorv1ep4wgcy/test2">  
    3.     <xmlns="http://jabber.org/protocol/muc#user">  
    4.          <item jid="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android"   
    5.                                  affiliation="owner" role="moderator"/>  
    6.                 <status code="201"/>  
    7.     </x>  
    8. </presence>  

    给自己发送出席

    服务器返回2:

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

    客户端发送:

    [html] view plain copy
     
    1. <iq id="wcCqI-64" to="room2@qqgroup.8ntmorv1ep4wgcy" type="get">  
    2.    <query xmlns="http://jabber.org/protocol/muc#owner">  
    3.    </query>  
    4. </iq >  

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

    refreshConfigurationFormValues()房间配置信息

    服务器返回:

    [html] view plain copy
     
    1. <iq type="result" id="wcCqI-64" from="room2@qqgroup.8ntmorv1ep4wgcy"       to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">  
    2.     <query xmlns="http://jabber.org/protocol/muc#owner">  
    3.     <xmlns="jabber:x:data" type="form">  
    4.     <title>房间配置</title>  
    5. <instructions>  
    6.      已创建房间“room2”。要接受缺省配置,请单击“确定”按钮。  
    7.      或填写以下表单以完成设置:  
    8. </instructions>  
    9. <field var="FORM_TYPE" type="hidden">  
    10.    <value>http://jabber.org/protocol/muc#roomconfig</value>  
    11. </field>  
    12.   
    13. <field var="muc#roomconfig_roomname" type="text-single"   
    14.                         label="房间名称">  
    15.     <value>room2</value>  
    16. </field>  
    17.   
    18. <field var="muc#roomconfig_roomdesc" type="text-single"   
    19.                             label="描述">  
    20.     <value>room2</value>  
    21. </field>  
    22.   
    23. <field var="muc#roomconfig_changesubject" type="boolean"   
    24.                  label="允许占有者更改主题">  
    25.     <value>1</value>  
    26. </field>  
    27.   
    28. <field var="muc#roomconfig_maxusers" type="list-single"   
    29.                                        label="最大房间占有者人数">  
    30.     <option label="10">  
    31.         <value>10</value>  
    32.     </option>  
    33.     <option label="20">  
    34.         <value>20</value>  
    35.     </option>  
    36.     <option label="30">  
    37.         <value>30</value>  
    38.     </option>  
    39.     <option label="40">  
    40.         <value>40</value>  
    41.     </option>  
    42.     <option label="50">  
    43.         <value>50</value>  
    44.     </option>  
    45.     <option label="无">  
    46.         <value>0</value>  
    47.     </option>  
    48.      <value>30</value>  
    49. </field>  
    50.   
    51. <field var="muc#roomconfig_presencebroadcast" type="list-multi"   
    52.                 label="其 Presence 是 Broadcast 的角色">  
    53.     <option label="主持者">  
    54.         <value>moderator</value>  
    55.     </option>  
    56.     <option label="参与者">  
    57.         <value>participant</value>  
    58.     </option>  
    59.     <option label="访客">  
    60.         <value>visitor</value>  
    61.     </option>  
    62.     <value>moderator</value>  
    63.     <value>participant</value>  
    64.     <value>visitor</value>  
    65.  </field>  
    66.     
    67. <field var="muc#roomconfig_publicroom" type="boolean"   
    68.               label="列出目录中的房间">  
    69. <value>1</value>  
    70. </field>  
    71.   
    72. <field var="muc#roomconfig_persistentroom" type="boolean"   
    73. label="房间是持久的">  
    74. <value>1</value></field>  
    75.   
    76. <field var="muc#roomconfig_moderatedroom" type="boolean"   
    77. label="房间是适度的">  
    78. <value>1</value>  
    79. </field>  
    80.   
    81. <field var="muc#roomconfig_membersonly" type="boolean"   
    82. label="房间仅对成员开放">  
    83. <value>1</value>  
    84. </field>  
    85.   
    86. <field type="fixed">  
    87. <value>注意:缺省情况下,只有管理员才可以在仅用于邀请的房间中发送邀请。</value>  
    88. </field>  
    89.   
    90. <field var="muc#roomconfig_allowinvites" type="boolean"   
    91.                        label="允许占有者邀请其他人">  
    92. <value>1</value>  
    93. </field>  
    94.   
    95. <field var="muc#roomconfig_passwordprotectedroom" type="boolean"   
    96.                        label="需要密码才能进入房间">  
    97. <value>0</value>  
    98. </field>  
    99.   
    100. <field type="fixed">  
    101. <value>如果需要密码才能进入房间,则您必须在下面指定密码。</value>  
    102. </field>  
    103.   
    104. <field var="muc#roomconfig_roomsecret" type="text-private"   
    105.                        label="密码"/>  
    106. <field var="muc#roomconfig_whois" type="list-single"   
    107.                        label="能够发现占有者真实 JID 的角色">  
    108. <option label="主持者">  
    109.      <value>moderators</value>  
    110. </option>  
    111.   
    112. <option label="任何人">  
    113.      <value>anyone</value>  
    114. </option>  
    115. <value>anyone</value>  
    116. </field>  
    117.   
    118. <field var="muc#roomconfig_enablelogging" type="boolean"   
    119.                        label="登录房间对话">  
    120.      <value>1</value>  
    121. </field>  
    122.   
    123. <field var="x-muc#roomconfig_reservednick" type="boolean"   
    124.                        label="仅允许注册的昵称登录">  
    125. <value>1</value>  
    126.     </field>  
    127.   
    128. <field var="x-muc#roomconfig_canchangenick" type="boolean"   
    129.                        label="允许使用者修改昵称">  
    130. <value>1</value>  
    131. </field>  
    132.   
    133. <field type="fixed">  
    134. <value>允许用户注册房间</value>  
    135. </field>  
    136.   
    137. <field var="x-muc#roomconfig_registration" type="boolean"   
    138.                        label="允许用户注册房间">  
    139. <value>1</value>  
    140. </field>  
    141.   
    142. <field type="fixed">  
    143. <value>您可以指定该房间的管理员。请在每行提供一个 JID。</value>  
    144. </field>  
    145.   
    146. <field var="muc#roomconfig_roomadmins" type="jid-multi"   
    147.                        label="房间管理员"/>  
    148.   
    149. <field type="fixed">  
    150. <value>您可以指定该房间的其他拥有者。请在每行提供一个 JID。</value>  
    151. </field>  
    152.   
    153. <field var="muc#roomconfig_roomowners" type="jid-multi"   
    154. label="房间拥有者">  
    155. <value>test2@8ntmorv1ep4wgcy</value>  
    156. </field>  
    157. </x>  
    158. </query>  
    159. </iq>  

    客户端发送1

    [html] view plain copy
     
    1. <iq id="wcCqI-65" to="room2@qqgroup.8ntmorv1ep4wgcy" type="set">  
    2.       <query xmlns="http://jabber.org/protocol/muc#owner">  
    3.       <xmlns="jabber:x:data" type="submit">  
    4.       <field var="FORM_TYPE" type="hidden">  
    5.           <value>http://jabber.org/protocol/muc#roomconfig</value>  
    6.       </field>  
    7.       <field var="muc#roomconfig_roomname" type="text-single">  
    8.            <value>room2</value>  
    9.       </field>  
    10.       <field var="muc#roomconfig_roomdesc" type="text-single">  
    11.           <value>测试2</value>  
    12.       </field>  
    13.       <field var="muc#roomconfig_roomowners" type="jid-multi">  
    14.           <value>test2@8ntmorv1ep4wgcy</value>  
    15.       </field>  
    16.       </x>  
    17.       </query>  
    18. </iq>  

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

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

    服务端返回1

    [html] view plain copy
     
    1. <message type="groupchat" from="room2@qqgroup.8ntmorv1ep4wgcy" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">  
    2. <body>该房间现在已解锁。  
    3. </body>  
    4. </message>  

    服务端返回2

    [html] view plain copy
     
    1. <iq type="result" id="wcCqI-65" from="room2@qqgroup.8ntmorv1ep4wgcy" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android"/>  

    客户端发送

    [html] view plain copy
     
    1. <iq id="wcCqI-66" to="room2@qqgroup.8ntmorv1ep4wgcy" type="get">  
    2. <query xmlns="http://jabber.org/protocol/disco#info">  
    3. </query>  
    4. </iq>  

    处理类:IQDiscoInfoHandler

    服务端返回

    [html] view plain copy
     
    1. <iq type="result" id="wcCqI-66" from="room2@qqgroup.8ntmorv1ep4wgcy"       
    2.                    to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">  
    3. <query xmlns="http://jabber.org/protocol/disco#info">  
    4. <identity category="conference" name="room2" type="text"/>  
    5. <feature var="http://jabber.org/protocol/muc"/>  
    6. <feature var="muc_public"/><feature var="muc_membersonly"/>  
    7. <feature var="muc_moderated"/>  
    8. <feature var="muc_nonanonymous"/>  
    9. <feature var="muc_unsecured"/>  
    10. <feature var="muc_persistent"/>  
    11. <feature var="http://jabber.org/protocol/disco#info"/>  
    12.       <xmlns="jabber:x:data" type="result">  
    13.       <field var="FORM_TYPE" type="hidden">  
    14.            <value>http://jabber.org/protocol/muc#roominfo</value>  
    15.       </field>  
    16.       <field var="muc#roominfo_description" label="描述">  
    17.           <value>测试2</value>  
    18.       </field>  
    19.       <field var="muc#roominfo_subject" label="主题">  
    20.           <value></value>  
    21.       </field>  
    22.       <field var="muc#roominfo_occupants" label="占有者人数">  
    23.           <value>1</value>  
    24.       </field>  
    25.       <field var="x-muc#roominfo_creationdate" label="创建日期">  
    26.          <value>20131202T02:22:08</value>  
    27.      </field>  
    28. </x>  
    29. </query>  
    30. </iq>  

    加入房间

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

    [html] view plain copy
     
    1. <iq id="BfI3V-47" to="room2@conference.8ntmorv1ep4wgcy" type="get">  
    2.   <query xmlns="http://jabber.org/protocol/disco#info"/>  
    3. </iq>  

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

    [html] view plain copy
     
    1. <iq type="result" id="BfI3V-55" from="room2@conference.8ntmorv1ep4wgcy" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android">  
    2.   <query xmlns="http://jabber.org/protocol/disco#info">  
    3.     <identity category="conference" name="room2" type="text"/>  
    4.     <feature var="http://jabber.org/protocol/muc"/>  
    5.     <feature var="muc_public"/>  
    6.     <feature var="muc_open"/>  
    7.     <feature var="muc_unmoderated"/>  
    8.     <feature var="muc_nonanonymous"/>  
    9.     <feature var="muc_unsecured"/>  
    10.     <feature var="muc_persistent"/>  
    11.     <feature var="http://jabber.org/protocol/disco#info"/>  
    12.     <xmlns="jabber:x:data" type="result">  
    13.       <field var="FORM_TYPE" type="hidden">  
    14.         <value>http://jabber.org/protocol/muc#roominfo</value>  
    15.       </field>  
    16.       <field var="muc#roominfo_description" label="描述">  
    17.         <value>测试房间2</value>  
    18.       </field>  
    19.       <field var="muc#roominfo_subject" label="主题">  
    20.         <value></value>  
    21.       </field>  
    22.       <field var="muc#roominfo_occupants" label="占有者人数">  
    23.         <value>0</value>  
    24.       </field>  
    25.       <field var="x-muc#roominfo_creationdate" label="创建日期">  
    26.         <value>20131202T07:08:32</value>  
    27.       </field>  
    28.     </x>  
    29.   </query>  
    30. </iq>  

    客户端再次发送状态

    [html] view plain copy
     
    1. <presence id="BfI3V-57" to="room2@conference.8ntmorv1ep4wgcy/test2"><xmlns="http://jabber.org/protocol/muc"></x></presence>  

    服务端返回:

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

    邀请用户

    请求用户发送消息内容

    [html] view plain copy
     
    1. <message id="BfI3V-64" to="room2@conference.8ntmorv1ep4wgcy">  
    2.   <xmlns="http://jabber.org/protocol/muc#user">  
    3.     <invite to="test1@8ntmorv1ep4wgcy">  
    4.       <reason>请把我加入会议中。</reason>  
    5.     </invite>  
    6.   </x>  
    7. </message>  

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

    Test1接收邀请

    发送消息:

    [html] view plain copy
     
    1. <presence id="6808K-48" to="room2@conference.8ntmorv1ep4wgcy/test1">  
    2.   <xmlns="http://jabber.org/protocol/muc"/>  
    3. </presence>  

    服务端将发送如下消息

    [html] view plain copy
     
    1. <presence id="6808K-48" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android" from="room2@conference.8ntmorv1ep4wgcy/test1">  
    2. <xmlns="http://jabber.org/protocol/muc#user">  
    3. <item jid="test1@8ntmorv1ep4wgcy/Spark 2.6.3#android" affiliation="none" role="participant"/>  
    4. </x>  
    5. </presence>  
    6. <presence id="BfI3V-57" to="test1@8ntmorv1ep4wgcy/Spark 2.6.3#android" from="room2@conference.8ntmorv1ep4wgcy/test2">  
    7. <xmlns="http://jabber.org/protocol/muc#user">  
    8. <item jid="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android" affiliation="owner" role="moderator"/>  
    9. </x>  
    10. </presence>  


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

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

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

    624308915@qq.com

  • 相关阅读:
    Window7中Eclipse运行MapReduce程序报错的问题
    Hadoop以及其外围生态系统的安装参考
    《node.js开发指南》第五章与新版Node变化太大的一些问题
    打造开发React Native的Sublime
    幸福
    近日阅读记录
    react中属性默认值是true?
    git撤销操作总结
    React Native中的DeviceEventEmitter.addListener与DeviceEventEmitter.emit
    React父子组件的一个混淆点
  • 原文地址:https://www.cnblogs.com/wangle1001986/p/7170193.html
Copyright © 2020-2023  润新知