Java Smack 4.1.0 beta-1上的文件传输失败

Java Smack 4.1.0 beta-1上的文件传输失败,java,xmpp,openfire,smack,Java,Xmpp,Openfire,Smack,我正在开发一个涉及两个Smack客户端的应用程序,其中一个客户端试图向另一个客户端发送jar文件。我写这篇文章是作为桌面应用程序的一部分,而不是Android应用程序的一部分(我想我会做出区分,因为StackOverflow上的大多数Smack查询都与Android相关)。我使用OpenFire3.9.3作为XMPP服务器。对于所有代码示例,我都使用以下Smack库(版本4.1.0-beta 1):提供的Smack-java7、Smack tcp、Smack扩展和Smack sasl。以下是Ou

我正在开发一个涉及两个Smack客户端的应用程序,其中一个客户端试图向另一个客户端发送jar文件。我写这篇文章是作为桌面应用程序的一部分,而不是Android应用程序的一部分(我想我会做出区分,因为StackOverflow上的大多数Smack查询都与Android相关)。我使用OpenFire3.9.3作为XMPP服务器。对于所有代码示例,我都使用以下Smack库(版本4.1.0-beta 1):提供的Smack-java7、Smack tcp、Smack扩展和Smack sasl。以下是OutgoingFileTransfer的代码(注意,在所有代码示例中,Smack用户名等信息都已匿名化):

此代码的输出为:

将test.jar发送到receiver@local_openfire/扑通

14:09:43.748信息-文件传输状态:初始

14:09:44.249信息-文件传输状态:协商流

14:09:44.751信息-文件传输状态:协商流

//此消息持续几秒钟,直到最后

14:09:53.805信息-文件传输状态:协商流

14:09:54.308信息-文件传输到receiver@local_openfire/扑通 完成

14:09:54.309错误-org.jivesoftware.smack.packet。XMPPError@30e95075

14:09:54.310错误-描述性文本:空

14:09:54.310错误-条件:服务不可用

14:09:54.310错误-类型:取消

对于收入文件传输,代码为:

@Override
public void fileTransferRequest(FileTransferRequest request)
{
    final String requestorId = request.getRequestor();
    LOG.info("FileTransferRequest from: " + requestorId);
    // Only respond to requests from the sender
    if (requestorId.contains(senderId))
    {
        final IncomingFileTransfer transfer = request.accept();
        LOG.info("FileTransferRequest accepted");
        try
        {
            final String fileName = transfer.getFileName();
            transfer.recieveFile(new File(fileName));
            LOG.info("Incoming file transfer: " + fileName);
            LOG.info("Transfer status is: " + transfer.getStatus());
            while (!transfer.isDone())
            {
                final double progress = transfer.getProgress();
                final double progressPercent = progress * 100.0;
                String percComplete = String.format("%1$,.2f", progressPercent);
                LOG.info("Transfer status is: " + transfer.getStatus());
                LOG.info("File transfer is " + percComplete + "% complete");
                Thread.sleep(1000);
            }

            // Now that the file transfer is done check for errors
            // or exceptions
            FileTransfer.Error transferError = transfer.getError();
            if (transferError != null)
            {
                LOG.error("Transfer error occurred: " + transferError.getMessage());
            }
            Exception transferException = transfer.getException();
            if (transferException != null)
            {
                LOG.error("Transfer exception occurred: " + transferException);
                if (transferException instanceof SmackException.NoResponseException)
                {
                    SmackException.NoResponseException smackException = (SmackException.NoResponseException)transferException;
                    smackException.printStackTrace();
                }
            }
            LOG.info("FileTransfer complete");
            provisioningComplete = true;
        }
        // For now just logging exceptions
        catch (SmackException e)
        {
            LOG.error("SmackException trying to receive jar file", e);
        }
        catch (InterruptedException e)
        {
            // Do nothing
        }
        catch (IOException e)
        {
            LOG.error("IOException trying to receive jar file", e);
        }

    }
    else
    {
        LOG.warn("FileTransferRequest rejected");
        try
        {
            request.reject();
        }
        catch (NotConnectedException e)
        {
            LOG.warn("NotConnectedException when rejecting FileTransferRequest");
        }
    }
}
此代码的输出为:

14:09:43.766信息-文件传输请求来自: sender@local_openfire/扑通

14:09:43.767信息-已接受文件传输请求

14:09:43.768信息-传入文件传输:test.jar

14:09:43.769信息-转账状态为:正在协商转账

14:09:43.770信息-传输状态为:协商流

14:09:43.770信息-文件传输已完成0.00%

14:09:44.771信息-传输状态为:协商流

14:09:44.771信息-文件传输已完成0.00%

14:09:45.776信息-传输状态为:协商流

14:09:45.776信息-文件传输已完成0.00%

14:09:46.778信息-传输状态为:协商流

