Tomcat中的连接器是一个独立的模块,可以被插入到servlet容器中,而且还有很多连接器可以使用。例如Coyote,mod_jk,mod_jk2,mod_webapp等。Tomcat中使用的连接器必须满足以下条件:
1.实现org.apache.catalina.Connector接口
2.负责创建实现了org.apache.catalina.Request接口的request对象
3.负责创建实现了org.apache.catalina.Response接口的response对象
Tomcat的连接器等待引入的HTTP请求,创建request对象和response对象,然后调用org.apache.catalina.Container接口的invoke()方法,将request对象和response对象传给servlet容器。
package com.whatsmars.tomcat.connector; /** * Created by shenhongxi on 16/4/11. */ public final class Bootstrap { public static void main(String[] args) { HttpConnector connector = new HttpConnector(); Container container = new SimpleContainer(); connector.setContainer(container); connector.setBufferSize(2048); connector.start(); } }
package com.whatsmars.tomcat.connector; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; /** * Created by shenhongxi on 16/4/11. */ public class HttpConnector implements Runnable { private Container container; boolean stopped; private String scheme = "http"; private int bufferSize; public void run() { ServerSocket serverSocket = null; int port = 8080; try { serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")); } catch (IOException e) { e.printStackTrace(); System.exit(1); } while (!stopped) { // Accept the next incoming connection from the server socket Socket socket = null; try { socket = serverSocket.accept(); } catch (Exception e) { continue; } // Hand this socket off to an HttpProcessor HttpProcessor processor = new HttpProcessor(this); processor.process(socket); } } public void start() { new Thread(this).start(); } public String getScheme() { return scheme; } public int getBufferSize() { return bufferSize; } public void setBufferSize(int bufferSize) { this.bufferSize = bufferSize; } public Container getContainer() { return container; } public void setContainer(Container container) { this.container = container; } }
package com.whatsmars.tomcat.connector; import org.apache.catalina.util.Enumerator; import org.apache.catalina.util.ParameterMap; import org.apache.catalina.util.RequestUtil; import javax.servlet.*; import javax.servlet.http.*; import java.io.BufferedReader; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.Principal; import java.util.*; /** * Created by shenhongxi on 16/4/11. */ public class HttpRequest implements HttpServletRequest { private String requestURI; private int contentLength; private String contentType; private String queryString; private String method; private String protocol; protected Map headers = new HashMap(); protected SocketInputStream input; /** * The parsed parameters for this request. This is populated only if * parameter information is requested via one of the * <code>getParameter()</code> family of method calls. The key is the * parameter name, while the value is a String array of values for this * parameter. * <p> * <strong>IMPLEMENTATION NOTE</strong> - Once the parameters for a * particular request are parsed and stored here, they are not modified. * Therefore, application level access to the parameters need not be * synchronized. */ protected ParameterMap parameters = null; // extends LinkedHashMap has a boolean var 'locked' /** * Have the parameters for this request been parsed yet? */ protected boolean parsed = false; public HttpRequest(SocketInputStream input) { this.input = input; } public void addHeader(String name, String value) { name = name.toLowerCase(); synchronized (headers) { ArrayList values = (ArrayList) headers.get(name); if (values == null) { values = new ArrayList(); headers.put(name, values); } values.add(value); } } public String getParameter(String name) { parseParameters(); String values[] = (String[]) parameters.get(name); if (values != null) return (values[0]); else return (null); } public Map getParameterMap() { parseParameters(); return (this.parameters); } public Enumeration getParameterNames() { parseParameters(); return (new Enumerator(parameters.keySet())); } public String[] getParameterValues(String name) { parseParameters(); String values[] = (String[]) parameters.get(name); if (values != null) return (values); else return null; } /** * Parse the parameters of this request, if it has not already occurred. * If parameters are present in both the query string and the request * content, they are merged. */ protected void parseParameters() { if (parsed) return; ParameterMap results = parameters; if (results == null) results = new ParameterMap(); results.setLocked(false); String encoding = getCharacterEncoding(); if (encoding == null) encoding = "ISO-8859-1"; // Parse any parameters specified in the query string String queryString = getQueryString(); try { RequestUtil.parseParameters(results, queryString, encoding); } catch (Exception e) { ; } // Parse any parameters specified in the input stream String contentType = getContentType(); if (contentType == null) contentType = ""; int semicolon = contentType.indexOf(';'); if (semicolon >= 0) { contentType = contentType.substring(0, semicolon).trim(); } else { contentType = contentType.trim(); } if ("POST".equals(getMethod()) && (getContentLength() > 0) && "application/x-www-form-urlencoded".equals(contentType)) { try { int max = getContentLength(); int len = 0; byte buf[] = new byte[getContentLength()]; ServletInputStream is = getInputStream(); while (len < max) { int next = is.read(buf, len, max - len); if (next < 0 ) { break; } len += next; } is.close(); if (len < max) { throw new RuntimeException("Content length mismatch"); } RequestUtil.parseParameters(results, buf, encoding); } catch (UnsupportedEncodingException ue) { ; } catch (IOException e) { throw new RuntimeException("Content read fail"); } } // Store the final results results.setLocked(true); parsed = true; parameters = results; } public void setRequestURI(String requestURI) { this.requestURI = requestURI; } public void setContentLength(int contentLength) { this.contentLength = contentLength; } public void setContentType(String contentType) { this.contentType = contentType; } public void setQueryString(String queryString) { this.queryString = queryString; } public void setMethod(String method) { this.method = method; } public void setProtocol(String protocol) { this.protocol = protocol; } // ... other methods }
package com.whatsmars.tomcat.connector; import javax.servlet.ServletOutputStream; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.util.Collection; import java.util.Locale; /** * Created by shenhongxi on 16/4/11. */ public class HttpResponse implements HttpServletResponse { OutputStream output; HttpRequest request; PrintWriter writer; public HttpResponse(OutputStream output) { this.output = output; } public void setRequest(HttpRequest request) { this.request = request; } /** * call this method to send headers and response to the output */ public void finishResponse() { // sendHeaders(); // Flush and close the appropriate output mechanism if (writer != null) { writer.flush(); writer.close(); } } public void addCookie(Cookie cookie) { } public boolean containsHeader(String name) { return false; } public String encodeURL(String url) { return null; } public String encodeRedirectURL(String url) { return null; } public String encodeUrl(String url) { return null; } public String encodeRedirectUrl(String url) { return null; } public void sendError(int sc, String msg) throws IOException { } public void sendError(int sc) throws IOException { } public void sendRedirect(String location) throws IOException { } // ... other methods }
package com.whatsmars.tomcat.connector; import com.whatsmars.tomcat.servlet.StaticResourceProcessor; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; /** * Created by shenhongxi on 16/4/11. */ public class HttpProcessor { private HttpConnector connector; private HttpRequest request; private HttpResponse response; private HttpRequestLine requestLine = new HttpRequestLine(); public HttpProcessor(HttpConnector connector) { this.connector = connector; } public void process(Socket socket) { SocketInputStream input = null; OutputStream output = null; try { input = new SocketInputStream(socket.getInputStream(), connector.getBufferSize()); // 1.读取套接字的输入流 output = socket.getOutputStream(); // create HttpRequest object and parse request = new HttpRequest(input); response = new HttpResponse(output); response.setRequest(request); response.setHeader("Server", "Mars Servlet Container"); parseRequest(input, output); // 解析请求行,即HTTP请求的第一行内容 parseHeaders(input); // 解析请求头 if (request.getRequestURI().startsWith("/servlet/")) { connector.getContainer().invoke((HttpServletRequest) request, (HttpServletResponse) response); } else { StaticResourceProcessor processor = new StaticResourceProcessor(); //processor.process(request, response); } socket.close(); } catch (Exception e) { e.printStackTrace(); } } private void parseHeaders(SocketInputStream input) throws IOException, ServletException{ while (true) { // 一行一行解析完header HttpHeader header = new HttpHeader(); // Read the next header input.readHeader(header); if (header.nameEnd == 0) { if (header.valueEnd == 0) { return; } else { throw new ServletException("httpProcessor parseHeaders colon"); } } String name = new String(header.name, 0, header.nameEnd); String value = new String(header.value, 0, header.valueEnd); request.addHeader(name, value); // do something for some headers, ignore others. if (name.equals("cookie")) { // ... // request.addCookie(cookies[i]); } else if (name.equals("content-length")) { int n = -1; try { n = Integer.parseInt(value); } catch (Exception e) { throw new ServletException("httpProcessor.parseHeaders.contentLength"); } request.setContentLength(n); } else if (name.equals("content-type")) { request.setContentType(value); } } } private void parseRequest(SocketInputStream input, OutputStream output) throws IOException, ServletException { input.readRequestLine(requestLine); String method = new String(requestLine.method, 0, requestLine.methodEnd); String uri = null; String protocol = new String(requestLine.protocol, 0, requestLine.protocolEnd); // Validate the incoming request line if (method.length() < 1) { throw new ServletException("Missing HTTP request method"); } else if (requestLine.uriEnd < 1) { throw new ServletException("Missing HTTP request URI"); } // Parse any query parameters out of the request URI int question = requestLine.indexOf("?"); if (question >= 0) { request.setQueryString(new String(requestLine.uri, question + 1, requestLine.uriEnd - question - 1)); uri = new String(requestLine.uri, 0, question); } else { request.setQueryString(null); uri = new String(requestLine.uri, 0, requestLine.uriEnd); } String normalizedUri = normalize(uri); ((HttpRequest) request).setMethod(method); request.setProtocol(protocol); if (normalizedUri != null) { ((HttpRequest) request).setRequestURI(normalizedUri); } else { ((HttpRequest) request).setRequestURI(uri); } if (normalizedUri == null) { throw new ServletException("Invalid URI: " + uri + "'"); } } // Return a context-relative path, beginning with a "/" protected String normalize(String path) { if (path == null) return null; String normalized = path; // ... return path; } }
package com.whatsmars.tomcat.connector; /** * Created by shenhongxi on 16/4/13. */ public final class HttpHeader { public static final int INITIAL_NAME_SIZE = 32; public static final int INITIAL_VALUE_SIZE = 64; public static final int MAX_NAME_SIZE = 128; public static final int MAX_VALUE_SIZE = 4096; public char[] name; public int nameEnd; public char[] value; public int valueEnd; protected int hashCode = 0; public HttpHeader() { this(new char[INITIAL_NAME_SIZE], 0, new char[INITIAL_VALUE_SIZE], 0); } public HttpHeader(char[] name, int nameEnd, char[] value, int valueEnd) { this.name = name; this.nameEnd = nameEnd; this.value = value; this.valueEnd = valueEnd; } public HttpHeader(String name, String value) { this.name = name.toLowerCase().toCharArray(); this.nameEnd = name.length(); this.value = value.toCharArray(); this.valueEnd = value.length(); } /** * Release all object references, and initialize instance variables, in * preparation for reuse of this object. */ public void recycle() { nameEnd = 0; valueEnd = 0; hashCode = 0; } }
package com.whatsmars.tomcat.connector; /** * Created by shenhongxi on 16/4/13. */ public final class HttpRequestLine { public static final int INITIAL_METHOD_SIZE = 8; public static final int INITIAL_URI_SIZE = 64; public static final int INITIAL_PROTOCOL_SIZE = 8; public static final int MAX_METHOD_SIZE = 1024; public static final int MAX_URI_SIZE = 32768; public static final int MAX_PROTOCOL_SIZE = 1024; public char[] method; public int methodEnd; public char[] uri; public int uriEnd; public char[] protocol; public int protocolEnd; public HttpRequestLine() { this(new char[INITIAL_METHOD_SIZE], 0, new char[INITIAL_URI_SIZE], 0, new char[INITIAL_PROTOCOL_SIZE], 0); } public HttpRequestLine(char[] method, int methodEnd, char[] uri, int uriEnd, char[] protocol, int protocolEnd) { this.method = method; this.methodEnd = methodEnd; this.uri = uri; this.uriEnd = uriEnd; this.protocol = protocol; this.protocolEnd = protocolEnd; } public int indexOf(String str) { // ... return -1; } /** * Release all object references, and initialize instance variables, in * preparation for reuse of this object. */ public void recycle() { methodEnd = 0; uriEnd = 0; protocolEnd = 0; } }
package com.whatsmars.tomcat.connector; import java.io.IOException; import java.io.InputStream; /** * Created by shenhongxi on 16/4/11. * Extends InputStream to be more efficient reading lines during HTTP header processing. */ public class SocketInputStream extends InputStream { /** * Underlying input stream. */ private InputStream input; /** * Internal buffer. */ protected byte[] buf; /** * Last valid byte. */ protected int count; /** * Position in the buffer. */ protected int pos; public SocketInputStream(InputStream input, int bufferSize) { this.input = input; this.buf = new byte[bufferSize]; } // input => buf => HttpRequestLine public void readRequestLine(HttpRequestLine requestLine) throws IOException { // Recycling check if (requestLine.methodEnd != 0) requestLine.recycle(); // Checking for a blank line // Reading the method name // Reading URI // Reading protocol } // input => buf => HttpHeader public void readHeader(HttpHeader header) throws IOException { // Recycling check if (header.nameEnd != 0) header.recycle(); // Checking for a blank line // Reading the header name // Reading the header value (which can be spanned over multiple lines) } @Override 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 = input.read(buf, 0, buf.length); if (nRead > 0) { count = nRead; } } }
package com.whatsmars.tomcat.connector; import com.whatsmars.tomcat.servlet.Constants; import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.net.URLStreamHandler; /** * Created by shenhongxi on 16/4/14. */ public class SimpleContainer implements Container { public void invoke(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String servletName = ( (HttpServletRequest) request).getRequestURI(); servletName = servletName.substring(servletName.lastIndexOf("/") + 1); URLClassLoader loader = null; try { URL[] urls = new URL[1]; URLStreamHandler streamHandler = null; File classPath = new File(Constants.WEB_ROOT); String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ; urls[0] = new URL(null, repository, streamHandler); loader = new URLClassLoader(urls); } catch (IOException e) { System.out.println(e.toString() ); } Class myClass = null; try { myClass = loader.loadClass(servletName); } catch (ClassNotFoundException e) { System.out.println(e.toString()); } Servlet servlet = null; try { servlet = (Servlet) myClass.newInstance(); servlet.service((HttpServletRequest) request, (HttpServletResponse) response); } catch (Exception e) { System.out.println(e.toString()); } catch (Throwable e) { System.out.println(e.toString()); } } }
package com.whatsmars.tomcat.connector; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * Created by shenhongxi on 16/4/14. */ public interface Container { public void invoke(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException; }
最后,关于HttpProcessor池,连接器线程和处理器线程的等待与唤醒,请参考多线程知识自行脑补。
Request和Response的外观类(包装类)参见http://wely.iteye.com/blog/2290575
相关推荐
在文件"模拟Tomcat"中,可能包含了实现上述过程的源代码,包括连接器的实现、HTTP请求的解析、Servlet的调度、线程管理等模块。通过阅读和分析这些代码,我们可以更深入地学习Tomcat的工作原理,这对于理解和开发Web...
《模拟Tomcat运行机制》是一本深入解析Tomcat内核的书籍,主要针对Tomcat4.1.12和5.0.18版本。Tomcat作为一款流行的、开源的Servlet容器,其复杂性在于由多个组件组成。本书旨在帮助读者理解Tomcat的工作原理,无论...
【TOMCAT工作原理详解】 ...通过模拟TOMCAT工作过程,可以更深入地理解这些概念,为实际开发打下坚实基础。在`mvcdemo`这样的实践中,你可以亲自体验和学习如何在TOMCAT上部署和运行一个简单的MVC应用。
在IT行业中,构建一个模拟Tomcat或Servlet服务器是一项高级...通过以上知识点的学习和实践,你将能够构建一个基本的模拟Tomcat和Servlet服务器,这将帮助你深入理解Web服务器的工作原理,提升你的Java网络编程技能。
【标题】:“模拟Tomcat工作原理” 【描述】:这篇博文详细探讨了Tomcat服务器的工作流程,通过解析和模拟其内部机制,帮助读者深入理解Web应用的部署与运行过程。虽然描述部分没有提供具体信息,但我们可以从标签...
Java模拟Tomcat Web服务器源码解析 在Java世界中,Tomcat是一款广泛应用的开源Web服务器和Servlet容器。它遵循Java Servlet和JavaServer Pages(JSP)规范,为开发者提供了轻量级、高效的运行环境。本篇文章将深入...
在手写Tomcat时,我们可以模拟一个简单的基于 BIO(阻塞I/O)的连接器。这涉及到监听特定端口,接受连接请求,读取HTTP请求头和正文,然后将其传递给容器进行处理。 2. 容器(Container):Tomcat的容器分为多个...
总结来说,"tomcat-redis依赖jar包"涉及到的是将Tomcat应用服务器与Redis缓存系统整合的过程,包括连接器选择、配置、缓存策略、性能优化、安全措施以及监控与故障排查等多个方面。正确地集成和使用这些jar包,能够...
如果你需要深入理解Tomcat连接器的工作原理,或者想要对其进行定制,你可以编译这个源代码。这通常涉及到下载源码,配置构建环境(如Apache Maven或Ant),然后执行构建命令来生成可部署的二进制文件。 了解Tomcat ...
Tomcat默认使用两种连接器:BIO(Blocking I/O)和NIO(Non-blocking I/O)。在`nginx+tomcat8开始默认NIO方式.png`中,我们可以看到Tomcat 8及以上版本默认采用NIO模式,这种模式相比BIO更能处理大量并发连接,因为...
- 提升了性能,增强了线程池和连接器的管理。 **HttpWatch** HttpWatch是一个集成在浏览器中的工具,用于分析网页加载速度和性能。它提供了详细的HTTP请求和响应信息,帮助开发者识别和优化网页的瓶颈。以下是一些...
优化Tomcat主要是通过调整相关参数和配置,以提高系统的性能和稳定性,例如修改JVM参数、调整连接器的设置、配置缓存大小、启用异步处理等。 Tomcat的安装和配置涉及多个步骤,包括下载合适的Tomcat版本、设置环境...
1. **Coyote**: 这是Tomcat的网络连接器,负责接收和发送HTTP请求/响应。 2. **AprLifecycleListener**: 使用Apache Portable Runtime(APR)库提高性能。 3. **StandardContext**: 负责管理Web应用,加载和卸载应用...
#### 六、Tomcat连接器选择 - **选择合适的连接器**: - **BIO (Blocking I/O)**: 传统的同步阻塞I/O模型。 - **NIO (Non-blocking I/O)**: 支持非阻塞I/O模型,适用于高并发场景。 - **APR (Apache Portable ...
内容包括Tomcat的基础概念、Java JDK的安装、Tomcat的下载、环境变量的配置、Tomcat的启动和验证、Web应用程序的部署、Tomcat的配置文件编辑、虚拟主机的配置、连接器的设置、会话管理、安全性配置、性能监控以及...
- **监控界面**:部署完成后,通过访问LambdaProbe提供的Web界面,可以直观地查看到详细的Tomcat性能数据以及连接器的状态等信息。 使用LambdaProbe可以更细致地了解Tomcat各方面的运行状态,有助于定位潜在的...
这包括设置防火墙规则、配置正确的连接器设置、更新到无漏洞的Tomcat版本等。 安装和运行Apache Tomcat 8.0.46在Linux环境中的步骤大致如下: 1. 解压下载的zip文件:`unzip apache-tomcat-8.0.46.zip` 2. 设置...
5. **连接器(Connector)** Connector是Tomcat接收请求的入口,每个Connector都有自己的监听端口。配置HTTP和AJP Connector,关注`maxThreads`、`minSpareThreads`等属性,以适应不同类型的请求负载。 **JVM参数...
本实例将详细讲解如何使用Nginx作为反向代理和负载均衡器,结合Tomcat9作为Java应用服务器来搭建一个负载均衡系统。Nginx以其高性能、轻量级的特性,常被用作前端服务器,负责将用户请求分发到后端多个Tomcat实例上...
- **连接器设置**:调整连接器的最大线程数、空闲线程数等参数,以适应不同场景的需求。 - **日志级别**:合理设置日志记录级别,避免因日志输出过多而影响性能。 - **缓存机制**:启用静态文件缓存,减少不必要的...