android 使用asmack库进行文件收发

来源:互联网 发布:网络教育概念股 编辑:程序博客网 时间:2024/06/13 07:11

网友 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 这个文件