【问题标题】:Socket reading using BufferedInputStream使用 BufferedInputStream 读取套接字
【发布时间】:2014-02-01 20:22:13
【问题描述】:

我正在使用 Java 的 BufferedInputStream 类来读取发送到套接字的字节。 到套接字的数据是 HTTP 形式,所以通常是一个带有定义内容长度的标头,然后是一些内容。

我遇到的问题是,有时BufferedInputStream.read() 不会读取发送给它的全部数据。它返回读取的字节数,但这比发送的要少得多。我已经验证了使用 Wireshark 发送的字节,并且可以确认正在传输完整的消息。)

示例代码如下:

BufferedInputStream inFromClient = new BufferedInputStream(socket.getInputStream());
int contentLength = getContentLengthFromHeader();    
byte[] b = new byte[contentLength];
int bytesRead = inFromClient.read(b, 0, contentLength);

一旦 read() 完成,有时bytesRead 等于 contentLength,但在其他情况下 read() 似乎没有读到内容的末尾。 有人对正在发生的事情有任何想法吗? Java缓冲输出吗?有没有更好的从套接字读取的方法?

【问题讨论】:

    标签: java sockets http bufferedinputstream


    【解决方案1】:

    您假设read() 填充了缓冲区。检查Javadoc。它至少传输一个字节,仅此而已。

    你不需要大缓冲区和BufferedInputStream. 将后者更改为DataInputStream.readFully().

    【讨论】:

      【解决方案2】:

      这是 read() 方法的正常行为:您需要循环读取,直到 read 返回 -1。 (参见 http://docs.oracle.com/javase/7/docs/api/java/io/BufferedInputStream.html#read(byte[],%20int,%20int))

      一般来说,发生这种情况是因为 read 方法试图在阻塞之前将它可以返回的所有数据返回给您,而不是您将获得的所有数据。

      我经常使用一些实用方法来处理这类事情:(断章取意 - 请注意,我不是 channelCopy 方法的作者,但来源已注明)

         /**
          * Efficiently copy from an InputStream to an OutputStream; uses channels and 
          * direct buffering for a faster copy than oldCopy. 
          * @param in - non-null readable inputstream
          * @param out - non-null writeable outputstream
          * @throws IOException if unable to read or write for some reason.
          */
         public static void streamCopy(InputStream in, OutputStream out) throws IOException {
            assert (in != null);
            assert (out != null);
            ReadableByteChannel inChannel = Channels.newChannel(in);
            WritableByteChannel outChannel = Channels.newChannel(out);
            channelCopy(inChannel, outChannel);
         }
      
         /**
          * Read the *BINARY* data from an InputStream into an array of bytes. Don't 
          * use this for text.
          * @param is - non-null InputStream
          * @return a byte array with the all the bytes provided by the InputStream 
          * until it reaches EOF.
          * @throws IOException 
          */
         public static byte[] getBytes(InputStream is) throws IOException{
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            streamCopy(is, os);
            return os.toByteArray();
         }
      
      
         /**
          * A fast method to copy bytes from one channel to another; uses direct 16k 
          * buffers to minimize copies and OS overhead.
          * @author http://thomaswabner.wordpress.com/2007/10/09/fast-stream-copy-using-javanio-channels/
          * @param src - a non-null readable bytechannel to read the data from
          * @param dest - a non-null writeable byte channel to write the data to
          */   
         public static void channelCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException {
            assert (src != null);
            assert (dest != null);
            final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
            while (src.read(buffer) != -1) {
               // prepare the buffer to be drained
               buffer.flip();
               // write to the channel, may block
               dest.write(buffer);
               // If partial transfer, shift remainder down
               // If buffer is empty, same as doing clear()
               buffer.compact();
            }
      
            // EOF will leave buffer in fill state
            buffer.flip();
      
            // make sure the buffer is fully drained.
            while (buffer.hasRemaining()) {
               dest.write(buffer);
            }
        }
      

      【讨论】:

      • channelCopy() 方法的循环应该是 while (src.read(buffer) > 0 || buffer.position() > 0). 然后你就可以摆脱在 EOF 结束的东西。
      • 嗨@ejp,感谢您的评论;我想你可能是对的,但我不太明白——你能详细说明一下吗?如果我用您的建议替换while (src.read(buffer) != -1),在我看来,复制操作可能会提前终止,例如,如果 http 传输暂时停止(读取可能返回 0,因为没有新数据可用,我们可以完成耗尽我们拥有的缓冲区,将其保留在位置 0,)但此时终止循环是错误的(因为尚未达到 EOF) - 我在这里错过了什么?
      • 您缺少||.之后的部分
      • @ejp 我真的很想把这个功能弄好,但我认为你直到我的评论结束才会阅读?如果 read() 返回 0(当前没有可用数据)并且 buffer.position() 返回 0(我们已将缓冲区中的所有数据排入 OutputStream),则您的 while 循环条件为假,循环将退出。但是,除非我误读了规范,否则这种情况可能会在 EOF 之前在停止的 http 传输期间发生(javadoc 说 read() 可以返回 0,并且空缓冲区的位置 == 0,)并且它会导致复制过早退出.您的回复似乎没有解决我问题的症结。
      • 抱歉串扰,@adam,我将把这个讨论转移到我自己的问题中...... :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-05
      • 1970-01-01
      相关资源
      最近更新 更多