登录 用户中心() [退出] 后台管理 注册
   
您的位置: 首页 >> xmppmini - 真正实用的 xmpp 客户端 >> 主题: xmppmini 协议整理文档 -- 3.好友协议     [回主站]     [分站链接]
标题
xmppmini 协议整理文档 -- 3.好友协议
clq
浏览(822) + 2020-10-18 12:49:52 发表 编辑

关键字:

[2020-10-28 16:57:40 最后更新]
xmppmini 协议整理文档 -- 3.好友协议

这部分太复杂,因此先整理这一部分。

----------------------------------------------------------------
1.取好友列表
比较简单,就是一个请求一个回复。

//返回好友列表的操作
//if "jabber:iq:roster" == xmlns
//查询好友列表
//参考 https://blog.csdn.net/w690333243/article/details/70207210
//请求大概是,其实 spark 客户端是不发送 from 节的(至少我手头的版本如此)
//<iq from='juliet@example.com/balcony'
//       id='bv1bs71f'
//       type='get'>
//    <query xmlns='jabber:iq:roster'/>
//</iq>

返回的内容大致为

    res := "<iq type=\"result\"" +
    //+' id="tckDM-9"'
    " id=\"" + id + "\"" + //id 一定要和原来的一样
    ////" to=\"" + t_jid + "\"" + //查询的是自己的,所以回应中也要写这个
    " to=\"" + from_jid + "\"" + //查询的是自己的,所以回应中也要写这个
    ">" +
    
    
    "<query xmlns=\"jabber:iq:roster\">" +
    
    /*
    //subscription="none"  应该是是否在线//不对,应该是否已通过为好友,取值为 both from to
    ////+'<item jid="elizabeth@longbourn.lit" name="Elizabeth" subscription="both"/>'
    ////+'<item jid="bingley@netherfield.lit" name="Bingley" subscription="both"/>'
    "<item jid=\"elizabethaaa@longbourn.lit\" name=\"Elizabeth\" subscription=\"both\"><group>Friends</group></item>" +
    "<item jid=\"bingley@netherfield.lit\" name=\"Bingley\" subscription=\"both\"><group>Friends</group></item>" +
    "<item jid=\"t111@127.0.0.1\" name=\"t111@127.0.0.1\" subscription=\"both\"><group>Friends</group></item>" +
    "<item jid=\"t211@127.0.0.1\" name=\"t211@127.0.0.1\" subscription=\"both\"><group>Friends</group></item>" +
    "<item jid=\"n1@newbt.net\" name=\"n1@newbt.net\" subscription=\"both\"><group>Friends</group></item>" +
    "<item jid=\"n2@newbt.net\" name=\"n2@newbt.net\" subscription=\"both\"><group>Friends</group></item>" +
    //openfire 多了 <group>Friends</group>
    */
    
    uls +
    
    
    "</query>" +
    "</iq>"
    ;



2.单个好友新增通知
这部分其实并不是必须的,但在多数客户端中不实现的话它们会不认新增加的用户,因为它们不会去在添加好友后刷新整个好友列表。
消息本身比较简单,相关概念则需要理解。
实际上是由服务器在任意时刻发送一个消息包,客户端发现这种类型的包后会在自己的好友列表中添加。实际在这一包的变体还用于更新好友间的关系(我们放到下个包说)。

3.添加好友的请求

请求为
<iq id="lhj5w-99" type="set">
  <query xmlns="jabber:iq:roster">
    <item jid="ccc4@127.0.0.1" name="ccc4">
      <group>Friend</group>
    </item>
  </query>
</iq>


会回应一个添加新好友的信息。其实是一个独立的上述的“单个好友新增通知”,信息 id 都是新的。
<iq id="377-3" to="clq@127.0.0.1/Spark" type="set">
  <query xmlns="jabber:iq:roster">
    <item jid="ccc4@127.0.0.1" name="ccc4" subscription="none">
      <group>Friend</group>
    </item>
  </query>
</iq>

不过对应这个消息 id 的回应其实是下面这个,其实照协议应该是回应这一个。上面的那个应该是可选的,不过事实上变成了必须的。
<iq id="lhj5w-99" to="clq@127.0.0.1/Spark" type="result"/>


4.请求对方添加自己的回应
有些客户端,在得不到对方回应时居然会报错(例如 spark)。这其实不应该,因为对方就可能不在线或者是对方的服务器这时候暂停了啊。
所以有一些客户端是不强求的这一部分的,不过可以尽量实现。

客户端:
<presence id="lhj5w-27" to="ccc3@127.0.0.1" type="subscribe"></presence>

