解决asmack在android上收发文件的negotiating stream问题


这两天在研究openfire+spark+smack(asmack),自己搭建一个IM聊天系统。

asmack在2010年底就不更新了。遗留了很多的bug。

在从spark发送文件到android的时候。状态一直阻塞在 negotiating stream几秒之后。传输结束。返回状态为Error并且拿不到错误消息。折腾了几天。论坛翻了个痛。

参照好多大牛说改写org.jivesoftware.smackx.filetransfer.Socks5TransferNegotiator.discoverLocalIP()方法。尼玛我搞的源码的Socks5TransferNegotiator这个类里根本没有discoverLocalIP()方法!

最后在反复尝试下。我搞出了两种方法。

1.这种方法比较流氓。

大致的意思是,你丫收不到就继续收。收到为止。代码片段如下。

public static class RecFileTransferListener implements FileTransferListener {

		@Override
		public void fileTransferRequest(FileTransferRequest request) {
			Log.i("info", "接收文件开始.....");
			final IncomingFileTransfer inTransfer = request.accept();
			final String fileName = request.getFileName();
			long length = request.getFileSize();
			Log.i("info", "文件大小:" + length + "  " + request.getRequestor());
			Log.i("info", "" + request.getMimeType());
			try {

				final File file = new File("/sdcard/" + fileName);
				Log.i("info", file.getAbsolutePath());
				new Thread(){

					@Override
					public void run() {
						// TODO Auto-generated method stub
						try {
							inTransfer.recieveFile(file);
							while (!inTransfer.getStatus().equals(Status.complete)) {
								if (inTransfer.getStatus().equals(Status.error)) {
									Log.e("error","ERROR!!! " + inTransfer.getError());
									inTransfer.cancel();
									inTransfer.recieveFile(file);
								} else {
									Log.i("info", "status:" + inTransfer.getStatus());
									Log.i("info", "process:" + inTransfer.getProgress());
								}
							}
							if (inTransfer.isDone()) {
								Log.i("info", "Done+status:" + inTransfer.getStatus());
								Log.i("info",
										"Done+process:" + inTransfer.getProgress());
							}
						} catch (XMPPException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
					
				}.start();

			} catch (Exception e) {
				e.printStackTrace();
			}

		}

	}
这种方法可以再第二次执行recieveFile的时候收到该文件。

2.修改asmack的源码。

我下载的已经是别人改过的。但是在这里也是不能用。只能自己看着改了。照猫画虎了。

源码来源http://www.eoeandroid.com/thread-186418-1-1.html

从recieveFile一步一步debug,最后发现在FaultTolerantNegotiator.createIncomingStream()方法执行时会报ExecutionException:No response from remote client

下面是改好的方法

 public InputStream createIncomingStream(StreamInitiation initiation) throws XMPPException {
        PacketCollector collector = connection.createPacketCollector(
                getInitiationPacketFilter(initiation.getFrom(), initiation.getSessionID()));
        System.out.println("From:"+initiation.getFrom());
        System.out.println("SessionID:"+initiation.getSessionID());
        connection.sendPacket(super.createInitiationAccept(initiation, getNamespaces()));

        ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(2);
        CompletionService<InputStream> service
                = new ExecutorCompletionService<InputStream>(threadPoolExecutor);
        List<Future<InputStream>> futures = new ArrayList<Future<InputStream>>();
        InputStream stream = null;
        XMPPException exception = null;
        try {
            futures.add(service.submit(new NegotiatorService(collector)));
            futures.add(service.submit(new NegotiatorService(collector)));

            int i = 0;
            while (stream == null && i < futures.size()) {
                Future<InputStream> future;
                try {
                    i++;
//                    future = service.poll(10, TimeUnit.SECONDS); 
                    future = service.take();
                }
                catch (InterruptedException e) {
                    continue;
                }

                if (future == null) {
                    continue;
                }

                try {
                    stream = future.get();
                }
                catch (InterruptedException e) {
                    /* Do Nothing */
                }
                catch (ExecutionException e) {
                    exception = new XMPPException(e.getCause());
                }
            }
        }
        finally {
            for (Future<InputStream> future : futures) {
                future.cancel(true);
            }
            collector.cancel();
            threadPoolExecutor.shutdownNow();
        }
        if (stream == null) {
            if (exception != null) {
                throw exception;
            }
            else {
                throw new XMPPException("File transfer negotiation failed.");
            }
        }

        return stream;
    }
 future = service.poll(10, TimeUnit.SECONDS); 
改为
 future = service.take();
这两句区别在于poll取task有超时时间为10s

take没有设置超时时间,知道有下一个task返回为止。

另外还要修改另一个地方的代码。

org.jivesoftware.smack.PacketCollector的nextResult(long timeout)方法。将poll的时间增加10s

 public Packet nextResult(long timeout) {
        long endTime = System.currentTimeMillis() + timeout;
        do {
            try {
                return resultQueue.poll(timeout+10000, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) { /* ignore */ }
        } while (System.currentTimeMillis() < endTime);
        return null;
}
OK.这样再把他生成jar然后build进你的项目里。这个该死的negotiating stream就不会出现了。

具体的原因我也大搞不懂为什么这个改就OK。还希望看到得大牛指点一二。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值