【问题标题】:Java - Wait for stream to finish writing?Java - 等待流完成写入?
【发布时间】:2013-03-22 14:43:08
【问题描述】:

我有一个在 java 中运行的“服务器”,我通过字节数组将文件发送到一个“客户端”(android 系统)。该功能工作正常,但我注意到当我尝试发送更大的文件(1MB +)时,它在关闭之前没有成功写入所有字节(例如:尝试发送 5mb,它只发送了 902 个字节)。有没有办法让代码在关闭流之前等待流完成写入?下面是代码和堆栈跟踪。本质上,发送消息方法在附加和发送文件时调用(在处理 GUI 的类中)。在这种情况下,我并没有尝试从客户端接收数据,只是向客户端发送数据。

编辑:我知道我现在只将字节数组设置为 1MB。我有它 5MB,它仍然只写

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)

这是客户端的代码sn-p。

    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);
    }
}

【问题讨论】:

  • 当您的字节缓冲区只有 1MB 大小时,您不能写入大于 1MB 的文件。您正在填充缓冲区、写入缓冲区并关闭输出流。也许尝试使用 BufferedReader 和循环来代替
  • 啊,是的,我确实改变了它,看看我能写的最大值是多少。我有它的 5MB,但它仍然没有写完整的东西。即使有 1MB,它也只写了 902 个字节。
  • 您的 while 循环是否多次运行?
  • 始终使用套接字围绕“读取”循环。

标签: java android sockets bytearray outputstream


【解决方案1】:

一、read的JavaDoc:

从输入流中读取一些字节并将它们存储到 缓冲区数组 b.实际读取的字节数返回为 一个整数。此方法阻塞,直到输入数据可用,结束 检测到文件,或者抛出异常。

由于源是一个套接字,它可能返回比预期更少的字节,即使缓冲区大小为 1 MByte,返回 902 KBytes 也是可以的。您必须循环直到流结束。

以下方法读取所有字节,而不是调用侦听器:

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 );

如您所见,会发生很多重新分配:您必须重新设计协议以首先发送文件的大小,然后分配一次接收缓冲区。

【讨论】:

  • 当我得到异常时我并没有尝试接收任何数据,所以我不明白读取和接收字节是怎么回事?
  • "reset by peer",原因在对方。贴出对方的代码。
  • 我的回答适用于客户端:套接字在网络传输数据时提供数据,而不是全部在一个块中。你必须循环“阅读”。
  • @kaptaincooke - 当 InputStream 是 Socket 时, read() 几乎不会返回超过 65K 的任何内容;通常少谢 1600 字节。这是因为操作系统会在调用时为您返回尽可能多的接收和缓冲,这通常不是您需要的那么多 - 本质上,当没有任何缓冲时,进程将等待,但只要至少有一个字节你会得到什么。循环循环获取消息的所有内容;另外,修复您的协议以预先发送消息的长度。
  • 这似乎奏效了。我还更改了发送消息功能,以 1KB 块的形式发送数据。它很好地接收这些块并将它们拼凑在一起(但是,对于一个 4MB 的文件,客户端会在一段时间后崩溃。我的猜测是与内存有关?)
【解决方案2】:

您在完成读取之前将读取缓冲区设置为空:

 in = socket.getInputStream();
 while(mRun) {
     in.read(serverMessage);
     if(serverMessage != null && mMessageListener != null) {
         mMessageListener.messageReceived(new String(serverMessage));
     }
     serverMessage = null;
}

这将导致 NullPointerException 并且您的代码将关闭套接字。

【讨论】:

    【解决方案3】:

    设置socket.setSendBufferSize(1024)(用于发送)还确保在接收时读取1024字节块。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-07
      相关资源
      最近更新 更多