socket流读取read阻塞和readLine阻塞问题解决方案

场景:编写一个简单的httpserver,请求一直无响应。 

分析:经排查,发现是在对socket的inputStream的最后一行读取时阻塞了。代码大概如下:

        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        String line = "";
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }    

 

在网上搜索得出问题的根本原因:socket流没有结束符。我们对流的读取大概分两种,read()和readLine()。

阻塞场景:read()            没有读取到任何数据
        readLine()        没有读取到结束符或者换行符

正是因为socket流没有结束符,而我们又不能强求请求体最后一定加上换行符,所以导致在readLine最后一行阻塞了。

 

1、换成read方法读取也不行,比如下面;结果read永远不会返回-1,因为没有结束符,最后在没有读取到数据的情况没有返回-1,而是选择了等待。

        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        char[] bt = new char[1024];
        while (br.read(bt) != -1) {
            System.out.println(bt);
        }

2、对于流数据比较小的情况,我们可以给bt初始化一个足够大的长度,一次将所有数据读取出来;http请求可以保证请求数据不会为空,继而保证了第一次读取不会阻塞。但这只是个妥协的办法,因为我们请求长度不可控。

        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        char[] bt = new char[足够大];
        br.read(bt);
        System.out.println(bt);    

 

解决方案:在读取前,使用ready()方法判断是否还有数据没有读取。http请求不会为空,所以使用do-while保证能读取到数据。注意:这里如果使用while,可能会在第一次ready()时数据还没传输过来导致直接跳过。

        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        char[] bt = new char[1024];
        do {
            br.read(bt);
            System.out.println(bt);
        } while (br.ready());

 

最后再看看最终http请求读取、解析的代码。为了方便解析,请求头使用readLine(),请求体使用read。而且readLine()效率高于read()。

    public void parse() throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        int lineNum = 0;
        String line = "";
        while ((line = br.readLine()) != null) {
            if ("".equals(line)) {
                System.out.println("");
                //请求体
                parseContent(br);
                break;
            }
            if (lineNum++ == 0) {
                //首行
                parseFirstLine(line);
            } else {
                //请求头
                parseHeader(line);
            }
        }
    }

    private void parseFirstLine(String line) {
        String[] first = line.split("\\s");
        method = first[0];
        url = first[1];
        agree = first[2];
        System.out.println(line);
    }

    private void parseHeader(String line) {
        //TODO 待实现
        System.out.println(line);
    }

    private void parseContent(BufferedReader br) throws IOException {
        StringBuilder sb = new StringBuilder();
        char[] bt = new char[1024];
        while (br.ready())  {
            br.read(bt);
            sb.append(bt);
        };
        this.content = sb.toString();
        System.out.println(content);
    }

结果与http请求格式:

 

 

 

posted @ 2020-11-13 14:00  士三刀  阅读(5128)  评论(1编辑  收藏  举报