解决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。还希望看到得大牛指点一二。

### AVDTP 和 A2DP 蓝牙音频协议的区别 #### 协议定位 AVDTP(Audio/Video Distribution Transport Protocol)主要负责定义蓝牙设备间媒体流传输的具体机制,包括参数协商、建立连接和数据传输过程中的信令交互。而A2DP(Advanced Audio Distribution Profile)则是更高层次的应用层配置文件,专注于规定如何通过蓝牙技术实现高质量立体声音频的分发[^1]。 #### 功能侧重点 - **AVDTP** - 定义了蓝牙设备之间数据流句柄的参数协商、建立和传输过程以及相互交换的信令实体形式。 - 是A2DP框架的基础协议,在实际操作中提供了必要的技术支持来确保音频能够顺利地从源端发送到接收端。 - **A2DP** - 主要关注于提供一种标准方法让支持此配置文件的不同品牌型号间的蓝牙装置可以互相传递高品质的音乐内容。 - 不涉及具体的底层通信细节而是依赖像AVDTP这样的基础协议完成其功能需求[^2]. #### 应用场景 当提到具体应用场景时,两者也有所区分: - 对于希望开发兼容多种品牌的无线耳机或扬声器产品的制造商来说,遵循A2DP是非常重要的;而对于那些致力于优化这些产品内部工作流程的技术人员而言,则更关心AVDTP所提供的特性[^3]. ```python # Python伪代码展示两者的不同作用范围 class BluetoothDevice: def __init__(self, name): self.name = name def setup_stream(device_a, device_b): # 基于AVDTP的操作 print(f"{device_a.name} and {device_b.name} are negotiating stream parameters.") def play_audio(source_device, target_device): # 基于A2DP的行为 print(f"Now playing audio from {source_device.name} to {target_device.name}.") ```
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值