java InputStream#read的 end of stream的问题

导读:本篇文章讲解 java InputStream#read的 end of stream的问题,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

网络流的结尾(end of stream)是什么?

java.io包中大多数read方法的 javadoc 中,可以读到

the total number of bytes read into the buffer,

 or -1 if there is no more data because the end of the stream has been reached

大致意思是:读取到缓冲区的字节总数,或 -1,如果没有更多的数据,因为已经达到流的结尾

我们都知道 读取文件时,只要读取到 文件的 EOF 标志位,就代表文件读取完成了,inputStream 就会返回 -1,代表可以结束读取了,因此,这 个 EOF 不需要我们主动去判断。

但是,我从未真正获得 -1,因为大多数流(在网络的情况下) 流(我最常使用的流)只是阻塞程序执行,直到在远程端将某些内容写入流中,才会继续执行。

因为 socket 是基于 TCP/IP 规则,编写的程序,那么,就涉及到 TCP/IP 数据包的结构了,所以,我想,socket之间也是通过 改变数据包中某个标志位通知对方的,让对方改变 socket中的 shutOut=true

public
class Socket implements java.io.Closeable {
    /**
     * Various states of this socket.
     */
    private boolean created = false;
    private boolean bound = false;
    private boolean connected = false;
    private boolean closed = false;
    private Object closeLock = new Object();
    // shutDownInputStream
    private boolean shutIn = false;
    // shutDownOutputStream
    private boolean shutOut = false;
}

经过代码调试,我发现 read 是一个阻塞函数,如果A端没有主动断开OutputStream(输出流),那么,B端就会认为A端仍旧可能发送数据。

像 read这种阻塞读取函数还有: BufferedReader#readLineDataInputStream#readUTF等。

代码测试

客户端

public class SocketClient {
    public static void main(String[] args) throws IOException {
        Socket client;
        OutputStream out = null;
        InputStream input = null;
        try {
            client = new Socket("127.0.0.1",8888);
            out = client.getOutputStream();
            input = client.getInputStream();

            // 测试1:主动 end of stream
            // client.shutdownInput();

            // 发送
            out.write("0".getBytes());
            out.flush();

            // 注意:缓冲区满了,不是end of stream
            byte[] buf = new byte[5];
            int size;
            // 读取
            while ((size = input.read(buf,0,buf.length)) != -1) {
                System.out.println("[" + size + "] byte: " + new String(buf));
                // 相当于 自己强制 end if stream,自己的 read 方法会返回 -1
                // 相当于,我不想在读了,返回 -1 自我结束
                // 对 服务端 没有任何影响
                // client.shutdownInput();
            }
            System.out.println(size);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            assert out != null;
            out.close();
            assert input != null;
            input.close();
        }
    }
}

服务端

public class SocketServer {

    public static void main(String[] args) {
        SocketServer ss = new SocketServer();
        int port = 8888;
        try {
            ss.startServer(port);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void startServer(int port) {
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            Socket server;
            System.out.println("server socket is start……");
            while (true) {
                server = serverSocket.accept();
                try {
                    InputStream in = server.getInputStream();
                    OutputStream out = server.getOutputStream();

                    byte[] buf = new byte[5];
                    int size;
                    while ((size = in.read(buf,0,buf.length)) != -1) {
                        System.out.println("[" + size + "] byte: " + new String(buf));

                        // 测试 1:写回 3 个字节
                        out.write("111".getBytes());

                        // 测试二:每 1 秒,写回 一个 1
                        // for (int i = 0;i < 10;i++){
                        //     out.write("1".getBytes());
                        //     // 休眠 1 秒
                        //     // TimeUnit.SECONDS.sleep(1);
                        // }
                        
                        // 测试3:关闭输出流,告诉 客户端的 read 方法,end of tream
                        // 因此,客户端的 read 方法 会 返回 -1
                        // server.shutdownOutput();
                    }
                    System.out.println(size);
                    out.flush();
                } catch (Exception e) {
                    e.printStackTrace();
                    break;
                } finally {
                    System.out.println("close client socket");
                    server.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

如何触发 end of stream ?

  1. Socket任意一端在调用Socket#shutdownOutput方法, 关闭outPutStream(输出流),这样对应的另一端的Socket#inputStream上的read方法就会返回 -1

注意: 不能调用Socket#getInputStream().close()。因为它会导致socket直接被关闭。 当然,如果不需要继续在socket上进行读操作,也可以直接关闭socket。但是这个方法不能用于通信双方需要多次交互的情况。

// 调用shutdownOutput 通知对端的 read 方法返回 -1
socket.shutdownOutput();  
  1. Socket任意一端在调用Socket#shutdownInput方法, 那么,调用这个方法的一端的 inputStream,相当于 end of stream了,以后就没办法再读取数据了。但是,这个方法对另一端没有任何影响。

大白话就是:我不想再读了,自我了结,强制让 read方法返回 -1。

socket.shutdownInput();
  1. 如果任意一端强制掉线(就是程序被强制停止了)或者主动调用 socket#close方法,那么,另一端的 read 方法就会抛出异常。
java.net.SocketException: Software caused connection abort: recv failed
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:171)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at test.socket.SocketServer.startServer(SocketServer.java:41)
	at test.socket.SocketServer.main(SocketServer.java:24)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/69730.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!