• Java socket中关闭IO流后,发生什么事?(以关闭输出流为例)


    感谢大佬:https://blog.csdn.net/jdsjlzx/article/details/50992901

    声明:该博文以socket中,关闭输出流为例进行说明。

    为了方便讲解,我们把DataOutputstream dout = new DataOutputStream(new BufferedOutputStream(mySocket.getOutputStream()));中的dout做为Socket输出流的代言。同样的,din是输入流的代言。

    可以造成dout被关闭的操作有:

    1、调用dout.close();或din.close();因为使用这种流关闭,会造成socket被关闭,所以输入输出流都将不可再用。
    2、调用socket.close();
    3、调用socket.shutdownOutputStream();单方面关闭dout,此时din还可正常使用。

    以下,我将对socket中关闭输出流进行3个测试:

    输出流关闭测试一:socket关闭吗?
    输出流关闭测试二:该流是否可以重新开启?
    输出流关闭测试三:输出缓冲区里的数据是丢弃,还是发送?
    测试结果如下:
    测试一:dout.close();会造成socket被关闭,但socket.shutdownOutputStream()不会。

    测试二:不可以,会抛出异常!

    测试三:丢弃

    微笑客户端程序:

    package com.test2;  
    import java.io.*;  
    import java.net.*;  
    /** 
    * @ClassName: SocketTest 
    * @Description: 测试Socket中,流关闭后,socket是否关闭?是否可重开流?输出缓存区的数据是发送出去,还是丢弃? 
    * @author 慢跑学Android 
    * @date 2011-11-12 上午11:15:21 
    *  
    */  
    public class SocketTest {  
        Socket mySocket;  
        DataOutputStream dout;  
        public static void main(String[] args){  
            new SocketTest();  
        }  
          
        public SocketTest(){  
            // 输出流关闭的测试一:socket关闭吗?  
            test1();  
            // 输出流关闭测试二:该流是否可以重新开启?  
            test2();  
            // 输出流关闭测试三:输出缓冲区里的数据是丢弃,还是发送?  
            test3();  
        }  
      
        private void test1() {  
            // 输出流关闭的测试一:socket关闭吗?  
            System.out.println("
    ****2种方式关闭输出流,Socket是否关闭?***
    ");  
            try {  
                mySocket = new Socket("27.154.122.233",9999);  
            } catch (UnknownHostException e) {  
                e.printStackTrace();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
              
            try {  
                dout = new DataOutputStream(new BufferedOutputStream(mySocket.getOutputStream()));  
                //下面这一句主要是用来证明socket确实处于开启状态  
                System.out.println("输出流刚打开,Socket是否关闭?" + mySocket.isClosed());  
                mySocket.shutdownOutput();  
                System.out.println("使用shutdownOutput关闭输出流,Socket是否关闭?" + mySocket.isClosed());  
                dout.close();  
                System.out.println("使用close关闭输出流,Socket是否关闭?" + mySocket.isClosed());  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
      
        private void test2() {  
            // 输出流关闭测试二:使用shutdownOutputStream后,输出流是否可以重新开启?  
            System.out.println("
    ****使用shutdownOutputStream后,输出流是否可以重新开启?***
    ");  
            try {  
                mySocket = new Socket("27.154.122.233",9999);  
            } catch (UnknownHostException e) {  
                e.printStackTrace();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
              
            try {  
                dout = new DataOutputStream(new BufferedOutputStream(mySocket.getOutputStream()));  
                mySocket.shutdownOutput();  
                // 重开输出流  
                dout = new DataOutputStream(mySocket.getOutputStream());  
                dout.writeUTF("是否允许我重开?");  
                // 清空输出缓存,确保当dout通道没问题时,消息可以到达服务器  
                dout.flush();  
            } catch (IOException e) {  
                e.printStackTrace();  
            } finally {  
                try {  
                    mySocket.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
          
        private void test3(){  
            // 输出流关闭测试三:输出缓冲区里的数据是丢弃,还是发送?  
            System.out.println("
    ***输出缓冲区里的数据是丢弃,还是发送?****
    ");  
            try {  
                mySocket = new Socket("27.154.122.233",9999);  
            } catch (UnknownHostException e) {  
                e.printStackTrace();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
              
            try {  
                dout = new DataOutputStream(new BufferedOutputStream(mySocket.getOutputStream()));  
                dout.writeUTF("shutdownOutput后,数据发得得出去吗?");  
                mySocket.shutdownOutput();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
    

    微笑服务器端程序:

    /**    
    * @Title: ServerSocketTest.java 
    * @Package com.test1 
    * @Description: TODO(该文件为”Socket中,流关闭后,发生什么事“的Sever测试端) 
    * @author 慢跑学Android 
    * @date 2011-11-12 上午11:31:05 
    * @version V1.0    
    */  
    package com.test1;  
      
    import java.io.*;  
    import java.net.*;  
      
    public class ServerSocketTest extends Thread{  
        private ServerSocket myServerSocket;  
        private final int PORT = 9999;  
        public static void main(String[] args){  
            ServerSocketTest sst = new ServerSocketTest();  
            sst.start();  
        }  
          
        public ServerSocketTest(){  
            // 初始化一个ServeSocket端  
            try {  
                myServerSocket = new ServerSocket(PORT);  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
          
        public void run(){  
            while(true){  
                System.out.println("我是服务器,我在9999端口监听....");  
                try {  
                    Socket socket = myServerSocket.accept();  
                    DataInputStream din = new DataInputStream(new BufferedInputStream(socket.getInputStream()));  
                    String msgIn = din.readUTF();  
                    System.out.println(msgIn.trim());  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }  
    

    说明一点:

    在test3()中,因为dout = new DataOutputStream(newBufferedOutputStream(mySocket.getOutputStream()));使用了Buffered,所以在dout.writeUTF()方法后,如果没有使用dout.flush();数据会存在输出缓存中,不会发送出去的。

    如果我们队dout的声明是,dout = new DataOutputStream(mySocket.getOutputStream());那么,数据会立即发送出去。(除非,对方没有调用read()来读取数据,且数据量极大,超过了对方的输入缓存。不过,此时dout.writeUTF();这里会堵塞。)

    以下是程序运行后,客户端与服务器各自的控制台输出情况:

    ----------------------------------客户端--------------------------

    java.net.SocketException: Socket output is shutdown
     at java.net.Socket.getOutputStream(Unknown Source)
     at com.test2.SocketTest.test2(SocketTest.java:66)
     at com.test2.SocketTest.<init>(SocketTest.java:22)
     at com.test2.SocketTest.main(SocketTest.java:15)
    

    *2种方式关闭输出流,Socket是否关闭?

    输出流刚打开,Socket是否关闭?false
    使用shutdownOutput关闭输出流,Socket是否关闭?false
    使用close关闭输出流,Socket是否关闭?true

    *使用shutdownOutputStream后,输出流是否可以重新开启?

    输出缓冲区里的数据是丢弃,还是发送?*

    ---------------------------------服务器------------------------------

    我是服务器,我在9999端口监听…
    我是服务器,我在9999端口监听…

    java.io.EOFException
     at java.io.DataInputStream.readUnsignedShort(Unknown Source)
     at java.io.DataInputStream.readUTF(Unknown Source)
     at java.io.DataInputStream.readUTF(Unknown Source)
     at com.test1.ServerSocketTest.run(ServerSocketTest.java:37)
    java.io.EOFException
     at java.io.DataInputStream.readUnsignedShort(Unknown Source)
     at java.io.DataInputStream.readUTF(Unknown Source)
     at java.io.DataInputStream.readUTF(Unknown Source)
     at com.test1.ServerSocketTest.run(ServerSocketTest.java:37)
    java.io.EOFException
     at java.io.DataInputStream.readUnsignedShort(Unknown Source)
    

    我是服务器,我在9999端口监听…

     at java.io.DataInputStream.readUTF(Unknown Source)
     at java.io.DataInputStream.readUTF(Unknown Source)
     at com.test1.ServerSocketTest.run(ServerSocketTest.java:37)
    

    总结:

    1. 直接调用流的.close()会将socket一同关闭,如果想单方面关闭某个流而不关闭socket,可以调用socket.shutdownXXX();
    2. 使用带有缓冲区的包装流的话需要注意刷新缓存区问题,即flush(),即便不刷新最后正常关闭的时候也会刷出去。
  • 相关阅读:
    MSSQL2008 R2 数据库展开报错:值不能为空 参数名:viewInfo
    疑似Windows server自动更新引发的sqlserver宕机
    SQL SERVER 数据库被标记为“可疑”的解决办法
    SQL SERVER日志中报错:等待闩锁时出现超时:类 log_manager
    sqlserver服务因登陆失败无法启动1069
    sqlserver事务日志增长过快
    SQL Server – “Could not connect because the maximum number of ‘1’ user connections has already been reached.”
    SQL SERVER 2012评估期过期
    sqlserver服务启动失败1067
    SQL Server 检测到基于一致性的逻辑 I/O 错误 pageid 不正确(应为 1:10202320,但实际为 0:0)
  • 原文地址:https://www.cnblogs.com/tfxz/p/12621592.html
Copyright © 2020-2023  润新知