理解tomcat之搭建简易http服务器
来源:互联网 发布:三件套女装秋装淘宝 编辑:程序博客网 时间:2024/05/29 15:36
做过java web的同学都对tomcat非常熟悉。我们在使用tomcat带来的便利的同时,是否想过tomcat是如何工作的呢?tomcat本质是一个http服务器,本篇文章将搭建一个简单的http服务器。
1 Catalina模型
首先我们先了解一下tomcat的大致工作原理。tomcat的核心是servlet容器,我们称它为Catalina(为什么叫这个名字?我也不知道 ̄へ ̄)。模型图如1.1
图1.1
Connector是用来“连接”容器里边的请求的。它的工作是为接收到每一个 HTTP 请求构造一个 request 和 response 对象。然后它把流程传递给容器。容器从连接器接收到 requset 和 response 对象之后调用 servlet 的 service 方法用于响应。谨记,这个描述仅仅是冰山一角而已。这里容器做了相当多事情。例如,在它调用 servlet 的 service 方法之前,它必须加载这个 servlet,验证用户(假如需要的话),更新用户会话等等。以此为思路,我们就开始我们的构造http服务器之旅吧。
2 服务器搭建
首先我们明确一下我们的服务器的功能点。
1. 需要有一个类去接收http请求;
2. 需要一个自定义Request类和Response类,把接收到的请求构造成这两个类;
3. 根据请求的格式来确定处理方式:返回静态资源 or 进入Servlet ?
4. 需要一个Servlet类执行业务逻辑
UML图如下2.1
图2.1
2.1 HttpServer
首先构造HttpServer类
1 public class HttpServer { 2 3 4 private static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; 5 private static boolean shutdown = false; 6 7 public static void main(String[] args) { 8 HttpServer server = new HttpServer(); 9 server.await();10 }11 12 public static void await() {13 ServerSocket serverSocket = null;14 int port = 8080;15 try {16 serverSocket = new ServerSocket(port, 1,17 InetAddress.getByName("127.0.0.1"));18 } catch (IOException e) {19 e.printStackTrace();20 System.exit(1);21 }22 // Loop waiting for a request23 while (!shutdown) {24 Socket socket = null;25 InputStream input = null;26 OutputStream output = null;27 try {28 socket = serverSocket.accept();29 input = socket.getInputStream();30 output = socket.getOutputStream();31 // create Request object and parse32 Request request = new Request(input);33 request.parseUrl();34 // create Response object35 Response response = new Response(output);36 response.setRequest(request);37 38 if (request.getUri().startsWith("/v2/")) {39 ServletProcessor processor = new ServletProcessor();40 processor.process(request, response);41 }42 else {43 StaticResourceProcessor processor =44 new StaticResourceProcessor();45 processor.process(request, response);46 }47 // Close the socket48 socket.close();49 //check if the previous URI is a shutdown command50 shutdown = request.getUri().equals(SHUTDOWN_COMMAND);51 } catch (Exception e) {52 e.printStackTrace();53 System.exit(1);54 }55 }56 }57 }
我们的服务器启动入口放在了HttpServer里面。await()方法负责接收Socket连接,只有当用户输入了代表shutdown的URL时,服务器才会停止运行。Request类提供了解析请求的功能,根据请求的url,来决定是返回静态资源,或者进入对应的servlet类执行service逻辑。
这里我们需要关注一下ServerSocket这个类的用法。Socket 类代表一个客户端套接字,即任何时候你想连接到一个远程服务器应用的时候,你都会第一时间想到这个类。而ServerSocket 和 Socket 不同,服务器套接字的角色是等待来自客户端的连接请求。一旦服 务器套接字获得一个连接请求,它创建一个 Socket 实例来与客户端进行通信。 ServletSocket套接字的其中一个构造函数为
public ServerSocket(int port, int backLog, InetAddress bindingAddress);
port代表端口号,backLog代表这个套接字可支持的最大连接数量,bindingAddress代表服务器绑定的地址。一旦你有一个 ServerSocket 实例,你可以通过调用 ServerSocket 类的 accept 方法j。这个监听当前地址的当前端口上的请求,方法只会在有连接请求时才会返回,并且返回值是一个 Socket 类的实例。
2.2 Request Response
servlet 的 service 方法从 servlet 容器中接收一个 javax.servlet.ServletRequest 实例 和一个 javax.servlet.ServletResponse 实例。这就是说对于每一个 HTTP 请求,servlet 容器 必须构造一个 ServletRequest 对象和一个 ServletResponse 对象并把它们传递给正在服务的 servlet 的 service 方法。
1 public class Request implements ServletRequest { 2 3 private InputStream input; 4 private String uri; 5 6 public Request(InputStream input) { 7 this.input = input; 8 } 9 10 public String getUri(){ 11 return uri; 12 } 13 14 public void parseUrl() { 15 StringBuffer request = new StringBuffer(2048); 16 int i; 17 byte[] buffer = new byte[2048]; 18 19 try { 20 i = input.read(buffer); 21 } catch (IOException e) { 22 e.printStackTrace(); 23 i = -1; 24 } 25 26 for (int j = 0; j < i; j++) { 27 request.append((char) buffer[j]); 28 } 29 30 System.out.print(request.toString()); 31 uri = parseUri(request.toString()); 32 } 33 34 private static String parseUri(String requestString) { 35 int index1, index2; 36 index1 = requestString.indexOf(' '); 37 if (index1 != -1) { 38 index2 = requestString.indexOf(' ', index1 + 1); 39 if (index2 > index1) 40 return requestString.substring(index1 + 1, index2); 41 } 42 return null; 43 } 44 45 @Override 46 public Object getAttribute(String name) { 47 return null; 48 } 49 50 @Override 51 public Enumeration getAttributeNames() { 52 return null; 53 } 54 55 @Override 56 public String getCharacterEncoding() { 57 return null; 58 } 59 60 @Override 61 public void setCharacterEncoding(String env) throws UnsupportedEncodingException { 62 63 } 64 65 @Override 66 public int getContentLength() { 67 return 0; 68 } 69 70 @Override 71 public String getContentType() { 72 return null; 73 } 74 75 @Override 76 public ServletInputStream getInputStream() throws IOException { 77 return null; 78 } 79 80 @Override 81 public String getParameter(String name) { 82 return null; 83 } 84 85 @Override 86 public Enumeration getParameterNames() { 87 return null; 88 } 89 90 @Override 91 public String[] getParameterValues(String name) { 92 return new String[0]; 93 } 94 95 @Override 96 public Map getParameterMap() { 97 return null; 98 } 99 100 @Override101 public String getProtocol() {102 return null;103 }104 105 @Override106 public String getScheme() {107 return null;108 }109 110 @Override111 public String getServerName() {112 return null;113 }114 115 @Override116 public int getServerPort() {117 return 0;118 }119 120 @Override121 public BufferedReader getReader() throws IOException {122 return null;123 }124 125 @Override126 public String getRemoteAddr() {127 return null;128 }129 130 @Override131 public String getRemoteHost() {132 return null;133 }134 135 @Override136 public void setAttribute(String name, Object o) {137 138 }139 140 @Override141 public void removeAttribute(String name) {142 143 }144 145 @Override146 public Locale getLocale() {147 return null;148 }149 150 @Override151 public Enumeration getLocales() {152 return null;153 }154 155 @Override156 public boolean isSecure() {157 return false;158 }159 160 @Override161 public RequestDispatcher getRequestDispatcher(String path) {162 return null;163 }164 165 @Override166 public String getRealPath(String path) {167 return null;168 }169 170 @Override171 public int getRemotePort() {172 return 0;173 }174 175 @Override176 public String getLocalName() {177 return null;178 }179 180 @Override181 public String getLocalAddr() {182 return null;183 }184 185 @Override186 public int getLocalPort() {187 return 0;188 }189 }
Request类代表一个 request 对象并被传递给 servlet 的 service 方法。就本身而言,它必须实现 javax.servlet.ServletRequest 接口。这个类必须提供这个接口所有方法的实现。不过,我们想要让它非常简单并且仅仅提供实现其中一些方法,比如解析url的功能。在Request初始化时初始化成员变量inputStream,并且用parseUrl()方法创建了一个字节数组来读入输入流,并转化为成一个StringBuffer对象,进而解析url。
1 public class Response implements ServletResponse { 2 3 private static final int BUFFER_SIZE = 1024; 4 Request request; 5 OutputStream output; 6 PrintWriter writer; 7 8 public Response(OutputStream output) { 9 this.output = output; 10 } 11 12 public void setRequest(Request request) { 13 this.request = request; 14 } 15 16 public void sendStaticResource() throws IOException { 17 byte[] bytes = new byte[BUFFER_SIZE]; 18 FileInputStream fis = null; 19 try { 20 File file = new File(HttpServer.WEB_ROOT, request.getUri()); 21 if (file.exists()) { 22 fis = new FileInputStream(file); 23 int ch = fis.read(bytes, 0, BUFFER_SIZE); 24 while (ch != -1) { 25 output.write(bytes, 0, ch); 26 ch = fis.read(bytes, 0, BUFFER_SIZE); 27 } 28 } else { 29 String errorMessage = "HTTP/1.1 404 File Not Found\r\n" + 30 "Content-Type: text/html\r\n" + "Content-Length: 23\r\n" + "\r\n" + 31 "<h1>File Not Found</h1>"; 32 output.write(errorMessage.getBytes()); 33 } 34 } catch (Exception e) { 35 System.out.println(e.toString()); 36 } finally { 37 if (fis != null) 38 fis.close(); 39 } 40 } 41 42 @Override 43 public String getCharacterEncoding() { 44 return null; 45 } 46 47 @Override 48 public String getContentType() { 49 return null; 50 } 51 52 @Override 53 public ServletOutputStream getOutputStream() throws IOException { 54 return null; 55 } 56 57 @Override 58 public PrintWriter getWriter() throws IOException { 59 writer = new PrintWriter(output, true); 60 return writer; 61 62 } 63 64 @Override 65 public void setCharacterEncoding(String charset) { 66 67 } 68 69 @Override 70 public void setContentLength(int len) { 71 72 } 73 74 @Override 75 public void setContentType(String type) { 76 77 } 78 79 @Override 80 public void setBufferSize(int size) { 81 82 } 83 84 @Override 85 public int getBufferSize() { 86 return 0; 87 } 88 89 @Override 90 public void flushBuffer() throws IOException { 91 92 } 93 94 @Override 95 public void resetBuffer() { 96 97 } 98 99 @Override100 public boolean isCommitted() {101 return false;102 }103 104 @Override105 public void reset() {106 107 }108 109 @Override110 public void setLocale(Locale loc) {111 112 }113 114 @Override115 public Locale getLocale() {116 return null;117 }118 }
Response类则提供了发送静态资源的功能。sendStaticResource()方法根据request内解析过的url,在本地寻找指定文件。如果找得到,把文件内容读出到浏览器,如果找不到,那么返回404的错误码。
2.3 PrimitiveServlet类
1 public class PrimitiveServlet implements Servlet { 2 3 @Override 4 public void init(ServletConfig config) throws ServletException { 5 System.out.println("init"); 6 } 7 8 @Override 9 public ServletConfig getServletConfig() {10 return null;11 }12 13 @Override14 public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {15 System.out.println("from service");16 PrintWriter out = res.getWriter();17 out.println("Hello. Roses are red.");18 out.print("Violets are blue.");19 }20 21 @Override22 public String getServletInfo() {23 return "this is v2 info";24 }25 26 @Override27 public void destroy() {28 System.out.println("destroy");29 }30 }
PrimitiveServlet实现了标准的Servlet接口。我们简单的实现了Servlet生命周期的其他方法,并在service()方法我们做了最简单的向浏览器吐文字的操作。
2.4 ServletProcessor 和 StaticResourceProcessor
1 public class ServletProcessor { 2 3 public void process(Request request, Response response) { 4 String uri = request.getUri(); 5 String servletName = uri.substring(uri.lastIndexOf("/") + 1); 6 Class myClass = null; 7 try { 8 myClass = Class.forName("tomcat.v2." + servletName); 9 } catch (ClassNotFoundException e) {10 System.out.println(e.toString());11 }12 Servlet servlet = null;13 try {14 servlet = (Servlet) myClass.newInstance();15 servlet.service((ServletRequest) request, (ServletResponse) response);16 } catch (Exception e) {17 e.printStackTrace();18 } catch (Throwable e) {19 e.printStackTrace();20 }21 }22 }
ServletProcessor是处理serlvet请求的类。它的作用在于,根据请求的路径实例化对应的Servlet,并且执行该Servlet的service()方法。
1 public class StaticResourceProcessor { 2 3 public void process(Request request, Response response){ 4 try{ 5 response.sendStaticResource(); 6 }catch (IOException e){ 7 e.printStackTrace(); 8 } 9 }10 }
StaticResourceProcessor则简单的调用response的sendStaticResource()方法来返回静态资源。
最后我们还需要建一个辅助类Constants指定静态资源的存放路径
1 public class Constants {2 public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";3 }
3 启动服务器
启动main函数来启动我们的http服务器。在浏览器输入http://localhost:8080/test,得到的结果如图3.1
图3.1
这个url访问的是我的电脑中的一个文件test,它的存储路径为 /Users/wangyu/Documents/workspace/Tomcat/webroot/test,即当前项目的webroot目录下。
我们还可以输入一个servlet地址查看效果。在浏览器输入http://localhost:8080/v2/PrimitiveServlet,得到的结果如图3.2
图3.2
这和我们的PrimitiveServlet的输出是一致的。
4 结语
当然,tomcat的功能比我们的简易http服务器强大的多。但是通过这个最简单的http服务器,是否让你对http服务器有更深的一点了解了呢?
参考资料:
1 深入剖析Tomcat
转载自: http://www.cnblogs.com/maypattis/p/5549613.html
- 理解tomcat之搭建简易http服务器
- 理解tomcat之搭建简易http服务器
- 简易搭建http服务器
- http服务-搭建简易的http服务器
- Linux 搭建tomcat - MySql服务器(简易)
- 用nodejs搭建简易的HTTP服务器
- Node 搭建简易 HTTP 静态资源服务器
- 利用Python搭建简易的Http服务器
- J2EE进阶之tomcat服务器搭建,HTTP协议 八
- 使用Smart HTTP和Gitweb搭建简易个人git服务器
- Mac Apache服务器搭建、Tomcat(http)服务器搭建、Tomcat(https)服务器搭建
- tomcat搭建简易网站
- 简易http服务器
- 简易HTTP服务器
- HTTP服务器简易版
- android网络编程 -- HTTP通信(01)TOMCAT HTTP服务器搭建
- 搭建简易Samba服务器
- wap服务器简易搭建
- angular设置路由实现无刷新跳转
- android 网络框架之okhttp3
- B1019. 数字黑洞 (20')
- Centos一键部署openstack RDO packstack
- 虚幻4相关的一些资料
- 理解tomcat之搭建简易http服务器
- 启动storm遇到的while scanning a simple key in 'reader', line 54, column 2: nimbus.host:"node1"
- HEVC代码学习12:xCheckRDCostInter函数
- Laravel5.4 发送mail邮件
- vim关于窗口分割的命令:
- 手机的web页面调用相机拍照上传
- NAT和代理服务器的原理及应用
- 面向过程和面向对象的理解
- Solr -- 查询语法/参数