Java Smack 4.1.0 beta-1上的文件传输失败
我正在开发一个涉及两个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错误-类型:取消 对于收入文件传输,代码为: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
@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());
}