浅谈java socket的正确关闭姿势

 更新时间:2021年06月15日 14:49:52   作者:wkCaeser_  
这篇文章主要介绍了java socket的正确关闭姿势,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

java socket对应的是网络协议中的tcp,tcp的三次握手、四次挥手、11中状态什么的这里就不说了,不知道大家平常使用socket的时候如果不注意的情况下,会不会遇到各种异常报错。

例如:

java.net.SocketException:socket is closed

错误提示的出现场景:

自己主动关闭了socket,但是之后还从里面读写数据

Software caused connection abort: socket write error

错误提示的出现场景:

对方已经关闭socket,依旧向对方写数据

connection reset (by peer)

错误提示出现的场景:

一端socket被关闭,另一端仍然发送数据,发送的第一个数据包 connection reset by peer

一端socket退出,退出时为关闭连接,另一端读数据 connection reset

所以在使用socket时,需要约定好双方读写完成的条件,然后关闭输入输出流:

socket.shutdownInput();
socket.shutdownOutput();

即当一方写入完成后,调用shutdownOutput关闭输出流,这时候对方的read方法就会返回-1,这时候对方就知道你写完了,对方可以关闭输入流,然后等待对方写入完成调用shutdownOutput后己方再调用shutdownInput,双方就正常关闭了输入输出流,这时候socket就不会出现异常了。

下面是一个socket交互的例子:

server端

public class OioServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        while (true) {
            Socket socket = serverSocket.accept();
            System.out.println("socket = " + socket);
            new Thread(() -> {
                try {
                    InputStream in = socket.getInputStream();
                    OutputStream out = socket.getOutputStream();
                    out.write("hello! I get your message that is follow".getBytes(Charset.forName("UTF-8")));
                    byte[] buf = new byte[1024];
                    int len;
                    while ((len = in.read(buf)) != -1) {
                        System.out.print(new String(buf, 0, len, Charset.forName("UTF-8")));
                        out.write(buf, 0, len);
                    }
                    out.write("\n end \n".getBytes(Charset.forName("UTF-8")));
                    out.flush();
                    socket.shutdownInput();
                    socket.shutdownOutput();
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

client端

public class OioClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 8080);
        InputStream in = socket.getInputStream();
        new Thread(() -> {
            BufferedInputStream bufferIn = new BufferedInputStream(in);
            byte[] buf = new byte[1024];
            try {
                int len;
                while ((len = bufferIn.read(buf)) != -1) {
                    System.out.print(new String(buf, 0, len, Charset.forName("UTF-8")));
                }
            }catch (Exception e) {
                e.printStackTrace();
            }
            try {
                socket.shutdownInput();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
        OutputStream out = socket.getOutputStream();
        int cout = 10;
        while (cout-- > 0) {
            out.write(("this time is " + System.currentTimeMillis() + "\n").getBytes("UTF-8"));
        }
        socket.shutdownOutput();
    }
}

java socket - 半关闭

通常,使用关闭输出流来表示输出已经结束。但在进行网络通信时则不能这样做。因为我们关闭输出流时,该输出流对应的Socket也将随之关闭,这样程序将无法再从该socket中读取数据。

为了应付这种情况,socket提供了两个半关闭的方法用来只关闭socket的输入流或者输出流,用以表示输出数据已经发送完成。

方法详情:

shutdownInput():关闭该socket的输入流,程序还可以通过该socket的输出流输出数据;

shutdownOutput():关闭该socket的输出流,程序还可以通过该socket的输入流读取数据。

当调用shutdownInput()或shutdownOutput()方法关闭输入流或输出流后,该socket处于半关闭状态。

此时可以使用isInputShutdown()或isOutputShutdown()来判断该socket是否处于半读状态或半写状态。

需要注意的是,即使同一个socket先后调用shutdownInput()和shutdownInput()方法,该socket实例仍然没有被关闭,只是该socket既不能输出数据也不能读取数据而已。

当调用shutdownInput()或shutdownOutput()方法关闭了输入流或输出流之后,该socket无法再次打开输出流或输入流,因此这种做法不适合需要保持持久通信状态的交互式应用。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • SpringMVC Json自定义序列化和反序列化的操作方法

    SpringMVC Json自定义序列化和反序列化的操作方法

    这篇文章主要介绍了SpringMVC Json自定义序列化和反序列化的操作方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-01-01
  • Java中内存问题之OOM详解

    Java中内存问题之OOM详解

    这篇文章主要介绍了Java中内存管理的OOM详解,OOM,全称“Out Of Memory”,翻译成中文就是“内存用完了”,来源于java.lang.OutOfMemoryError,当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个error,需要的朋友可以参考下
    2023-08-08
  • SpringBoot框架配置文件路径设置方式

    SpringBoot框架配置文件路径设置方式

    这篇文章主要介绍了SpringBoot框架配置文件路径设置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • springboot实现浏览器截屏并添加文字

    springboot实现浏览器截屏并添加文字

    大家好,本篇文章主要讲的是springboot实现浏览器截屏并添加文字,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-02-02
  • Sentinel 整合SpringCloud的详细教程

    Sentinel 整合SpringCloud的详细教程

    Spring Cloud Alibaba Sentinel 是阿里巴巴提供的,致力于提供微服务一站式解决方案,这篇文章主要介绍了Sentinel 之 整合SpringCloud的相关知识,需要的朋友可以参考下
    2021-10-10
  • Java编程一维数组转换成二维数组实例代码

    Java编程一维数组转换成二维数组实例代码

    这篇文章主要介绍了Java编程一维数组转换成二维数组,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-01-01
  • java中哈希表及其应用详解

    java中哈希表及其应用详解

    Java中哈希表(Hashtable)是如何实现的呢?Hashtable中有一个内部类Entry,用来保存单元数据,我们用来构建哈希表的每一个数据是Entry的一个实例。假设我们保存下面一组数据,第一列作为key, 第二列作为value。
    2015-06-06
  • SpringBoot打jar包遇到的xml文件丢失的解决方案

    SpringBoot打jar包遇到的xml文件丢失的解决方案

    这篇文章主要介绍了SpringBoot打jar包遇到的xml文件丢失的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java中小球碰撞并使用按钮控制数量实例代码

    Java中小球碰撞并使用按钮控制数量实例代码

    这篇文章主要给大家介绍了关于Java中小球碰撞并使用按钮控制数量的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2021-12-12
  • Mybatis以main方法形式调用dao层执行代码实例

    Mybatis以main方法形式调用dao层执行代码实例

    这篇文章主要介绍了Mybatis以main方法形式调用dao层执行代码实例,MyBatis 是一款优秀的持久层框架,MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作,需要的朋友可以参考下
    2023-08-08

最新评论