clq
浏览(418) -
2020-01-02 15:34:41 发表
编辑
关键字: xmpp_doc
[2020-07-17 00:56:45 最后更新]
[xmppmini]xmpp服务器间的通信过程
其实比较复杂,感觉不用为好。
1.端口
明文端口为 5269 并且有可能使用了 STARTTLS。
2.首先由发送方(socket主动连接者)说话
其中有可能有讨厌的
jabber:server:dialback
在协议中发送者称之为 Originating Server,接收者为 Receiving Server(socket被动监听者)
有些文章里还会提到一个授权服务器,其实一般情况下就是发送者。只不过要接收者另外开一个 socket 去重连验证罢了,这是一个重要而很不容易理解的地方。
其实根据我的测试,至少对于 openfire 来说,接收者不去验证发送者,直接返回验证通过的信息也是可以的。
3.以 openfire 为例,对方第一句就会要求接收者实现回拨(或者是 ssl),所以服务器间的通信实际上就是要实现这个 xmpp 回拨。
至于其他的消息处理和客户端没有太大的区别,实现了这个回拨其他内容基本上不用看了。
4.所以第一句话非常重要
目前已知对于 openfire 服务器,有三种不同的第一句话会让它产生三种不同的处理方式和回应的信息,它们的处理大相径庭。
以向 openfire 服务器发送一条消息为例。第一句话是由发送者说的,不同的内容引起的处理流程如下:
第一种,明确说明自己有 STARTTLS 功能或者没有说明自己有回拨功能的情况下,会进行 STARTTLS 方式对话。过程比较啰嗦,先忽略。
第二种,明确说明自己有回拨功能并且没有说明自己有 STARTTLS 功能的情况下,对方是要回来连接自己的 5269 端口的,如果连接不上会回应以下消息
<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">Unable to connect to authoritative server</text></error></db:result>
例
<stream:stream xmlns:db="jabber:server:dialback" xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:server" from="vm2" to="vm1" version="1.0">
第三种,没有明确说明自己有回拨功能和 stl 功能,并且没有 version 为 1 的说明。这时候 openfire 应该是将它做为老式的服务器直接关闭了。
(未太仔细测试,似乎有时候会当做第二种方式)
例
<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:server" from="vm2" to="vm1">
如果是第三种情况下的(第一句话),其实可以尝试一下发送 message
应该不行,对方还是返回 "</stream:stream>" 并关闭
也就是说,对于 openfire 来说,传统文档所述的 xmpp 服务器交互过程已经过时了,不再支持,所以要不我们实现回拨,要不我们就要实现 ssl ,这就是为什么按网上的文档无法实现服务器交互过程的根本原因。
总之,以目前的国内环境来说,还是用第二种只使用回拨的方式为好。如果是用 STARTTLS 会有更多的麻烦等者大家。
5.接收者者收到第一条消息后的回应
即接收者的第一条回应信息。其实,以目前的 openfire 其实来说,接收者需要回应两条信息,一条是 stream:stream 说明流开始,另一条是 stream:features 说明自己支持的功能。
其中第二条会包含 starttls 和 dialback 。这里要注意,如果是我们实现的接收者想用回拨的方式的话,一定不要这个节中包含 starttls 。
例
<stream:stream xmlns:db="jabber:server:dialback" xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:server" from="vm1" to="vm2" id="7kmgufhbib" version="1.0"><stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"></starttls><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/><dialback xmlns="urn:xmpp:features:dialback"><errors/></dialback></stream:features>
6.发送者发送第二条消息
发送服务器向接收服务器发送一个key
<db:result
to='Receiving Server'
from='Originating Server'>
98AF014EDC0...
</db:result>
//消息体是一个 key ,发送给对方去验证
<db:result to="vm1" from="vm2">98AF014EDC0</db:result>
7.接收方得到 key 后去验证(自己开个 socket 去连接发送者的监听端口发些消息来验证,详见后面的参考文档,比较简单可以自己调试,在这里说的话就太容易混乱了)。验证通过的话会发送
<db:result from="vm1" to="vm2" type="valid"/>
(注:其实以国内的环境来说,反向再去连接对方的服务器,因为众所周之的原因十有八九是连接不上的,所以不验证直接返回成功消息也是可以的。另外局域网多级的情况下也是不可能回拨成功的)
8.发送端得到上面的命令后就完成了整个服务器与服务器的通道打通工作(这是回拨的实现)。这时候直接发送 message 体给接收者,就可以将消息发送给对方的客户端了。
这里还有一个要注意的地方:如果消息体的内容不合法,openfire 会无情的返回流关闭消息,关闭 socket ,让你以为这些过程出错了 ... 其实只是你最后的消息体有误罢了。
所以 openfire 这东东恐怕还是不够成熟,另外测试中还发现,如果 openfire 无法将客户端的消息发送给其他服务器的话(在这个回拨中有误连接不上的话),它是不会告诉客户端的。
客户端会傻傻的以为对方已经收到了,继续发下一条消息 ... 所以想要将 xmpp 在自己的项目中实用的话,回应确认恐怕是要自己加上的。
--------------------------------------------------------
容易出错的地方
很多教程都在解释回拨的时候说,接收者会返回
<stream:stream ... 其中带有 id ,但实际上光有这一句还是不行的。原因不明,改天再查协议文档吧。
//网上非常多的教程都没有这句,但从调试 openfire 中可以检测到。没有这句的话至少 openfire 是不会返回信息的
"<stream:features><mechanisms xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"><mechanism>PLAIN</mechanism></mechanisms><dialback xmlns=\"urn:xmpp:features:dialback\"><errors/></dialback></stream:features>" +
----
就是说实际上要返回两个 stream 头。
2020.07.17 更新:这两个 stream 实际还是不同的,一个是 stream:stream, 一个是 stream:features , 其中 stream:stream 要让对方回调的话是一定要有 id 节的。今天调试了很久已证实。
另外 starttls 和这个回调并不冲突,只不过是要先完成 starttls 再进行这个 stream 的游戏。也就是说,当接收服务器说自己有 starttls 的时候,stream 就不只是两个了。
这个 id 节,在 RFC3920 中称之为“标签”。从协议来看,主动连接方的第一句 stream 中应该是不用带 id 的(回应者必须带)。
"
步骤 2: Server2 发送一个流标签给 Server1 作为应答:
"
文档实在太长,我放在了 http://newbt.net/ms/vdisk/show_bbs.php?id=94A564791F84506B279A075471AD0E9D&pid=160
clq
2020-01-03 13:56:48 发表
编辑
参考 https://www.iteye.com/blog/willberpanmsn-2258795
注意,这是有误的,原因见上面。
--------------------------------------------------------
XMPP之服务器回拨
1.流程:
发起服务器和接收服务器建立一个连接。
发起服务器通过到连接发送一个 'key' 值给接收服务器。
接收服务器建立一个连接到授权服务器。
接收服务器发送一个相同的 'key' 值给授权服务器。
授权服务器回答这个key是否合法。
接收服务器通知发起服务器是否被验证通过。
我们用用以下图形展示这个事件流程:
流程图:
1.
1.发起服务器向接收服务器建立连接
2.发送头
<stream:stream
xmlns:stream='http://etherx.jabber.org/streams'
xmlns='jabber:server'
xmlns:db='jabber:server:dialback'>
注意: 'to'和'from'属性在流的根元素是可选的(OPTIONAL). 其中包含的xmlns:db名字空间向接收服务器声明了发起服务器支持回拨. 如果名字空间不正确,接收实体必须生成一个<invalid-namespace/>流错误条件并且终止XML流和相应的TCP连接.
3.接收服务器返回头
<stream:stream
xmlns:stream='http://etherx.jabber.org/streams'
xmlns='jabber:server'
xmlns:db='jabber:server:dialback'
id='457F9224A0...'>
注意: 'to'和'from'属性在流的根元素是可选的(OPTIONAL). 如果名字空间不正确,发起服务器必须生成一个<invalid-namespace/>流错误条件并且终止XML流和相应的TCP连接.也要注意,在这里接收服务器应该应答但是可以出于安全策略考虑只是悄悄地终止XML流和TCP连接;无论如何,如果接收服务器希望继续,它必须回送一个流头信息给发起服务器.
4.发送服务器向接收服务器发送一个key
<db:result
to='Receiving Server'
from='Originating Server'>
98AF014EDC0...
</db:result>
5.接收服务器向发起服务器所在的域建立TCP连接,连接到的是授权服务器(由于授权服务器可以是发起服务器,所以可以使用重用现有连接)。
6.接收服务器向授权服务器发送一个流头
<stream:stream
xmlns:stream='http://etherx.jabber.org/streams'
xmlns='jabber:server'
xmlns:db='jabber:server:dialback'>
7.授权服务器返回一个流头
<stream:stream
xmlns:stream='http://etherx.jabber.org/streams'
xmlns='jabber:server'
xmlns:db='jabber:server:dialback'
id='1251A342B...'>
如果一个流错误发生在接收服务器和 授权服务器 之间,接收服务器必须生成一个 <remote-connection-failed/> 流错误条件并且终止它和 发起服务器 之间的XML流和相应的TCP连接.
8.接收服务器把从发起服务器发过来的key发给授权服务器
<db:verify
from='Receiving Server'
to='Originating Server'
id='457F9224A0...'>
98AF014EDC0...
</db:verify>
9.授权服务器验证key是否合法:
<db:verify
from='Originating Server'
to='Receiving Server'
type='valid'
id='457F9224A0...'/>
或
<db:verify
from='Originating Server'
to='Receiving Server'
type='invalid'
id='457F9224A0...'/>
10.如果成功 接收服务器向发起服务器发起通知
<db:result
from='Receiving Server'
to='Originating Server'
type='valid'/>
NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.