14:09:46.778信息-文件传输已完成0.00%

14:09:47.782信息-传输状态为:协商流

14:09:47.783信息-文件传输已完成0.00%

14:09:48.784错误-发生传输异常: org.jivesoftware.smack.SmackException:执行中出错

14:09:48.784信息-文件传输完成

在这段代码运行之后,在接收器端,我在当前工作目录中有一个名为“test.jar”的文件,文件大小为0字节。我在不同的机器上对发送者和接收者,以及在同一台机器上对发送者和接收者都进行了尝试。我最初使用的是Smack 4.0.6,但后来改用了最新的代码库(在撰写本文时为4.1.0-beta 1),希望这个bug能够得到解决。没有这样的运气。如果有任何建议,我将不胜感激。谢谢

更新日期1-30-2015 我再也看不到发送方的XMPPError了。相反,发送方仍然停留在文件传输状态:协商流状态。但是,接收器会出现以下错误:

SmackException.NorResponseException:在数据包回复中未收到响应超时。超时时间为5000ms(~5s)

我可以看到如何增加OutingFileTransfer类的超时,但不能增加IncomingFileTransfer类的超时

更新日期:2015年2月2日 我使用Smack调试工具捕获了原始XML节。为了简洁起见,我只包括那些与文件传输相关的信息(即,不包括在场信息或名册信息包)。这是:

<iq to="receiver@smack_server/Smack" id="NK8Lh-11" type="set" 
from="sender@smack_server/Smack">
    <si xmlns="http://jabber.org/protocol/si" id="jsi_3077759398544954943" 
    mime-type="text/plain" profile="http://jabber.org/protocol/si/profile/file-transfer">
        <file xmlns="http://jabber.org/protocol/si/profile/file-transfer" name="test.txt" 
        size="37">
            <desc>A test file</desc>
        </file>
        <feature xmlns="http://jabber.org/protocol/feature-neg">
            <x xmlns="jabber:x:data" type="form">
                <field var="stream-method" type="list-single">
                    <option>
                        <value>http://jabber.org/protocol/bytestreams</value>
                    </option>
                    <option>
                        <value>http://jabber.org/protocol/ibb</value>
                    </option>
                </field>
            </x>
        </feature>
    </si>
</iq>

<iq to="sender@smack_server/Smack" id="NK8Lh-11" type="result">
  <si xmlns="http://jabber.org/protocol/si">
    <feature xmlns="http://jabber.org/protocol/feature-neg">
      <x xmlns="jabber:x:data" type="submit">
        <field var="stream-method">
          <value>http://jabber.org/protocol/bytestreams</value>
          <value>http://jabber.org/protocol/ibb</value>
        </field>
      </x>
    </feature>
  </si>
</iq>


<iq to="receiver@smack_server/Smack" id="NK8Lh-13" type="get" from="sender@smack_server/Smack">
    <query xmlns="http://jabber.org/protocol/disco#info"/>
</iq>

<iq to="sender@smack_server/Smack" id="NK8Lh-13" type="result">
  <query xmlns="http://jabber.org/protocol/disco#info">
    <identity category="client" name="Smack" type="pc"/>
    <feature var="http://jabber.org/protocol/disco#items"/>
    <feature var="vcard-temp"/>
    <feature var="http://jabber.org/protocol/bytestreams"/>
    <feature var="http://jabber.org/protocol/ibb"/>
    <feature var="http://jabber.org/protocol/si"/>
    <feature var="http://jabber.org/protocol/xhtml-im"/>
    <feature var="jabber:x:data"/>
    <feature var="urn:xmpp:time"/>
    <feature var="jabber:iq:privacy"/>
    <feature var="http://jabber.org/protocol/si/profile/file-transfer"/>
    <feature var="urn:xmpp:ping"/>
    <feature var="jabber:iq:last"/>
    <feature var="http://jabber.org/protocol/commands"/>
    <feature var="http://jabber.org/protocol/muc"/>
    <feature var="http://jabber.org/protocol/xdata-validate"/>
    <feature var="http://jabber.org/protocol/xdata-layout"/>
    <feature var="http://jabber.org/protocol/disco#info"/>
  </query>
</iq>

<iq to="receiver@smack_server/Smack" id="NK8Lh-25" type="set" from="sender@smack_server/Smack">
    <query xmlns="http://jabber.org/protocol/bytestreams" sid="jsi_3077759398544954943" mode="tcp">
        <streamhost jid="sender@smack_server/Smack" host="ipv6_addr1" port="7778"/>
        <streamhost jid="sender@smack_server/Smack" host="ipv4_addr1" port="7778"/>
        <streamhost jid="sender@smack_server/Smack" host="ipv6_addr2" port="7778"/>
        <streamhost jid="proxy.smack_server" host="ipv4_addr2" port="7777"/>
    </query>