回应的格式比较特别,目前的实现,例如 openfire 的回应如下。这其实上已经与传统的 RFC3920 不一样的,查了一下,应该是定义在 rfc6121 中的。参考 https://blog.csdn.net/jdcdrdim/article/details/7226987

<iq xmlns="jabber:client" type="set" id="494-10" to="clq@127.0.0.1/gajim.6929J2FG">
<query xmlns="jabber:iq:roster">
<item jid="ccc5@127.0.0.1" ask="subscribe" subscription="none" />
</query>
</iq>

这里要特别注意的是,如果是实现客户端,一定要注意 presence 包是在收到设置用户的回应的 "result" 的 iq 包才能发送的,并且这个 iq 包中的 id 要和自己请求添加好友时的包 id 一致才行。

clq
2020-10-19 16:30:17 发表 编辑

5.
这时候服务器可发送一个包给对方,提示对方也加自己好友。
服务器:
<presence id="19547f14-369c-4d4a-9e84-4d8a12f09be0" to="ccc@127.0.0.1" from="clq@127.0.0.1" type="subscribe">
  <status>您好, 我是clq. 我想添加您到我的联系人列表.</status>
  <x xmlns="vcard-temp:x:update">
    <photo/>
  </x>
  <c xmlns="http://jabber.org/protocol/caps" hash="sha-1" node="http://gajim.org" ver="EhDgXYarwDkGz8n/wbp2z37FJWE="/>
</presence>



如果对方同意就会回应两个包给服务器:

<presence id="4cx6D-51" to="clq@127.0.0.1" type="subscribed"/> //同意对方添加
id 是新的。

第二个包可以理解为给对方加注释别名,不过实际上是设置好友信息。实际上可以不发。

<iq id="AixN9-131" type="set">
  <query xmlns="jabber:iq:roster">
    <item jid="clq@127.0.0.1" name="clq">
      <group>Friends</group>
    </item>
  </query>
</iq>
可以看到这就是前述的“单个好友新增通知”。后面还有一些复杂的操作,主要是通知对方有没有加自己为好友。这其实没有必要,而且现代的 im 大都不支持这样做,特别是标准 xmpp 允许对方将自己地址簿中的好友删除,这显然是不合适的。

因此,xmppmini 并不支持后续的操作。

如果不想理会对方本来是可以不回应的,不过在有些服务器环境下比如 openfire 的时候,不回应的话,下次每次你上线的时候都会自动给您发一次。这显然是不符合现在的 im 系统现状的。为了兼容这种情况,还是明确拒绝得了。消息包为

<presence id="4cx6D-51" to="clq@127.0.0.1" type="unsubscribed"/> //拒绝对方添加

可以看到,只是 type 不同罢了。

另外这个提示对方也加自己好友的消息包可以由服务器多次发送,客户端是会多次提示的。
而客户端也可以通过发送以下包多次通过服务器发送这个提示。
这个包实际上是包3和包4的合集,实际上就是重复添加对方为好友的请求,实际上也就是说现在 xmpp 客户端普通是在请求好友时同时发送包3和包4。
clq
2020-10-28 15:36:19 发表 编辑

6.考虑兼容标准 xmpp 服务器的情况下
实际上众多复杂的好友相关操作信息包也只是由三种包组成。

一种是比较简单的 result 的 iq 包。
二是 "jabber:iq:roster" 好友信息 iq 包。
三是 presence 好友关注状态包。

而相关的各种操作是由用户与好友之前的“好友状态”(实际上是相互是否关注状态)决定的。这个状态客户端全部是通过"jabber:iq:roster" 好友信息 iq 包知晓的。
取得这种状态的方法有两个:一是取好友列表,二是取单个好友状态。而取单个好友状态实际上就是添加好友的消息包,服务器对于好友不同的回应也是不同的。

首先,这个状态为消息包中的节点 subscription 的值。
有 none, both, from, to 四种,实际上还有 remove 。

//subscription 关系到联系人状态信息的传输,有 none, both, from, to 四种,实际上还有 remove
//remove 就是对方删除了你的好友。
//none 是你刚建立的地址,但服务器还没同意是否加入你的好友地址簿,要你发出 presence 请求后才修改为 to
//none 也有可能在对方已经删除你的请求下由服务器主动发过来。注意还不是 "remove"。只有对方也主动删除后才会是 "remove"。
//to 表示你加了对方,但对方没加你。并且要对方回复过同意的 presence 包。这时你可看到对方是否上线。
//from 表示对方加了你,你还没加对方。并且要你回复过同意的 presence 包。这时对方可以看到你是否上线。
//both 这时才是双方互相加了好友。 你发送了 jabber:iq:roster set 请求在服务器中加对方地址,同时发送 presence 要求知道对方在线状态,并且对方回复了同意的 presence 包。
//同时对方也这样做了。这种情况下才会是 both 。

