我有一个运行在Java中的“服务器”,并且正在通过字节数组将文件发送到“客户端”(Android系统)。该功能运行良好,但我注意到当我尝试发送较大的文件(1MB +)时,它在关闭之前未成功写入所有字节(例如:尝试发送5mb,并且仅发送902个字节)。有没有一种方法可以让代码在关闭流之前等待流完成写入?下面是代码和堆栈跟踪。本质上,当附加和发送文件时(在处理GUI的类中),将调用send message方法。在这种情况下,我没有尝试从客户端接收数据,只是向客户端发送数据。
编辑:我知道我现在只将字节数组设置为1MB。我只有5MB的空间,但仍然只有不到1MB的空间。即使只有1MB,也只能写入902个字节。
EDIT2:我已经在处理接收数据的客户端上发布了一些代码。
public void sendMessage(File file) {
if(mOut != null) {
try {
FileInputStream inStream = new FileInputStream(file);
byte[] message = new byte[1024 * 1024];
inStream.read(message);
mOut.write(message, 0, message.length);
mOut.flush();
inStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void run() {
super.run();
running = true;
try {
System.out.println("S: Connecting...");
ServerSocket serverSocket = new ServerSocket(SERVERPORT);
Socket client = serverSocket.accept();
linked = true;
System.out.println("S: Receiving...");
try {
mOut =client.getOutputStream();
InputStream in = client.getInputStream();
while(running) {
byte[] message = new byte[1024 * 1024];
in.read(message);
if(message != null && messageListener != null) {
messageListener.messageReceived(message);
}
}
} catch (Exception ex) {
System.out.println("S:Error");
ex.printStackTrace();
} finally {
client.close();
System.out.println("S:Done");
}
} catch (Exception ex) {
System.out.println("S: Error");
ex.printStackTrace();
}
}
---------------------------------------------------------------------------
java.net.SocketException: Connection reset by peer: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(Unknown Source)
at java.net.SocketOutputStream.write(Unknown Source)
at Server.sendMessage(Server.java:34)
at ServerBoard$2.actionPerformed(ServerBoard.java:57)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at Server.run(Server.java:58)
这是客户端的代码段。
public void run() {
mRun = true;
serverMessage = new byte[1024 * 1024];
try {
InetAddress serverAddr = InetAddress.getByName(SERVERIP);
Log.e("TCP Client", "C:Connecting...");
Socket socket = new Socket(serverAddr, SERVERPORT);
try {
in = socket.getInputStream();
while(mRun) {
in.read(serverMessage);
if(serverMessage != null && mMessageListener != null) {
mMessageListener.messageReceived(new String(serverMessage));
}
serverMessage = null;
}
Log.e("RESPONSE FROM SERVER","S:Received Message: '" + serverMessage + "'");
} catch(Exception ex) {
Log.e("TCP","S: Error", ex);
} finally {
socket.close();
}
} catch(Exception ex) {
Log.e("TCP","C: Error", ex);
}
}
最佳答案
首先,read的JavaDoc:
从输入流中读取一些字节并将其存储到
缓冲区数组b。实际读取的字节数返回为
一个整数。此方法将阻塞,直到输入数据可用为止。
检测到文件,或引发了异常。
由于源是套接字,因此即使缓冲区的大小为1 MByte,返回的字节数也可能比预期的少,返回902 KByte也可以。您必须循环播放直到流结束。
以下方法读取所有字节,然后调用侦听器:
byte[] message = new byte[0];
byte[] buffer = new byte[4096];
int len;
while(( len = is.read( buffer )) > -1 ) {
byte[] tmp = new byte[message.length+len];
System.arraycopy( message, 0, tmp, 0, message.length );
System.arraycopy( buffer , 0, tmp, message.length, len );
message = tmp;
}
messageListener.messageReceived( message );
如您所见,发生了许多重新分配:您必须重新设计协议,才能首先发送文件的大小并为接收缓冲区分配一次。