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請求格式:

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM