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("\n****2种方式关闭输出流,Socket是否关闭?***\n");  
        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("\n****使用shutdownOutputStream后,输出流是否可以重新开启?***\n");  
        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("\n***输出缓冲区里的数据是丢弃,还是发送?****\n");  
        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(),即便不刷新最后正常关闭的时候也会刷出去。
posted @ 2019-11-29 15:16  超级小白龙  阅读(547)  评论(0编辑  收藏  举报