网友 t8500071说asmack有更新了,去http://code.google.com/p/asmack/看了下,现在代码转到 https://github.com/Flowdalic/asmack
所以大家可以直接去下官方的测试,下面的没啥用了。。
之前记录了部分的怎么用smack进行连接、登录、聊天等等,这边就接着说文件传输,看官方的文档,
http://www.igniterealtime.org/builds/smack/docs/latest/documentation/extensions/index.html
看完文档你会觉得好简单呐,不过要操作起来真的挺要命的,至少我挺纠结的。
先说步骤
1、创建一个文件传输管理的类
// Create the file transfer manager
FileTransferManager manager = new FileTransferManager(connection);
2、从这个管理类创建一个向 特定用户 发送文件的输出类
// Create the outgoing file transfer
OutgoingFileTransfer transfer = manager.createOutgoingFileTransfer("romeo@montague.net"); //这边用户要为 完整的jid,user@servername/resource
// Send the file
transfer.sendFile(new File("shakespeare_complete_works.txt"), "You won't believe this!");
3、接收文件即直接加个监听器
// Create the listener
manager.addFileTransferListener(new FileTransferListener() {
public void fileTransferRequest(FileTransferRequest request) {
// Check to see if the request should be accepted
if(shouldAccept(request)) {
// Accept it
IncomingFileTransfer transfer = request.accept();
transfer.recieveFile(new File("shakespeare_complete_works.txt"));
} else {
// Reject it
request.reject(); // 拒绝接收文件。
}
}
});
4、查看文件收发的状态
while(!transfer.isDone()) { //可以直接加到 transfer.sendFile 或者 transfer.recieveFile 后面。
if(transfer.getStatus().equals(Status.ERROR)) {
System.out.println("ERROR!!! " + transfer.getError());
} else {
System.out.println(transfer.getStatus());
System.out.println(transfer.getProgress());
}
sleep(1000);
}
如果是smack在Pc运行的话,服务器配置没问题,一般就没问题了。<1>
我这边的库可以了,我会上传供大家测试哈。
在文件传输这个环节,服务器的配置挺还是很重要的,因为文件传输使用到了两种协议,IBB协议和S5B协议,协议介绍看这里。
其中在文件传输过程中,由更顶层协议去裁判,是否传输文件,如果是则使用哪种协议进行传输,(如果S5B可用,默认S5B),我使用的这个库也是这样。我尝试过很多版本的asmack库,都不行,衰人一个啊。后面就还是慢慢来吧,看下源码好了。我说下我遇到情况。
出现错误且里面 有说到<streamhost jid="myid@mydomain.com/smack" host="127.0.0.1" port="7777"/>,就是asmack库里面的一个函数错了,直接返回127.0.0.1这个IP,修改org.jivesoftware.smackx.filetransfer.Socks5TransferNegotiator中的函数:
private String discoverLocalIP() throws UnknownHostException {
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress()) {
return inetAddress.getHostAddress().toString();
}
}
}
} catch (SocketException ex) {
Logger.error("Error retrieving the local IP", ex);
}
throw new UnknownHostException("Failed to retrieve local IP");
//return InetAddress.getLocalHost().getHostAddress(); //注释掉原本的代码
}
这样保证返回的android机子的ip不是127.0.0.1。
因为在S5B协议里面,有一个<streamhost host="" jid="" port="" />的元素,这个是用于告诉对方本机可以用于建立文件传输的stream流的host,如果你发送了一个host为127.0.0.1的话,那对方在判断这个地址的时候,会误判为可以用(应该是这个地址是特殊地址,本地回路嘛),然后对方会回复一个result给你告诉你他那边确认OK,然后这个时候,不论是你还对方,都无法把stream流建立起来的。所以上面就是为了屏蔽127.0.0.1,获得真实的本地IP。
上面的解决办法是可以行的。不过还有一种方法就是直接不让你这个客户端找到本地ip,这样你这边就没有<streamhost host="" jid="" port="" />这个东西了,你就会去向服务器请求,查询服务器的代理,一般会有一个proxy.server-name这样的一个代理,这个代理的ip來至哪里?看openfire服务器,你很容易可以发现,服务器的server-name就是你的计算机名字,在/etc/hosts里面添加你的当前PC的IP和计算机名,之后重启服务器,服务器就会将server-name绑定到当前的IP上,proxy.your-openfire-host这个代理的IP跟服务器的IP一样,这样客服端就可以找到proxy.server-name代理的IP,之后两个客服端就会建立一个用使用代理而建立起来的stream流,然后开始传输文件。
当然,上面的方法还是挺OK的,不过我还发现了一个方法,其实也就是在你不让客服端不发现的本地IP和proxy.server-name所对应的IP为NULL的时候,客户端会放弃使用S5B,而使用IBB,这个使用协议是默认的,名字翻译过来就是带内字节流,带内?带内我个人理解是把要发送的文件分割,然后放在packet里面进行传输,而不像S5B那样再建立一个stream流。如果你用IBB发送文件的时候,你打开DEBUG,你就可以在发送的packet里面看到一堆被格式化过的文字,那些就是被传输文件的数据啦。
我自己改的 asmack :http://download.csdn.net/detail/shmcclmm/5046135
我修改的是 org.jivesoftware.smackx/bytestreams/Socks5BytestreamManager.java 这个文件