Java Socket - read()方法如何知道流的结束?

3
  1. InputStream.read(byte[]) 方法如何知道已经到达“流的末尾”并返回“-1”?

  2. 返回“-1”的所有条件是什么?

  3. 如何检测“流的末尾”(不发送包含要读取的总字节数的整数)?

使用示例:

InputStream input = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
for(int size = -1; (size = input.read(buffer)) != -1; ) {
    baos.write(buffer, 0, size);
}

2
如果它是一个TCP套接字,那是因为另一端发送了一个TCP握手消息来关闭连接。 - Jesper
如果它不是TCP套接字,它根本不会返回-1,因为Java支持的唯一其他套接字是UDP,它不是流协议,并且根本没有流结束。 - user207421
@Nayuki 为什么您删除了 [tag:tcp] 标签?那是问题的关键。 - user207421
2个回答

2

InputStream 是一个抽象类型,有许多实现。例如,FileInputStream 将在到达文件结尾时返回 -1。如果是 TCP 套接字,则在连接关闭时将返回 -1。如何确定流结束是依赖于具体实现的。


3
根据问题的标题,我认为关注的是套接字实现。 - aioobe

-1

它并不会。

当你尝试从套接字读取n个字节时,调用可能在n个字节准备好之前返回,并返回读取的字节数。read()如何决定返回?基于超时时间。超时值在AbstractPlainSocketImpl.java中被注释为SO_TIMEOUT。实际上,真正的读取是使用本地代码完成的,可能是用C编写的,SO_TIMEOUT默认为本地代码的值。但是,你可以使用Socket.setSocketTimeout(millis)设置超时值。

SocketInputStream.java

        n = socketRead(fd, b, off, length, timeout);
        if (n > 0) {
            return n;
        }

如果您遵循HTTP协议,客户端和服务器将使用content-length头来指示彼此请求和响应何时结束以及何时开始新的请求和响应。字节接收顺序由TCP层处理。
套接字流没有流结束检查,例如文件的feof检查。它是一种双向通信读写方式。但是,您可以检查是否有可读的字节。TCP连接保持活动状态,直到客户端或服务器选择关闭为止。

1
它确实如此。当对等方关闭连接时,TCP套接字会传递流的末尾。你的第一个和最后一个段落相互矛盾。 - user207421
...而且文件没有EOF标记。如果数据或流的结束已经被接收,read()不会基于超时来决定何时返回。这个答案看起来像是猜测。 - user207421
SocketInputStream.java --> int read(byte b[], int off, int length, int timeout) throws IOException --> n = socketRead0(fd, b, off, length, timeout);SocketInputStream.java --> private native int socketRead0(FileDescriptor fd, byte b[], int off, int len, int timeout) throws IOException;SocketInputStream.c --> nread = recv(fd, bufP, len, 0);https://man7.org/linux/man-pages/man2/recv.2.htmlhttps://www.gnu.org/software/libc/manual/html_node/EOF-and-Errors.html - Janardhan B. Chinta
我深入研究了EOF是如何确定的。文件指针通过标志位来跟踪EOF。在struct_FILE.h中,#define _IO_EOF_SEEN 0x0010表示EOF已被读取。在struct_FILE.h中,#define __feof_unlocked_body(_fp) (((_fp)->_flags & _IO_EOF_SEEN) != 0)表示如果文件指针的标志位包含_IO_EOF_SEEN,则返回1,否则返回0。在fileops.c中,当_IO_file_xsgetn函数执行时,count = _IO_SYSREAD (fp, s, count); if (count == 0) fp->_flags |= _IO_EOF_SEEN;,如果读取到的字符数为0,则将文件指针的标志位置为_IO_EOF_SEEN。从这一点开始,它真的变得混乱不堪,有宏、虚表等等...。总之,_IO_SYSREAD返回-1表示EOF。在libioP.h中,#define _IO_SYSREAD(FP, DATA, LEN) JUMP2 (__read, FP, DATA, LEN) - Janardhan B. Chinta
我不知道你的第一个评论想证明什么,它没有起到任何作用。在注释中放入难以辨认的代码没有太大用处,请修改你的答案,因为它是错误的。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接