//所以 xmppmini 的处理逻辑是,只要 subscription 不是 remove 就应该让对方的消息通过。因为这时候不是你添加过对方,就是你同意过对方添加。
//但只要状态不是 both 就提示你要加一下对方好友。
//所以当明确对方是好友,而状态不是 both 时最好是依次发送三个包:
//同意对方加自己的包(不论对方是否真的请求过);加对方入地址的 jabber:iq:roster set 包;提示对方加自己的 presence 包。


客户端“添加好友”、“设置好友备注”、“取好友间关注状态”实际上是同一个包。可以理解为:“我要设置某个地址为我的好友,备注名为什么什么”。这时候服务会告诉你
“好的,你们现在是好友了,好友间状态是什么什么”另外根据不同的场景还有可能告诉客户端好友的备注是什么什么。之所以不是每次都返回备注,是因为,服务器可能主动发一个包来告诉客户端,对方已经删除了你之类的,其中只包括了好友间的关注状态。

而且,收到这个包时。服务器还会判断是否你加过对方好友(即 subscription 为 none ),如果没有,还会发一个 presence 包给对方,提示对方也加你的好友。

所以说,对于标准 xmpp 来说,这个 subscription 的值是很重要的。

而在 xmppmini 中,我们可以利用这一点来处理好友间的关系。当本地缓存的 subscription 不是 both 时都自动发送一个 "jabber:iq:roster" 的 set 类型 iq 包去设置用户的备注。这样服务器会回一个相同类型的包给我们,我们就知道好友当前与我们的状态了,如果不是 both ,那么我们就需要告诉用户消息有可能无法送达(实际上标准 xmpp 是可以的,不过我们 xmppmini 中必须限制,因为垃圾消息太多了)。同时提醒用户发送一个提醒对方加了我方为好友的 presence 包。

目前的标准 xmpp 服务器会在客户端登录发送 presence 在线包时会判断如果我加了对方对方没加我,就自动提示对方添加自己好友,这样做显然并不好,这不是间接提示对方说我上线了嘛。应当提示用户,让用户自己决定是否提示对方。现在的标准 xmpp 服务器也会在回复的 "jabber:iq:roster" 中说明是否发送过添加好友的请求。也就是 ask 节点中的内容。




clq
2020-10-28 15:47:47 发表 编辑

7.删除好友
有了以上的知识,删除好友就容易理解了。
客户端发送:
<iq xmlns="jabber:client" type="set" id="1674169c-e422-4a05-abd9-a8a90cc75d24" from="clq@127.0.0.1/gajim.6929J2FG">
<query xmlns="jabber:iq:roster">
<item jid="ccc@127.0.0.1" subscription="remove" />
</query>
</iq>

服务器回应两个包。

<iq type="set" id="809-61" to="clq@127.0.0.1/gajim.6929J2FG">
<query xmlns="jabber:iq:roster">
<item jid="ccc@127.0.0.1" name="ccc" ask="unsubscribe" subscription="none">
<group>Friends</group>
</item>
</query>
</iq>


<iq type="set" id="700-64" to="clq@127.0.0.1/gajim.6929J2FG">
<query xmlns="jabber:iq:roster">
<item jid="ccc@127.0.0.1" subscription="remove" />
</query>
</iq>

同时还会给对方一个 set 的好友信息包,是将状态改为 "none",注意还不是 "remove"。只有对方也主动删除后才会是 "remove"。

clq
2020-10-28 16:57:40 发表 编辑

8.
"jabber:iq:roster"
如果是 get 后面的 item 是没有用处的,服务器会返回全部好友信息 .

另外,对于 openfire 这样的标准 xmpp 服务器,如果添加的用户域名是它的它就会判断用户是否存在。不存在的话就不允许添加。反而是不在服务器上的能加上去。
而 xmppmini 中,这是不检测的。客户端主动添加了,就加入到地址簿中。这样才合理。

服务器最好也不要提示是否有这个用户,那样就给黑客留下了机会。相当于告诉它 “没错,有这个手机号,来骚扰它吧。”


总数:4 页次:1/1 首页 尾页  
总数:4 页次:1/1 首页 尾页  


所在合集/目录
xmpp协议最小实现合集 更多
xmppmini协议整理文档 更多



发表评论:
文本/html模式切换 插入图片 文本/html模式切换


附件:



NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.

Copyright © 2005-2020 clq, All Rights Reserved
版权所有
桂ICP备15002303号-1