通过Socket读取数据InputStream.read()阻塞问题

本文探讨了在Java中使用Socket进行客户端-服务端通信时遇到的阻塞问题。作者通过示例代码展示了客户端发送消息后,服务端的`inputStream.read()`方法阻塞的情况,并提出了两种解决方案:1) 服务端限制一次读取固定长度的数据;2) 客户端在发送完消息后调用`shutdownOutput()`方法。这两种方法可以避免阻塞并确保数据的正常交换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

想用Socket写一个Server端和Client端一个请求回复的Demo 发现程序阻塞了 记录一下

我是这么写的

public class BioClient1 {
    public static void main(String[] args) throws IOException {
        Socket socket = null;
        try {
            //发送你好
            socket = new Socket("127.0.0.1", 8080);
            OutputStream outputStream = socket.getOutputStream();
            BufferedOutputStream dos = new BufferedOutputStream(outputStream);
            dos.write("你好啊".getBytes());
            dos.flush();
            //接收服务端返回数据
            InputStream inputStream = socket.getInputStream();
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int offset = -1;
            byte[] buffer = new byte[1024];
            while ((offset = inputStream.read(buffer)) != -1) {
                byteArrayOutputStream.write(buffer, 0, offset);
            }
            inputStream.close();
            System.out.println(byteArrayOutputStream);
            byteArrayOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                socket.close();
            }
        }
    }

}
public class BioServer1 {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);

        for(;;) {
            Socket accept = serverSocket.accept();
            SocketHandle socketHandle = new SocketHandle(accept);
            socketHandle.start();
        }
    }


    static class SocketHandle extends Thread {
        private Socket socket;

        SocketHandle(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                InputStream inputStream = socket.getInputStream();
                byte[] bytes = new byte[1024];
                int offset = -1;
                //如果用这个的话这行代码的read方法会阻塞 因为客户端的OutputStream没有关闭
                while ((offset = inputStream.read(bytes)) != -1) {
                    baos.write(bytes, 0, offset);
                }
                byte[] result = new byte[read];
                System.arraycopy(bytes, 0, result, 0, read);
                System.out.println(new String(result, StandardCharsets.UTF_8));
                OutputStream outputStream = socket.getOutputStream();
                outputStream.write("你也好啊".getBytes());
                outputStream.flush();
                outputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    socket.close();
                } catch (Exception e) {
                }
            }

        }
    }
}

整个流程大概是

 阻塞问题出现在Server端的inputStream.read()中 这个是因为客户端的outputStream发送完消息后并没有close 因为我之后还要用到客户端的inputStream获取服务端回复的消息 如果客户端发送完消息就关闭outputStream 之后获取inputStream会报连接重置异常 所以这边给出两种方案

1.只修改服务端只调用一次 inputStream.read  这样就不会阻塞了 但只能获取不能大于的数组大小的字符了 

public class BioServer1 {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);

        for(;;) {
            Socket accept = serverSocket.accept();
            SocketHandle socketHandle = new SocketHandle(accept);
            socketHandle.start();
        }
    }


    static class SocketHandle extends Thread {
        private Socket socket;

        SocketHandle(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                InputStream inputStream = socket.getInputStream();
                byte[] bytes = new byte[1024];
                int read = inputStream.read(bytes);
                byte[] result = new byte[read];
                System.arraycopy(bytes, 0, result, 0, read);
                System.out.println(new String(result, StandardCharsets.UTF_8));
                OutputStream outputStream = socket.getOutputStream();
                outputStream.write("你也好啊".getBytes());
                outputStream.flush();
                outputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    socket.close();
                } catch (Exception e) {
                }
            }
        }
    }
}

客户端的代码不用修改 但如果把服务端中的最后的socket.close和outputStream.close();去掉的话 客户端依旧会阻塞 

2.在客户端发送信息完成之后调用socket的shutdownOutput方法 这个就只需要修改客户端的代码

public class BioClient1 {
    public static void main(String[] args) throws IOException {
        Socket socket = null;
        try {
            //发送你好
            socket = new Socket("127.0.0.1", 8080);
            OutputStream outputStream = socket.getOutputStream();
            BufferedOutputStream dos = new BufferedOutputStream(outputStream);
            dos.write("你好啊".getBytes());
            dos.flush();
            //添加关闭socket 输出流
            socket.shutdownOutput();

            //接收服务端返回数据
            InputStream inputStream = socket.getInputStream();
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int offset = -1;
            byte[] buffer = new byte[1024];
            while ((offset = inputStream.read(buffer)) != -1) {
                byteArrayOutputStream.write(buffer, 0, offset);
            }
            inputStream.close();
            System.out.println(byteArrayOutputStream);
            byteArrayOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                socket.close();
            }
        }
    }

}

这个方法相对优雅 

相关阅读

记录一次Socket的异常:InputStream.read()阻塞问题_鸡冠花12138的博客-CSDN博客_inputstream read 超时

 

### Java `InputStream.read()` 方法阻塞的原因 `InputStream.read()` 方法会在没有可用字节时进入等待状态直到有数据到达或流关闭为止[^1]。当调用该方法而底层资源(例如套接字)没有任何待读取的数据时,当前线程会被挂起并处于阻塞状态。 对于基于TCP协议的Socket通信来说,在接收端通过`InputStream`尝试读取来自发送端的消息时如果对方尚未完成写入操作,则本地进程中的读取动作就会一直等待下去直至满足条件或者发生错误情况如连接中断等[^4]。 另外值得注意的是,即使存在某些情况下确实进行了所谓的“发送”,但如果实际传输的内容为空字符串(`""`)的话,那么接收侧依旧无法获取到有效的载荷信息从而导致看似无休止地停滞不前的现象出现。 #### 解决方案概述 为了防止不必要的长时间延迟甚至永久性的卡顿现象的发生,可以采取如下几种措施: - **设置超时时间** 可以为关联着特定通道实例的对象设定最大允许停留期限以便于在网络状况不佳或者其他异常情形下能够及时做出响应而不是无限期地持续监听下去。这可以通过配置对应的socket选项实现,比如SO_TIMEOUT参数用于指定以毫秒计的最大等候周期长度[^2]。 ```java // 设置读取超时时间为5秒钟 socket.setSoTimeout(5000); ``` - **优化消息结构设计** 应避免直接传递未经处理过的原始字符序列作为通讯载体的一部分;相反应当采用更加严谨的方式来构建报文体裁——譬如附加定界符或是规定确切大小限制等等手段确保每一帧都能被清晰识别出来进而减少误判的可能性。 - **捕获并妥善处置异常事件** 当遇到由上述调整所引发的各种意外状况之时,务必要编写好相应的捕捉逻辑来保障整个应用程序不至于因为单次失败就彻底崩溃掉而是可以选择重试机制或者是记录日志之后优雅退出[^3]。 ```java try { int bytesRead = inputStream.read(buffer); } catch (InterruptedIOException e) { System.out.println("Read timed out"); } catch (IOException e) { // Handle other possible I/O errors... } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值