关于Socket流中read()的阻塞问题

关于Socket流中read()的阻塞问题

在我学习Tcp的时候我遇到了一个问题就是关于socket的阻塞问题,具体代码是这样的

服务端:

public class MyServer {
    public static void main(String[] args) throws IOException {
        try (
                ServerSocket serverSocket = new ServerSocket(8281);
                Socket socket = serverSocket.accept();

                InputStream is = socket.getInputStream();
                OutputStream os = socket.getOutputStream();
                FileOutputStream fos = new FileOutputStream("copy.png")

        ) {
            int len;
            byte[] bytes = new byte[1024];
			//接收图片数据
            while ((len = is.read(bytes)) != -1) {
                System.out.println(len);
                fos.write(bytes, 0, len);

            }
			//向客户端返回接收成功
			System.out.println("接收成功");
            os.write("传输成功".getBytes());

        }
    }

}

客户端:

public class MyClient {
    public static void main(String[] args) throws IOException {
        try(
                Socket socket = new Socket("127.0.0.1",8281);
                InputStream is = socket.getInputStream();
                OutputStream os = socket.getOutputStream();
                BufferedReader br = new BufferedReader(new InputStreamReader(is));
                FileInputStream fs = new FileInputStream("D:\\WorkSpace\\core_java\\img.png")
        ){
        //发送数据到服务端
           int len;
            byte[] bytes = new byte[102400];
            while ((len = fs.read(bytes))!=-1){
                os.write(bytes,0,len);

            }
			//接收服务端的返回信息
            String line =br.readLine();
            System.out.println(line);

        }
    }



}

当运行这个程序的时候,我们发现当接收完了图片之后,服务端并没有接着运行下面给客户端传递接收消息的代码:输出接收成功。

在这里插入图片描述

通过调试发现原来一直服务端代码一直阻塞在了 while ((len = is.read(bytes)) != -1)这个地方,“那为什么图片接收到了末尾,不应该返回-1然后退出循环吗,为什么会一直阻塞在这里”。

这里我通过查阅资料给出的解释是,socket里面的流(socket.getInputStream();socket.getOutputStream();)和普通的流(FileOutputStream fos = new FileOutputStream(“copy.png”))是不一样的,普通的流读到末尾时read会返回一个-1,但是socket流并不会,socket流没有结束符。所以read会阻塞等待客户端发送数据。

那么到这里我就有了2个疑问:

1.那怎么解决这个阻塞的问题

2.既然socket流没有结束符那(len = is.read(bytes)) != -1,这里的-1是否可以改为其他任意数字,因为这里的-1似乎没有任何作用。

怎么解决这个阻塞问题

方法1:

当客户端把图片传完后我们显示的调用socket.shutdownOutput();来关闭输出流以告知客户端我的图片资源传完了。

public class MyClient {
    public static void main(String[] args) throws IOException {
        try(
                Socket socket = new Socket("127.0.0.1",8281);
                InputStream is = socket.getInputStream();
                OutputStream os = socket.getOutputStream();
                BufferedReader br = new BufferedReader(new InputStreamReader(is));
                FileInputStream fs = new FileInputStream("D:\\WorkSpace\\core_java\\img.png")
        ){
           int len;
            byte[] bytes = new byte[102400];
            while ((len = fs.read(bytes))!=-1){
                os.write(bytes,0,len);
            }

            socket.shutdownOutput();

            String line =br.readLine();
            System.out.println(line);

        }
    }
}

结果:

在这里插入图片描述

方法2:

在服务端的接收循环里面加一个判断条件,如果当前的len小于数组bytes的大小,那么则退出循环--------当读到图片的末尾部分时,最后一次读取时len的值要么等于0,要么小于数组bytes的长度。

public class MyServer {
    public static void main(String[] args) throws IOException {
        try (
                ServerSocket serverSocket = new ServerSocket(8281);
                Socket socket = serverSocket.accept();
                InputStream is = socket.getInputStream();
                OutputStream os = socket.getOutputStream();
                FileOutputStream fos = new FileOutputStream("copy.png")
        ) {
            int len;
            byte[] bytes = new byte[1024];

            while ((len = is.read(bytes)) != -1) {
                System.out.println(len);
                fos.write(bytes, 0, len);
                //判断退出条件
                if(len<bytes.length){
                    break;
                }
            }
            System.out.println("接收成功");
            os.write("传输成功".getBytes());
        }
    }
}

这也是行的通的。

注意事项:

当选用这个方法时,客户端的暂存数组bytes一定要大于等于服务端的暂存数组bytes,因为发送和读取的速度是很快的,如果客户端的数组小于服务端的数组,客户端一发出去,服务端会立马接收:比如客户端数组大小为1,服务端为1024.那么客户端发出一个1的大小数据,服务端就会用这个1024的数组接收,然后这里的len会等于1,然后继续下一个循环又从0开始接收,这样的话我们的len是永远小于1024的,所以行不通。

既然socket流没有结束符那(len = is.read(bytes)) != -1,这里的-1是否可以改为其他任意数字,因为这里的-1似乎没有任何作用:

经过我的测试,如果是用方法二来解决阻塞问题,那么这里的-1确实是可以改成任意数字,但是如果是用第一种方式通过socket.shutdownOutput();来解决阻塞问题时,这里的-1就不能改成其他的数字。他会报错。

由此我推断出当客户端关闭连接时,服务端的read就会读出一个-1,然后通过这个-1来退出服务端的读取循环。

还有就是当客户端代码为以下时,又不会发生阻塞问题:

public class MyClient {
    public static void main(String[] args) throws IOException {
        try(
                Socket socket = new Socket("127.0.0.1",8281);
                InputStream is = socket.getInputStream();
                OutputStream os = socket.getOutputStream();
                BufferedReader br = new BufferedReader(new InputStreamReader(is));
                FileInputStream fs = new FileInputStream("D:\\WorkSpace\\core_java\\img.png")
        ){
           int len;
            byte[] bytes = new byte[102400];
            while ((len = fs.read(bytes))!=-1){
                os.write(bytes,0,len);
            }

        }
    }



}

因为当图片数据传完后,这个try的代码块就执行完了,然后会自动关流。关了流,服务端就知道传完了,就不会发生阻塞问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值