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长连接
此文结束