1. 程式人生 > 其它 >深入刨析tomcat 之---第3篇 HTTP/1.1 長連線的實現原理

深入刨析tomcat 之---第3篇 HTTP/1.1 長連線的實現原理

writedby 張豔濤

長連線是HTTP/1.1的特徵之一,1.1出現的原因是因為一個客戶請求一個網頁,這是一個http請求,這個網頁中如果有圖片,那麼也會變為一個http請求,對於java客戶端,一個http請求

是通過socket.getinputstream.cast(PrintWriter).println("http請求頭"),如果倆個請求都通過一個socket來寫資料,那麼這個就是http長連線,如果你寫一個簡單http伺服器,那你實現的就不是長連線,每次請求都把socket.close()了,

所以判斷一個http請求時不是長連線就是判斷socket.close有沒有執行

那麼我們來看tomcat是如何實現長連線了的,對應深入理解tomcat第4章

實現思路是,如果socket不斷開的話,那麼socket.getInputStream(),得到的in流 會呼叫in.read()方法,進行阻塞,如果來了資料,讀取新進來的請求,如果滿足http協議進行解析;

 HttpProcessor類
public void run() {

        // Process requests until we receive a shutdown signal
        while (!stopped) {

            // Wait for the next socket to be assigned
            Socket socket = await
(); if (socket == null) continue; // Process the request from this socket try { process(socket); } catch (Throwable t) { log("process.invoke", t); } // Finish up this request connector.recycle(this
); } // Tell threadStop() we have shut ourselves down successfully synchronized (threadSync) { threadSync.notifyAll(); } }

進入方法

    private void process(Socket socket) {
        boolean ok = true;
        boolean finishResponse = true;
        SocketInputStream input = null;
        OutputStream output = null;

        // Construct and initialize the objects we will need
        try {
            input = new SocketInputStream(socket.getInputStream(),
                                          connector.getBufferSize());
        } catch (Exception e) {
            log("process.create", e);
            ok = false;
        }

        keepAlive = true;

        while (!stopped && ok && keepAlive) {

            finishResponse = true;

            try {
                request.setStream(input);
                request.setResponse(response);
                output = socket.getOutputStream();
                response.setStream(output);
                response.setRequest(request);
                ((HttpServletResponse) response.getResponse()).setHeader
                    ("Server", SERVER_INFO);
            } catch (Exception e) {
                log("process.create", e);
                ok = false;
            }


            // Parse the incoming request
            try {
                if (ok) {

                    parseConnection(socket);
                    parseRequest(input, output);
                    if (!request.getRequest().getProtocol()
                        .startsWith("HTTP/0"))
                        parseHeaders(input);
                    if (http11) {
                        // Sending a request acknowledge back to the client if
                        // requested.
                        ackRequest(output);
                        // If the protocol is HTTP/1.1, chunking is allowed.
                        if (connector.isChunkingAllowed())
                            response.setAllowChunking(true);
                    }

                }
            異常...略
            }

            // Finish up the handling of the request
            if (finishResponse) {
                try {
                    response.finishResponse();
                } catch (IOException e) {
                    ok = false;
                } catch (Throwable e) {
                    log("process.invoke", e);
                    ok = false;
                }
                try {
                    request.finishRequest();
                } catch (IOException e) {
                    ok = false;
                } catch (Throwable e) {
                    log("process.invoke", e);
                    ok = false;
                }
                try {
                    if (output != null)
                        output.flush();
                } catch (IOException e) {
                    ok = false;
                }
            }

            // We have to check if the connection closure has been requested
            // by the application or the response stream (in case of HTTP/1.0
            // and keep-alive).
            if ( "close".equals(response.getHeader("Connection")) ) {
                keepAlive = false;
            }

            // End of request processing
            status = Constants.PROCESSOR_IDLE;

            // Recycling the request and the response objects
            request.recycle();
            response.recycle();

        }

        try {
            shutdownInput(input);
            socket.close();
        } catch (IOException e) {
            ;
        } catch (Throwable e) {
            log("process.invoke", e);
        }
        socket = null;



    }

進入方法

  input.readRequestLine(requestLine);

接著進入

public void readRequestLine(HttpRequestLine requestLine)
        throws IOException {

        // Recycling check
        if (requestLine.methodEnd != 0)
            requestLine.recycle();

        // Checking for a blank line
        int chr = 0;
        do { // Skipping CR or LF
            try {
                chr = read();
            } catch (IOException e) {
                chr = -1;
            }
        } while ((chr == CR) || (chr == LF));
        if (chr == -1)
            throw new EOFException
                (sm.getString("requestStream.readline.error"));
        pos--;

        // Reading the method name

接著進入

    public int read()
        throws IOException {
        if (pos >= count) {//讀到了結尾
            fill();
            if (pos >= count)
                return -1;
        }
        return buf[pos++] & 0xff;
    }

接著

    /**
     * Fill the internal buffer using data from the undelying input stream.
     */
    protected void fill()
        throws IOException {
        pos = 0;
        count = 0;
        int nRead = is.read(buf, 0, buf.length);
        if (nRead > 0) {
            count = nRead;
        }
    }
parseRequest(input, output);


//input的構造方法
input = new SocketInputStream(socket.getInputStream(),
                                          connector.getBufferSize());

//
    public SocketInputStream(InputStream is, int bufferSize) {

        this.is = is;
        buf = new byte[bufferSize];

    }

上述倆個方法中的fill() 的is.read() 底層就是socket.getInputStream進行讀到緩衝區

這個方法是有阻塞的,那麼就實現了處理完一個http請求,接著讀取第二個請求

如果以上過程報錯,跳出while迴圈

        try {
            shutdownInput(input);
            socket.close();
        } catch (IOException e) {
            ;
        } catch (Throwable e) {
            log("process.invoke", e);
        }
        socket = null;

關閉socket,那麼就斷開了socket長連線

此文結束