手机客户端需要实现即时通信功能,在通信协议上采用开放的Jabber(XMPP)协议,整个XMPP协议最为核心也是最为复杂的部分是Subscription的运行机制,抓取了一下相关的通信协议报文,供开发时候参考。
1. 测试环境
1.1. 测试软件环境:
Jabber服务器:Openfire 3.5.2
Jabber客户端:Spark 2.5.8
抓包:Wireshark
1.2. 服务器环境:
Jabber服务器:192.168.1.12,安装Openfire,域为home1
Jabber用户客户端:192.168.1.10,安装Spark客户端,Jabber用户为chuanliang。
Jabber联系人客户端:192.168.1.11,安装Spark客户端,Jabber用户为liangchuan
1.3. 测试场景:
测试1:
Jabber用户chuanliang和Jabber联系人liangchuan被彼此的presence订阅,且允许把彼此加入自己的Roster
测试2
Jabber联系人liangchuan接受Jabber用户chuanliang的订阅,但是不允许chuanliang加入自己的Roster
测试3
Jabber联系人liangchuan不接受Jabber用户chuanliang的订阅,同时不允许chuanliang加入自己的Roster
以测试用例“测试1”说明一下测试过程:
1、 chuanliang@home1在192.168.1.10客户端上新增联系人liangchuan@home1
2、 liangchuan@home1在192.168.1.11客户端上“准许chuanliang@home1新增liangchuan@home1到chuanlian的联系人名单中”,同时”Add user to liangchuan’s roster”
3、 chuanliang@home1在192.168.1.10客户端上“准许liangchuan@home1新增chuanliang@home1到chuanlian的联系人名单中”,同时”Add user to chuanliang’s roster”
2. 测试1- contact accept subscription and allow add user to it’s roster
2.1. Jabber用户客户端发送一个type=’set’ 的Roster Update请求给Jabber服务器端
Ø 192.168.1.10->192.168.1.12
Jabber用户客户端发送一个<iq/>包给Jabber服务器端,其jabber:iq:roster名字空间中包含一个type=’set’,用于更新自己的roster
<iq id="7SJ55-63" type="set">
<query xmlns="jabber:iq:roster">
<item jid="liangchuan@home1" name="liangchuan">
<group>Friends</group>
</item>
</query>
</iq>
Ø 192.168.1.12->192.168.1.10
Jabber用户客户端从服务器接收到一个“roster push”,产生一个新的roster条目,其subscription状态置为“none”:
<iq type="set" id="308-51" to="chuanliang@home1/spark">
<query xmlns="jabber:iq:roster">
<item jid="liangchuan@home1" name="liangchuan" subscription="none">
<group>Friends</group>
</item>
</query>
</iq>
Ø 192.168.1.12->192.168.1.10
Jabber用户客户端在成功的情况下,从服务器接收到一个<iq/>包,其type=’result’。
<iq type="result" id="7SJ55-63" to="chuanliang@home1/spark"/>
2.2. Jabber用户客户端发送一个type=’subscribe’的<presence/>的包给联系人客户端
Jabber用户客户端发送一个type=’subscribe’的<presence/>的包给联系人(发送给到Jabber服务器)
Ø 192.168.1.10->192.168.1.12
<presence id="7SJ55-64" to="liangchuan@home1" type="subscribe"/>
Ø 192.168.1.12->192.168.1.10
Jabber用户客户端接着从服务器收到包含联系人待定子状态的’none’订阅状态的“roster push”;这个待定子状态在其联系人名单中包含一个ask=’subscribe’属性:
<iq type="set" id="132-52" to="chuanliang@home1/spark">
<query xmlns="jabber:iq:roster">
<item jid="liangchuan@home1" name="liangchuan" ask="subscribe" subscription="none">
<group>Friends</group>
</item>
</query>
</iq>
2.3. Jabber服务器发送一个type=’subscribe’的<presence/>的Roster push包给联系人客户端
Ø 192.168.1.12->192.168.1.11
Jabber服务器在接收到192.168.1.10发送的type=’subscribe’的<presence/>的包后,然后发送一个type=’subscribe’的<presence/>的包给联系人客户端
<presence id="7SJ55-64" to="liangchuan@home1" type="subscribe" from="chuanliang@home1"/>
Ø 192.168.1.11->192.168.1.12
Jabber联系人客户端发送一个<iq/>包给Jabber服务器,其jabber:iq:roster名字空间中包含一个type=’set’,用于更新自己的roster:
<iq id="8SAVC-26" type="set">
<query xmlns="jabber:iq:roster">
<item jid="chuanliang@home1" name="chuanliang">
<group>Friends</group>
</item>
</query>
</iq>
Ø 192.168.1.12->192.168.1.11
Jabber联系人客户端从服务器接收到一个<iq/>包,其type=’result’
<iq type="result" id="8SAVC-26" to="liangchuan@home1/spark"/>
2.4. Jabber联系人客户端发送一个type=’subscribe’的<presence/>的包给用户客户端
此部分请求发生在联系人客户端“准许chuanliang@home1新增liangchuan@home1到chuanlian的联系人名单中”时候同时选择了:“Add user to liangchuan’s roster”选项。如果没有选择,则无此部分请求。
Ø 192.168.1.11->192.168.1.12
<presence id="8SAVC-27" to="chuanliang@home1" type="subscribe"/>
Ø 192.168.1.12->192.168.1.10
服务器传递请求给Jabber用户端
<presence id="8SAVC-27" to="chuanliang@home1" type="subscribe" from="liangchuan@home1"/>
Ø 192.168.1.12->192.168.1.10
<iq type="set" id="867-55" to="chuanliang@home1/spark">
<query xmlns="jabber:iq:roster">
<item jid="liangchuan@home1" name
="liangchuan" subscription="to">
<group>Friends</group>
</item>
</query>
</iq>
2.5. Jabber联系人客户端接收订阅请求,发送一个type=’subscribed’的<presence/>的包给用户客户端
联系人下一步要做的就是决定是否接受订阅请求。我们假定联系人接受订阅请求
Ø 192.168.1.11->192.168.1.12
联系人的Jabber客户端发送一个type=’subscribed’的<presence/>的包给Jabber用户(通过服务器端)
<presence id="8SAVC-28" to="chuanliang@home1" type="subscribed"/>
Ø 192.168.1.12->192.168.1.10
<presence id="8SAVC-28" to="chuanliang@home1" type="subscribed" from="liangchuan@home1"/>
<presence id="8SAVC-6" from="liangchuan@home1/spark" to="chuanliang@home1/spark">
<status/>
<priority>1</priority>
</presence>
Ø 192.168.1.12->192.168.1.11
联系人的Jabber客户端从服务器接收一个“roster push”包含Jabber用户的条目,其中subscription状态置为“from”
<iq type="set" id="14-54" to="liangchuan@home1/spark">
<query xmlns="jabber:iq:roster">
<item jid="chuanliang@home1" name="chuanliang" ask="subscribe" subscription="from">
<group>Friends</group>
</item>
</query>
</iq>
2.6. Jabber用户客户端接收订阅请求,发送一个type=’subscribed’的<presence/>的包给联系人客户端
Ø 192.168.1.10->192.168.1.12
<presence id="7SJ55-65" to="liangchuan@home1" type="subscribed"/>
Ø 192.168.1.12->192.168.1.10
<iq type="set" id="382-56" to="chuanliang@home1/spark">
<query xmlns="jabber:iq:roster">
<item jid="liangchuan@home1" name="liangchuan" subscription="both">
<group>Friends</group>
</item>
</query>
</iq>
Ø 192.168.1.12->192.168.1.11
<iq type="set" id="697-57" to="liangchuan@home1/spark">
<query xmlns="jabber:iq:roster">
<item jid="chuanliang@home1" name="chuanliang" subscription="both">
<group>Friends</group>
</item>
</query>
</iq>
Ø 192.168.1.12->192.168.1.11
<presence id="7SJ55-65" to="liangchuan@home1" type="subscribed" from="chuanliang@home1"/>
<presence id="7SJ55-51" from="chuanliang@home1/spark" to="liangchuan@home1/spark">
<status/>
<priority>1</priority>
</presence>
3. 测试2- contact accept subscribption and not allow add user to it’s roster
3.1. Jabber用户客户端发送一个type=’set’ 的Roster Update请求给Jabber服务器端
Ø 192.168.1.10->192.168.1.12
Jabber用户客户端发送一个<iq/>包给Jabber服务器端,其jabber:iq:roster名字空间中包含一个type=’set’,用于更新自己的roster
<iq id="xHk99-51" type="set">
<query xmlns="jabber:iq:roster">
<item jid="liangchuan@home1" name="liangchuan">
<group>Friends</group>
</item>
</query>
</iq>
Ø 192.168.1.12->192.168.1.10
Jabber用户客户端从服务器接收到一个“roster push”,产生一个新的roster条目,其subscription状态置为“none”:
<iq type="set" id="133-130" to="chuanliang@home1/spark">
<query xmlns="jabber:iq:roster">
<item jid="liangchuan@home1" name="liangchuan" subscription="none">
<group>Friends</group>
</item>
</query>
</iq>
Ø 192.168.1.12->192.168.1.10
Jabber用户客户端在成功的情况下,从服务器接收到一个<iq/>包,其type=’result’。
<iq type="result" id="xHk99-51" to="chuanliang@home1/spark"/>
3.2. Jabber用户客户端发送一个type=’subscribe’的<presence/>的包给联系人客户端
Jabber用户客户端发送一个type=’subscribe’的<presence/>的包给联系人(发送给到Jabber服务器)
Ø 192.168.1.10->192.168.1.12
<presence id="xHk99-52" to="liangchuan@home1" type="subscribe"></presence>
Ø 192.168.1.12->192.168.1.10
Jabber用户客户端接着从服务器收到包含联系人待定子状态的’none’订阅状态的“roster push”;这个待定子状态在其联系人名单中包含一个ask=’subscribe’属性:
<iq type="set" id="35-132" to="chuanliang@home1/spark">
<query xmlns="jabber:iq:roster">
<item jid="liangchuan@home1" name="liangchuan" ask="subscribe" subscription="none">
<group>Friends</group>
</item>
</query>
</iq>
3.3. Jabber服务器发送一个type=’subscribe’的<presence/>的Roster push包给联系人客户端
Ø 192.168.1.12->192.168.1.11
Jabber服务器在接收到192.168.1.10发送的type=’subscribe’的<presence/>的包后,然后发送一个type=’subscribe’的<presence/>的包给联系人客户端
<presence id="xHk99-52" to="liangchuan@home1" type="subscribe" from="chuanliang@home1"/>
3.4. Jabber联系人客户端接收订阅请求,发送一个type=’subscribed’的<presence/>的包给用户客户端
联系人下一步要做的就是决定是否接受订阅请求。我们这里联系人接受订阅请求,但是取消”Add user to chuanliang’s roster”操作。
Ø 192.168.1.11->192.168.1.12
联系人的Jabber客户端发送一个type=’subscribed’的<presence/>的包给Jabber用户(通过服务器端)
<presence id="8SAVC-90" to="chuanliang@home1" type="subscribed"></presence>
Ø 192.168.1.12->192.168.1.10
<presence id="8SAVC-90" to="chuanliang@home1" type="subscribed" from="liangchuan@home1"/>
<presence id="8SAVC-30" from="liangchuan@home1/spark" to="chuanliang@home1/spark">
<status/>
<priority>1</priority>
</presence>
Ø 192.168.1.12->192.168.1.11
联系人的Jabber客户端从服务器接收一个“roster push”包含Jabber用户的条目,其中subscription状态置为“from”
<iq type="set" id="585-135" to="liangchuan@home1/spark">
<query xmlns="jabber:iq:roster">
<item jid="chuanliang@home1" subscription="from"/>
</query>
</iq>
4. 测试3- contact not accept subscription and not allow add user to it’s roster
4.1. Jabber用户客户端发送一个type=’set’ 的Roster Update请求给Jabber服务器端
转载请注明:出家如初,成佛有余 » Jabber协议分析之Subscriptions运行机制-Presence subscription