客户端A向B 发送文件的过程:
两个XMPP节点之间传输文件一般有三种方式:
- Out-of-Band Data 具体见XEP-0066: 点击打开链接
- in-band bytestream 适合传输较小的数据,通过直接携带在XML中进行传输 具体见:XEP-0047:点击打开链接
- socks5 通过服务器代理的方式,实现两个节点之间的直接传输
以下为通过socks5 方式传输文件的过程:
1.A向B发送请求查询B是否支持字节流。
2.B返回应答
3.A向服务器发送请求,查询可用的代理
4.服务器应答,返回目前disco列表中所有可用的服务项
5.A验证服务器中每一项是否是字节流代理
6.服务器应答每一项的查询结果
7.A发送请求,查询代理服务器的网络地址
8.服务器返回代理服务器的网络地址(包括IP和端口)
9.A通知B代理服务器的地址
10.B与代理进行验证和连接初始化
一旦A通知完B后,B就要主动与代理服务器建立socks5连接。
代理服务器中保持有一个ProxyConnectionManager,它绑定到一个IP和端口,然后启动一个线程不断地监听这个端口。在监听到B的连接后就调用processConnection()来处理。先验证是否为socks5类型的连接,再读取支持的验证方式,然后生成一个ProxyTransfer对象。用到B的socket连接来初始化此对象的outputStream,存入map中备用。
synchronized (connectionLock) {
ProxyTransfer transfer = connectionMap.get(responseDigest);
if (transfer == null) {
transfer = createProxyTransfer(responseDigest, connection);
transferManager.registerProxyTransfer(responseDigest, transfer);
connectionMap.put(responseDigest, transfer);
}
else {
transfer.setInputStream(connection.getInputStream());
}
}
11.B通知A自己与代理建立了连接
12.A与代理服务器建立连接
A接到通知后,主动与代理服务器建立连接。此时代理会用此连接来初始化已有ProxyTransfer对象的inputStream。如此一来ProxyTransfer就同时持有到A的inputStream和到B的outputStream。[参见上面的代码]
13.A发送请求给代理服务器,激活传输流
由DefaultProxyTransfer.doTransfer()方法来处理。不断从服务器与A建立的inputStream中读取数据,然后写入与B建立的outputStream中。<iq id="**" to="proxy.kinglong" type="set"> <query xmlns="http://jabber.org/protocol/bytestreams" sid="jsi_31243243242"> <activate>recever@kinglong/Spark 2.6.3</activate> </query> </iq>
do { // write to the output stream out.write(b, 0, count); amountWritten += count; // read more bytes from the input stream count = in.read(b); } while (count >= 0);
14.服务器通知A激活的结果
15.A与B开始传输
原理上讲流被激活,传输就已经开始了。
PS:对于离线文件的传输个人认为比较好的方法就是建立一个专门用于文件传输的客户端。此客户端一直连接到openfire上,当有离线文件需要发送时先将文件发往此客户端,并在数据库中建立离线发送机制。当对方上线后读取数据库中的信息,再由此客户端发送给目标节点。这样做的好处就是最终文件的存储方式可自己决定,而且最重要的是简单!
实际商业环境中多不采用XMPP文件传输协议,而是通过http,ftp等其他方式进行文件上传下载,XMPPServer只作为文件信息的传输中介。
更多详细分析敬请期待.....