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

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

想用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 超时

 

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值