</iq>

<iq to="receiver@smack_server/Smack" id="NK8Lh-26" type="set" from="sender@smack_server/Smack">
    <open xmlns="http://jabber.org/protocol/ibb" block-size="4096" sid="jsi_3077759398544954943" stanza="iq"/>
</iq>

测试文件
http://jabber.org/protocol/bytestreams
http://jabber.org/protocol/ibb
http://jabber.org/protocol/bytestreams
http://jabber.org/protocol/ibb

从最好的情况来看,我已经阅读了规范,看起来一切都在按它应该的方式进行。发送方发送初始SI请求,接收方使用支持的协议(即字节流和IBB)进行响应,然后发送方向接收方查询所有disco项目,接收方使用功能列表进行响应,发送方然后发送各种流主机,然后发送方通过IBB发送数据块。从那里,接收器获得SmackException:Error in execution方法,这是由SmackException.NoResponseException引起的,消息是在5秒内没有收到响应。看起来这个问题在这一点上基本上被忽略了,但希望有人能检查一下,我真的非常感谢任何帮助。谢谢

我已经解决了这个问题!下载Smack 4.0.6源代码并在接收方调试数据包解析后,我发现一切都做得很正确。正在解析IQ和数据包,XMPPTCPConnection正在正确处理数据包的处理。这个问题原来是某种种族状况。我相信问题就在这里:

// transfer is of type IncomingFileTransfer, created by
// FileTransferRequet.accept()
final String fileName = transfer.getFileName();
transfer.recieveFile(new File(fileName));
LOG.info("Incoming file transfer: " + fileName);
LOG.info("Transfer status is: " + transfer.getStatus());
while (!transfer.isDone())
{
    final double progress = transfer.getProgress();
    final double progressPercent = progress * 100.0;
    String percComplete = String.format("%1$,.2f", progressPercent);
    LOG.info("Transfer status is: " + transfer.getStatus());
    LOG.info("File transfer is " + percComplete + "% complete");
    Thread.sleep(1000);
}
由于某些原因,while循环占用了所有的周期,xmpptcp连接无法及时响应打开的IQ和数据IQ。所以我把进度监控移到了一个新的线程中,一切都正常了
// transfer is of type IncomingFileTransfer, created by
// FileTransferRequet.accept()
final String fileName = transfer.getFileName();
transfer.recieveFile(new File(fileName));
LOG.info("Incoming file transfer: " + fileName);
LOG.info("Transfer status is: " + transfer.getStatus());
while (!transfer.isDone())
{
    final double progress = transfer.getProgress();
    final double progressPercent = progress * 100.0;
    String percComplete = String.format("%1$,.2f", progressPercent);
    LOG.info("Transfer status is: " + transfer.getStatus());
    LOG.info("File transfer is " + percComplete + "% complete");
    Thread.sleep(1000);
}
private void sendFile(String to,String file){
    /*
     * This sends a file to someone
     * @param to the xmmp-account who receives the file, the destination  
     * @param file the path from the file
     */
    File f=new File(file);
    FileTransferManager manager = new FileTransferManager(conn);
    OutgoingFileTransfer transfer =
            manager.createOutgoingFileTransfer(to);

    // Send the file
    try {
        transfer.sendFile(f,"I have a file for you?");
    } catch (XMPPException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        sendMessage(logAccount,"Sorry,couldn't deliver the file");
    }

} 
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
public void startRecvFileListen(XMPPConnection conn){
    FileTransferManager manager = new FileTransferManager(conn);
    manager.addFileTransferListener(new FileTransferListener() {
        public void fileTransferRequest(FileTransferRequest request) {
            final IncomingFileTransfer inTransfer = request.accept();
            try {
                System.out.println("filename: "+request.getFileName());
                String filePath = "D:\\datas\\smackclient\\"+request.getFileName();
                inTransfer.recieveFile(new File(filePath));
                new Thread(){
                    @Override
                    public void run(){
                        long startTime = System.currentTimeMillis();
                        while(!inTransfer.isDone()){
                            if (inTransfer.getStatus().equals(Status.error)){
                                System.out.println(sdf.format(new Date())+"error!!!"+inTransfer.getError());
                            }else{
                                double progress = inTransfer.getProgress();
                                progress*=100;
                                System.out.println(sdf.format(new Date())+"status="+inTransfer.getStatus());
                                System.out.println(sdf.format(new Date())+"progress="+nf.format(progress)+"%");
                            }
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("used "+((System.currentTimeMillis()-startTime)/1000)+" seconds  ");
                    }
                }.start();
            } catch (XMPPException e) {
                JOptionPane.showMessageDialog(null, "failed", "error", JOptionPane.ERROR_MESSAGE);
                e.printStackTrace();
            }
        }
    });
    System.out.println(connection.getUser()+"--"+connection.getServiceName());
}