文件传输在 Smack 4.1.0 beta-1 上失败
File Transfer fails on Smack 4.1.0 beta-1
我正在开发一个涉及两个 Smack 客户端的应用程序,其中一个尝试将 jar 文件发送到另一个客户端。我把它写成桌面应用程序的一部分,而不是 Android 应用程序(我想我会做出这种区分,因为 Whosebug 上的大多数 Smack 查询都是 Android 相关的)。我使用 Openfire 3.9.3 作为 XMPP 服务器。对于所有代码示例,我使用以下 Smack 库(版本 4.1.0- beta 1):smack-java7、smack-tcp、smack-extensions 和 smack-sasl-provided。这是 OutgoingFileTransfer 的代码(注意所有代码示例中的信息,例如 Smack 用户名已被匿名化):
// LOG is of type org.apache.logging.log4j.Logger
// transferManager is the FileTransferManager for this XMPPTCPConnection
private void sendJarFile(final String to)
{
LOG.info("Sending " + jarFileName + " to " + to);
OutgoingFileTransfer jarTransfer =
transferManager.createOutgoingFileTransfer(to);
final File jarFile = new File(jarFileName);
try
{
jarTransfer.sendFile(jarFile, "The current jar file");
while (!jarTransfer.isDone())
{
LOG.info("File transfer status: " + jarTransfer.getStatus());
Thread.sleep(500);
}
LOG.info("File transfer to " + to + " is done");
// Now that the file transfer is done check for errors
// or exceptions
FileTransfer.Error error = jarTransfer.getError();
if (error != null)
{
LOG.error(error.getMessage());
}
Exception exception = jarTransfer.getException();
if (exception != null)
{
if (exception instanceof XMPPException.XMPPErrorException)
{
XMPPException.XMPPErrorException errorException =
(XMPPException.XMPPErrorException)exception;
XMPPError xmppError = errorException.getXMPPError();
LOG.error(xmppError);
LOG.error("Descriptive text: " + xmppError.getDescriptiveText());
LOG.error("Condition: " + xmppError.getCondition());
LOG.error("Type: " + xmppError.getType());
}
}
}
// For now, just catching and logging exceptions. Exception handling
// will be added in top-level classes
catch (SmackException e)
{
LOG.error("Exception trying to send jar file", e);
}
catch (InterruptedException e)
{
// Do nothing
}
catch (Exception e)
{
LOG.error("Exception trying to send jar file", e);
}
}
这段代码的输出是:
Sending test.jar to receiver@local_openfire/Smack
14:09:43.748 INFO - File transfer status: Initial
14:09:44.249 INFO - File transfer status: Negotiating Stream
14:09:44.751 INFO - File transfer status: Negotiating Stream
// This message continues for several seconds, until finally
14:09:53.805 INFO - File transfer status: Negotiating Stream
14:09:54.308 INFO - File transfer to receiver@local_openfire/Smack
is done
14:09:54.309 ERROR - org.jivesoftware.smack.packet.XMPPError@30e95075
14:09:54.310 ERROR - Descriptive text: null
14:09:54.310 ERROR - Condition: service-unavailable
14:09:54.310 ERROR - Type: cancel
对于 IncomingFileTransfer,代码是:
@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 INFO - FileTransferRequest from:
sender@local_openfire/Smack
14:09:43.767 INFO - FileTransferRequest accepted
14:09:43.768 INFO - Incoming file transfer: test.jar
14:09:43.769 INFO - Transfer status is: Negotiating Transfer
14:09:43.770 INFO - Transfer status is: Negotiating Stream
14:09:43.770 INFO - File transfer is 0.00% complete
14:09:44.771 INFO - Transfer status is: Negotiating Stream
14:09:44.771 INFO - File transfer is 0.00% complete
14:09:45.776 INFO - Transfer status is: Negotiating Stream
14:09:45.776 INFO - File transfer is 0.00% complete
14:09:46.778 INFO - Transfer status is: Negotiating Stream
14:09:46.778 INFO - File transfer is 0.00% complete
14:09:47.782 INFO - Transfer status is: Negotiating Stream
14:09:47.783 INFO - File transfer is 0.00% complete
14:09:48.784 ERROR - Transfer exception occurred:
org.jivesoftware.smack.SmackException: Error in execution
14:09:48.784 INFO - FileTransfer complete
在这段代码有运行之后,在接收端,我在当前工作目录中有一个名为"test.jar"的文件,文件大小为0字节。我在不同机器上的发送者和接收者,以及同一台机器上的发送者和接收者都试过了。我最初使用 Smack 4.0.6,但切换到最新的代码库(撰写本文时为 4.1.0-beta 1),希望这个错误可能已经得到解决。没有这样的运气。我将不胜感激任何建议。谢谢!
2015 年 1 月 30 日更新
我不再在发件人端看到 XMPPError。相反,发件人仍然停留在文件传输状态:协商流状态。但是,接收方收到以下错误:
SmackException.NoResponseException: 包回复超时没有收到回复。超时为 5000 毫秒(~5 秒)
我可以看到如何增加 OutgoingFileTransfer 的超时 class,但不是 IncomingFileTransfer。
更新 2-02-2015
我使用了 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>
据我所知,阅读规范后,一切似乎都在按预期进行。发送方发送初始 SI 请求,接收方以支持的协议(即字节流和 IBB)进行响应,然后发送方向接收方查询所有迪斯科项目,接收方以特征列表进行响应,然后发送方发送各种流主机,然后发送方通过 IBB 发送数据块。从那里,接收者得到 SmackException: Error in execution method,这是由 SmackException.NoResponseException 引起的,消息是 5 秒内没有收到响应。看起来这个问题在这一点上基本上被忽略了,但希望有人能检查出来,我真的很感激任何帮助。谢谢!
我已经解决了这个问题!下载 Smack 4.0.6 源代码并在 Receiver 端调试数据包解析后,我发现一切正常。正在解析 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 循环消耗了所有周期,XMPPTCPConnection 无法及时响应 Open IQ 和 Data IQ。所以我把进度监控移到了一个新的线程中,一切都很完美。作为参考,我在 Mac OS X 上使用 Java 版本 1.8.0_31(64 位)并且还在 Java 版本 1.8.[= Windows 7 Professional(64 位)上的 15=](64 位)。澄清一下,我的解决方案适用于 Smack 4.0.6。我还确认它在 Java 7 (1.7.0_51) 64 位上正常工作。
也许这个例子会给你提供一些思路:
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");
}
}
您可以在 Examples for org.jivesoftware.smackx.filetransfer.OutgoingFileTransfer 中找到更多信息。
还有一个可能有用的文件接收示例:
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());
}
我正在开发一个涉及两个 Smack 客户端的应用程序,其中一个尝试将 jar 文件发送到另一个客户端。我把它写成桌面应用程序的一部分,而不是 Android 应用程序(我想我会做出这种区分,因为 Whosebug 上的大多数 Smack 查询都是 Android 相关的)。我使用 Openfire 3.9.3 作为 XMPP 服务器。对于所有代码示例,我使用以下 Smack 库(版本 4.1.0- beta 1):smack-java7、smack-tcp、smack-extensions 和 smack-sasl-provided。这是 OutgoingFileTransfer 的代码(注意所有代码示例中的信息,例如 Smack 用户名已被匿名化):
// LOG is of type org.apache.logging.log4j.Logger
// transferManager is the FileTransferManager for this XMPPTCPConnection
private void sendJarFile(final String to)
{
LOG.info("Sending " + jarFileName + " to " + to);
OutgoingFileTransfer jarTransfer =
transferManager.createOutgoingFileTransfer(to);
final File jarFile = new File(jarFileName);
try
{
jarTransfer.sendFile(jarFile, "The current jar file");
while (!jarTransfer.isDone())
{
LOG.info("File transfer status: " + jarTransfer.getStatus());
Thread.sleep(500);
}
LOG.info("File transfer to " + to + " is done");
// Now that the file transfer is done check for errors
// or exceptions
FileTransfer.Error error = jarTransfer.getError();
if (error != null)
{
LOG.error(error.getMessage());
}
Exception exception = jarTransfer.getException();
if (exception != null)
{
if (exception instanceof XMPPException.XMPPErrorException)
{
XMPPException.XMPPErrorException errorException =
(XMPPException.XMPPErrorException)exception;
XMPPError xmppError = errorException.getXMPPError();
LOG.error(xmppError);
LOG.error("Descriptive text: " + xmppError.getDescriptiveText());
LOG.error("Condition: " + xmppError.getCondition());
LOG.error("Type: " + xmppError.getType());
}
}
}
// For now, just catching and logging exceptions. Exception handling
// will be added in top-level classes
catch (SmackException e)
{
LOG.error("Exception trying to send jar file", e);
}
catch (InterruptedException e)
{
// Do nothing
}
catch (Exception e)
{
LOG.error("Exception trying to send jar file", e);
}
}
这段代码的输出是:
Sending test.jar to receiver@local_openfire/Smack
14:09:43.748 INFO - File transfer status: Initial
14:09:44.249 INFO - File transfer status: Negotiating Stream
14:09:44.751 INFO - File transfer status: Negotiating Stream
// This message continues for several seconds, until finally
14:09:53.805 INFO - File transfer status: Negotiating Stream
14:09:54.308 INFO - File transfer to receiver@local_openfire/Smack is done
14:09:54.309 ERROR - org.jivesoftware.smack.packet.XMPPError@30e95075
14:09:54.310 ERROR - Descriptive text: null
14:09:54.310 ERROR - Condition: service-unavailable
14:09:54.310 ERROR - Type: cancel
对于 IncomingFileTransfer,代码是:
@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 INFO - FileTransferRequest from: sender@local_openfire/Smack
14:09:43.767 INFO - FileTransferRequest accepted
14:09:43.768 INFO - Incoming file transfer: test.jar
14:09:43.769 INFO - Transfer status is: Negotiating Transfer
14:09:43.770 INFO - Transfer status is: Negotiating Stream
14:09:43.770 INFO - File transfer is 0.00% complete
14:09:44.771 INFO - Transfer status is: Negotiating Stream
14:09:44.771 INFO - File transfer is 0.00% complete
14:09:45.776 INFO - Transfer status is: Negotiating Stream
14:09:45.776 INFO - File transfer is 0.00% complete
14:09:46.778 INFO - Transfer status is: Negotiating Stream
14:09:46.778 INFO - File transfer is 0.00% complete
14:09:47.782 INFO - Transfer status is: Negotiating Stream
14:09:47.783 INFO - File transfer is 0.00% complete
14:09:48.784 ERROR - Transfer exception occurred: org.jivesoftware.smack.SmackException: Error in execution
14:09:48.784 INFO - FileTransfer complete
在这段代码有运行之后,在接收端,我在当前工作目录中有一个名为"test.jar"的文件,文件大小为0字节。我在不同机器上的发送者和接收者,以及同一台机器上的发送者和接收者都试过了。我最初使用 Smack 4.0.6,但切换到最新的代码库(撰写本文时为 4.1.0-beta 1),希望这个错误可能已经得到解决。没有这样的运气。我将不胜感激任何建议。谢谢!
2015 年 1 月 30 日更新
我不再在发件人端看到 XMPPError。相反,发件人仍然停留在文件传输状态:协商流状态。但是,接收方收到以下错误:
SmackException.NoResponseException: 包回复超时没有收到回复。超时为 5000 毫秒(~5 秒)
我可以看到如何增加 OutgoingFileTransfer 的超时 class,但不是 IncomingFileTransfer。
更新 2-02-2015
我使用了 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>
据我所知,阅读规范后,一切似乎都在按预期进行。发送方发送初始 SI 请求,接收方以支持的协议(即字节流和 IBB)进行响应,然后发送方向接收方查询所有迪斯科项目,接收方以特征列表进行响应,然后发送方发送各种流主机,然后发送方通过 IBB 发送数据块。从那里,接收者得到 SmackException: Error in execution method,这是由 SmackException.NoResponseException 引起的,消息是 5 秒内没有收到响应。看起来这个问题在这一点上基本上被忽略了,但希望有人能检查出来,我真的很感激任何帮助。谢谢!
我已经解决了这个问题!下载 Smack 4.0.6 源代码并在 Receiver 端调试数据包解析后,我发现一切正常。正在解析 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 循环消耗了所有周期,XMPPTCPConnection 无法及时响应 Open IQ 和 Data IQ。所以我把进度监控移到了一个新的线程中,一切都很完美。作为参考,我在 Mac OS X 上使用 Java 版本 1.8.0_31(64 位)并且还在 Java 版本 1.8.[= Windows 7 Professional(64 位)上的 15=](64 位)。澄清一下,我的解决方案适用于 Smack 4.0.6。我还确认它在 Java 7 (1.7.0_51) 64 位上正常工作。
也许这个例子会给你提供一些思路:
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");
}
}
您可以在 Examples for org.jivesoftware.smackx.filetransfer.OutgoingFileTransfer 中找到更多信息。
还有一个可能有用的文件接收示例:
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());
}