Openfire (Wildfire) Connnection Manager目前还没有公开的JEP,所以把它的协议分析了一遍如下:
第一步:建立 socket 连接握手,一个 connection manager 可以跟服务器建立多个连接
1. 连接c2s
// to="cm1/socket1" 实际是是填写cm的名称(cm1)和当前 socket 的别名(socket1)
<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:connectionmanager" to="cm1/socket1" version="1.0">
s2c:
// id="d2a9bc17" 中的 stream id 用来下一步验证
<?xml version='1.0' encoding='UTF-8'?>
<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:connectionmanager" from="cm1/socket1" id="d2a9bc17" version="1.0" >
s2c: (tls, compress, etc)
<stream:features></stream:features>
2. 验证
c2s:
// 密码算法:sha1(streamid + password)
<handshake>f182e437fd8fbb0dec51667ff6e8ffd657cab9bb</handshake>
s2c:
<handshake></handshake>
// 表示验证通过,其他格式表示失败。
3. features
last step: server 告诉cm, server支持的features, 如果多个 socket 连接可能后面的 socket 不会收到这个包
s2c:
<iq type="set" id="143-23">
<configuration xmlns="http://jabber.org/protocol/connectionmanager">
<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>
<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
<mechanism>PLAIN</mechanism>
<mechanism>CRAM-MD5</mechanism>
<mechanism>DIGEST-MD5</mechanism>
<mechanism>ANONYMOUS</mechanism>
</mechanisms>
<compression xmlns="http://jabber.org/features/compress"><method>zlib</method></compression>
<auth xmlns="http://jabber.org/features/iq-auth"/>
<register xmlns="http://jabber.org/features/iq-register"/>
</configuration>
</iq>
c2s: cm 简单原包应答即可
<iq type="result" id="143-23" to="wildfire.server" from="cm1/socket1">
<configuration xmlns="http://jabber.org/protocol/connectionmanager">
<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>
<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
<mechanism>PLAIN</mechanism>
<mechanism>CRAM-MD5</mechanism>
<mechanism>DIGEST-MD5</mechanism>
<mechanism>ANONYMOUS</mechanism>
</mechanisms>
<compression xmlns="http://jabber.org/features/compress"><method>zlib</method></compression>
<auth xmlns="http://jabber.org/features/iq-auth"/>
<register xmlns="http://jabber.org/features/iq-register"/>
</configuration>
</iq>
第二步:代理 Jabber client 登录
1. 有个 client 连接过来了, CM 通知 Server open session
c2s:
<iq type="set" to="wildfire.server" from="cm1/socket1" id="90-0">
<session xmlns="http://jabber.org/protocol/connectionmanager" id="cm1d99f71af"><create/></session>
</iq>
s2c:
<iq type="result" id="90-0" from="wildfire.server" to="cm1/socket1">
<session xmlns="http://jabber.org/protocol/connectionmanager" id="cm1d99f71af"><create/></session>
</iq>
这时候 wildfire cm 的界面看到 client sessions = 1, 但是 sessions 的界面看不到 client
注意 cm1d99f71af 就是 client 的 streamid, 用来区分 client
在以后的包都放在 route streamid 中
2. auth client 客户端验证
c2s:
<route to="wildfire.server" from="cm1/socket2" streamid="cm1d99f71af">
<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">dGVzdDFi4zZwB0ZXN0TimsFakePacket</auth>
</route>
// 算法: base64.encode(username+@+host+/0+username+/0+password)<
s2c: success
<route from="wildfire.server" to="cm1" streamid="cm1d99f71af">
<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>
</route>
3. bind resource 便于c/s正式沟通
c2s:
// 从现在开始, iq id 从一个随机数递增
<route to="wildfire.server" from="cm1/socket3" streamid="cm1d99f71af">
<iq xmlns="jabber:client" id="cF25i-1" type="set">
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
<resource>TimAtHome</resource>
</bind>
</iq>
</route>
s2c:
<route streamid="cm1d99f71af" from="wildfire.server" to="cm1">
<iq type="result" id="cF25i-1" to="wildfire.server/cm1d99f71af">
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>[email protected]/TimAtHome</jid></bind>
</iq>
</route>
4. create session
按照 xmpp 这个应该在 auth 之后发出, 放在auth后第1步 (rfc3921 section[3])
但是为了构建完整的jid, 具备 resource, 所以放在 resource bind 之后
c2s:
<route to="wildfire.server" from="cm1/socket4" streamid="cm1d99f71af">
<iq xmlns="jabber:client" id="cF25i-0" type="set">
<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>
</iq>
</route>
s2c
<route streamid="cm1d99f71af" from="wildfire.server" to="cm1">
<iq type="result" id="cF25i-0" to="[email protected]/TimAtHome"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>
</route>
5. send presense, 用户的好友看到用户在线
c2s:
<route to="wildfire.server" from="cm1/socket1" streamid="cm1d99f71af">
<presence xmlns="jabber:client" id="cF25i-8"><status>Online</status>
</presence>
</route>
以后所有c/s之间的包 cm 只是简单封装,不再描述。
注意 cm 的客户端异常断线后,收到服务器发给该client的包需要给服务器回复一个错误信息。