zoukankan      html  css  js  c++  java
  • 深入刨析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长连接

    此文结束

  • 相关阅读:
    数组 A
    考试中。。。
    高精度加法
    1319:【例6.1】排队接水
    Docker基于容器创建镜像
    Docker上传/拉取阿里云镜像
    Docker配置阿里镜像加速器
    CentOS7安装、卸载Docker
    Python3+requests+unittest+log+excel+HTMLTestRunner+email框架接口自动化案例⑼——执行用例及GitHub源码地址
    Python3+requests+unittest+log+excel+HTMLTestRunner+email框架接口自动化案例⑻——测试用例
  • 原文地址:https://www.cnblogs.com/zytcomeon/p/14964551.html
Copyright © 2011-2022 走看看