千家信息网

编写Tomcat的方法是什么

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,本篇内容介绍了"编写Tomcat的方法是什么"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Apach
千家信息网最后更新 2026年11月03日编写Tomcat的方法是什么

本篇内容介绍了"编写Tomcat的方法是什么"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

Apache Tomcat 是Java Servlet, JavaServer Pages (JSP),Java表达式语言和Java的WebSocket技术的一个开源实现 ,通常我们将Tomcat称为Web容器或者Servlet容器 。

基本结构

tomcat架构图

我们可以把上面这张架构图做简化,简化后为:

什么是http协议

Http是一种网络应用层协议,规定了浏览器与web服务器之间如何通信以及数据包的结构。

通信大致可以分为四步:

  1. 先建立连接。

  2. 发送请求数据包。

  3. 发送响应数据包。

  4. 关闭连接。

优点

web服务器可以利用有限的连接为尽可能多的客户请求服务。

tomcat中Servlet的运作方式

  1. 在浏览器地址栏输入http://ip:port/servlet-day01/hello

  2. 浏览器依据IP、port建立连接(即与web服务器之间建立网络连接)。

  3. 浏览器需要将相关数据打包(即按照http协议要求,制作一个 请求数据包,包含了一些数据,比如请求资源路径),并且将请求 数据包发送出去。

  4. web服务器会将请求数据包中数据解析出来,并且将这些数据添加 到request对象,同时,还会创建一个response对象。

  5. web服务器创建Servlet对象,然后调用该对象的service方法(会将request和response作为参数)。注:在service方法里面,通过使用request获得请求相关的数据, 比如请求参数值,然后将处理结果写到response。

  6. web服务器将response中的数据取出来,制作响应数据包,然后发送给浏览器。

  7. 浏览器解析响应数据包,然后展现。

可以总结唯一张图:

什么是Servlet呢?

Servlet是JavaEE规范的一种,主要是为了扩展Java作为Web服务的功能,统一接口。由其他内部厂商如tomcat,jetty内部实现web的功能。如一个http请求到来:容器将请求封装为servlet中的HttpServletRequest对象,调用init(),service()等方法输出response,由容器包装为httpresponse返回给客户端的过程。

什么是Servlet规范?

  1. 从 Jar 包上来说,Servlet 规范就是两个 Jar 文件。servlet-api.jar 和 jsp-api.jar,Jsp 也是一种 Servlet。

  2. 从package上来说,就是 javax.servlet 和 javax.servlet.http 两个包。

  3. 从接口来说,就是规范了 Servlet 接口、Filter 接口、Listener 接口、ServletRequest 接口、ServletResponse 接口等。类图如下:

第一版:Socket版

使用Socket编程,实现简单的客户端和服务端的聊天。

服务端代码如下:

package com.tian.v1;  import java.io.*; import java.net.*;   public class Server {      public static String readline = null;     public static String inTemp = null;     public static String turnLine = "\n";     public static final String client = "客户端:";     public static final String server = "服务端:";     public static final int PORT = 8090;      public static void main(String[] args) throws Exception {         ServerSocket serverSocket = new ServerSocket(PORT);         System.out.println("服务端已经准备好了");         Socket socket = serverSocket.accept();          BufferedReader systemIn = new BufferedReader(new InputStreamReader(System.in));         BufferedReader socketIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));         PrintWriter socketOut = new PrintWriter(socket.getOutputStream());         while (true) {             inTemp = socketIn.readLine();             if (inTemp != null &&inTemp.contains("over")) {                 systemIn.close();                 socketIn.close();                 socketOut.close();                 socket.close();                 serverSocket.close();             }             System.out.println(client + inTemp);             System.out.print(server);              readline = systemIn.readLine();              socketOut.println(readline);             socketOut.flush();         }     } }

客户端代码如下:

package com.tian.v1;  import java.io.*; import java.net.*;  public class Client {      public static void main(String[] args) throws Exception {         String readline;         String inTemp;         final String client = "客户端说:";         final String server = "服务端回复:";          int port = 8090;         byte[] ipAddressTemp = {127, 0, 0, 1};         InetAddress ipAddress = InetAddress.getByAddress(ipAddressTemp);          //首先直接创建socket,端口号1~1023为系统保存,一般设在1023之外         Socket socket = new Socket(ipAddress, port);          BufferedReader systemIn = new BufferedReader(new InputStreamReader(System.in));         BufferedReader socketIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));         PrintWriter socketOut = new PrintWriter(socket.getOutputStream());         while (true) {             System.out.print(client);             readline = systemIn.readLine();              socketOut.println(readline);             socketOut.flush();             //处理             inTemp = socketIn.readLine();             if (inTemp != null && inTemp.contains("over")) {                 systemIn.close();                 socketIn.close();                 socketOut.close();                 socket.close();             }             System.out.println(server + inTemp);         }     } }

过程如下:

第二版:我们直接请求http://localhost:8090

实现代码如下:

package com.tian.v2;  import java.io.IOException; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket;  public class MyTomcat {     /**      * 设定启动和监听端口      */     private int port = 8090;      /**      * 启动函数      *      * @throws IOException      */     public void start() throws IOException {         System.out.println("my tomcat starting...");         String responseData = "6666666";         ServerSocket socket = new ServerSocket(port);         while (true) {             Socket accept = socket.accept();             OutputStream outputStream = accept.getOutputStream();             String responseText = HttpProtocolUtil.getHttpHeader200(responseData.length()) + responseData;             outputStream.write(responseText.getBytes());             accept.close();         }     }      /**      * 启动入口      */     public static void main(String[] args) throws IOException {         MyTomcat tomcat = new MyTomcat();         tomcat.start();     } }

再写一个工具类,内容如下;

ackage com.tian.v2;  public class HttpProtocolUtil {      /**      * 200 状态码,头信息      *      * @param contentLength 响应信息长度      * @return 200 header info      */     public static String getHttpHeader200(long contentLength) {         return "HTTP/1.1 200 OK \n" + "Content-Type: text/html \n"                 + "Content-Length: " + contentLength + " \n" + "\r\n";     }      /**      * 为响应码 404 提供请求头信息(此处也包含了数据内容)      *      * @return 404 header info      */     public static String getHttpHeader404() {         String str404 = "

404 not found

"; return "HTTP/1.1 404 NOT Found \n" + "Content-Type: text/html \n" + "Content-Length: " + str404.getBytes().length + " \n" + "\r\n" + str404; } }

启动main方法:

使用IDEA访问:

在浏览器访问:

自此,我们的第二版本搞定。下面继续第三个版本;

第三版:封装请求信息和响应信息

一个http协议的请求包含三部分:

  • 方法 URI 协议/版本

  • 请求的头部

  • 主体内容

比如

POST /index.html HTTP/1.1 Accept: text/plain; text/html Accept-Language: en-gb Connection: Keep-Alive Host: localhost User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98) Content-Length: 33 Content-Type: application/x-www-form-urlencoded Accept-Encoding: gzip, deflate  lastName=tian&firstName=JohnTian

简单的解释

  • 数据的第一行包括:方法、URI、协议和版本。在这个例子里,方法为POST,URI为/index.html,协议为HTTP/1.1,协议版本号为1.1。他们之间通过空格来分离。

  • 请求头部从第二行开始,使用英文冒号(:)来分离键和值。

  • 请求头部和主体内容之间通过空行来分离,例子中的请求体为表单数据。

类似于http协议的请求,响应也包含三个部分。

  • 协议 状态 状态描述

  • 响应的头部

  • 主体内容

比如:

HTTP/1.1 200 OK Server: Microsoft-IIS/4.0 Date: Mon, 5 Jan 2004 13:13:33 GMT Content-Type: text/html Last-Modified: Mon, 5 Jan 2004 13:13:12 GMT Content-Length: 112    HTTP Response Example   Welcome to Brainy Software  

简单解释

  • 第一行,HTTP/1.1 200 OK表示协议、状态和状态描述。

  • 之后表示响应头部。

  • 响应头部和主体内容之间使用空行来分离。

代码实现

创建一个工具类,用来获取静态资源信息。

package com.tian.v3;  import com.tian.v2.HttpProtocolUtil;  import java.io.IOException; import java.io.InputStream; import java.io.OutputStream;  /**  * 提取了一些共用类和函数  */ public class ResourceUtil {      /**      * 根据请求 url 获取完整绝对路径      */     public static String getPath(String url) {         String path = ResourceUtil.class.getResource("/").getPath();         return path.replaceAll("\\\\", "/") + url;     }      /**      * 输出静态资源信息      */     public static void outputResource(InputStream input, OutputStream output) throws IOException {         int count = 0;         while (count == 0) {             count = input.available();         }         int resourceSize = count;         output.write(HttpProtocolUtil.getHttpHeader200(resourceSize).getBytes());         long written = 0;         int byteSize = 1024;         byte[] bytes = new byte[byteSize];         while (written < resourceSize) {             if (written + byteSize > resourceSize) {                 byteSize = (int) (resourceSize - written);                 bytes = new byte[byteSize];             }             input.read(bytes);             output.write(bytes);             output.flush();             written += byteSize;         }     } }

另外HttpProtocolUtil照样用第二版本中。

再创建Request类,用来解析并存放请求相关参数。

package com.tian.v3;  import java.io.IOException; import java.io.InputStream;  public class Request {     /**      * 请求方式, eg: GET、POST      */     private String method;      /**      * 请求路径,eg: /index.html      */     private String url;      /**      * 请求信息输入流 
* 示例 *
      *  GET / HTTP/1.1      *  Host: localhost      *  Connection: keep-alive      *  Pragma: no-cache      *  Cache-Control: no-cache      *  Upgrade-Insecure-Requests: 1      *  User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36      * 
*/ private InputStream inputStream; public Request() { } public Request(InputStream inputStream) throws IOException { this.inputStream = inputStream; int count = 0; while (count == 0) { count = inputStream.available(); } byte[] bytes = new byte[count]; inputStream.read(bytes); // requestString 参考:this.inputStream 示例 String requestString = new String(bytes); // 按换行分隔 String[] requestStringArray = requestString.split("\\n"); // 读取第一行数据,即:GET / HTTP/1.1 String firstLine = requestStringArray[0]; // 遍历第一行数据按空格分隔 String[] firstLineArray = firstLine.split(" "); this.method = firstLineArray[0]; this.url = firstLineArray[1]; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public InputStream getInputStream() { return inputStream; } public void setInputStream(InputStream inputStream) { this.inputStream = inputStream; } }

把第二版的MyTomcat进行小小调整:

package com.tian.v3;  import java.io.IOException; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket;  public class MyTomcat {      private static final int PORT = 8090;     public void start() throws IOException {         System.out.println("my tomcat starting...");         ServerSocket socket = new ServerSocket(PORT);         while (true) {             Socket accept = socket.accept();             OutputStream outputStream = accept.getOutputStream();             // 分别封装 Request 和 Response             Request request = new Request(accept.getInputStream());             Response response = new Response(outputStream);             // 根据 request 中的 url,输出             response.outputHtml(request.getUrl());             accept.close();         }     }         public static void main(String[] args) throws IOException {         MyTomcat tomcat = new MyTomcat();         tomcat.start();     } }

然后再创建一个index.html,内容很简单:

            hello world   

you already succeed!

这一需要注意,index.html文件的存放路径不放错了,视本地路径来定哈,放在classes文件夹下的。你可以debug试试,看看你应该放在那个目录下。

启动MyTomcat。

访问http://localhost:8090/index.html

自此,我们针对于Http请求参数和相应参数做了一个简单的解析以及封装。

尽管其中还有很多问题,但是字少看起来有那点像样了。我们继续第四版,

第四版:实现动态请求资源

用过servlet的同学都知道,Servlet中有三个很重要的方法init、destroy 、service 。其中还记得我们自己写LoginServlet的时候,还会重写HttpServlet中的doGet()和doPost()方法。下面们就自己来搞一个:

Servlet类代码如下:

public interface Servlet {     void init() throws Exception;     void destroy() throws Exception;     void service(Request request, Response response) throws Exception; }

然后再写一个HttpServlet来实现Servlet。

代码实现如下:

package com.tian.v4;  public abstract class HttpServlet implements Servlet {     @Override     public void init() throws Exception {      }      @Override     public void destroy() throws Exception {      }      @Override     public void service(Request request, Response response) throws Exception {         String method = request.getMethod();         if ("GET".equalsIgnoreCase(method)) {             doGet(request, response);         } else {             doPost(request, response);         }     }     public abstract void doGet(Request request, Response response) throws Exception;      public abstract void doPost(Request request, Response response) throws Exception; }

下面我们就来写一个自己的Servlet,比如LoginServlet。

package com.tian.v4;  public class LoginServlet  extends HttpServlet {      @Override     public void doGet(Request request, Response response) throws Exception {         String repText = "

LoginServlet by GET method

"; response.output(HttpProtocolUtil.getHttpHeader200(repText.length()) + repText); } @Override public void doPost(Request request, Response response) throws Exception { String repText = "

LoginServlet by POST method

"; response.output(HttpProtocolUtil.getHttpHeader200(repText.length()) + repText); } @Override public void init() throws Exception { } @Override public void destroy() throws Exception { } }

大家是否还记得,我们在学习Servlet的时候,在resources目录下面有个web.xml。我们这个版本也把这个xml文件给引入。

               login         com.tian.v4.LoginServlet                    login         /login      

既然引入了xml文件,那我们就需要去读取这个xml文件,并解析器内容。所以这里我们需要引入两个jar包。

              dom4j         dom4j         1.6.1                   jaxen         jaxen         1.1.6      

万事俱备,只欠东风了。这时候我们来吧MyTomcat这个类做一些调整即可。

下面有个很重要的initServlet()方法,刚刚是对应下面这张图中的List servlets,但是我们代码里使用的是Map来存储Servlet的,意思就那么个意思,把Servlet放在集合里。

这也就是为什么大家都把Tomcat叫做Servlet容器的原因,其实真正的容器还是java集合。

package com.tian.v4;  import com.tian.v3.RequestV3; import com.tian.v3.ResponseV3; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader;  import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.List; import java.util.Map;  public class MyTomcat {     /**      * 设定启动和监听端口      */     private static final int PORT = 8090;     /**      * 存放 Servlet信息,url: Servlet 实例      */     private Map servletMap = new HashMap<>();       public void start() throws Exception {          System.out.println("my tomcat starting...");         initServlet();         ServerSocket socket = new ServerSocket(PORT);         while (true) {             Socket accept = socket.accept();             OutputStream outputStream = accept.getOutputStream();             // 分别封装 RequestV3 和 ResponseV3             RequestV4 requestV3 = new RequestV4(accept.getInputStream());             ResponseV4 responseV3 = new ResponseV4(outputStream);             // 根据 url 来获取 Servlet             HttpServlet httpServlet = servletMap.get(requestV3.getUrl());             // 如果 Servlet 为空,说明是静态资源,不为空即为动态资源,需要执行 Servlet 里的方法             if (httpServlet == null) {                 responseV3.outputHtml(requestV3.getUrl());             } else {                 httpServlet.service(requestV3, responseV3);             }             accept.close();         }     }           public static void main(String[] args) throws Exception {         MyTomcat tomcat = new MyTomcat();         tomcat.start();     }       /**      * 解析web.xml文件,把url和servlet解析出来,      * 并保存到一个java集合里(Map)      */     public void initServlet() throws Exception {         InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");         SAXReader saxReader = new SAXReader();         Document document = saxReader.read(resourceAsStream);         Element rootElement = document.getRootElement();         List list = rootElement.selectNodes("//servlet");         for (Element element : list) {             // show             Element servletnameElement = (Element) element.selectSingleNode("servlet-name");             String servletName = servletnameElement.getStringValue();             // server.ShowServlet             Element servletclassElement = (Element) element.selectSingleNode("servlet-class");             String servletClass = servletclassElement.getStringValue();              // 根据 servlet-name 的值找到 url-pattern             Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");             // /show             String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();             servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).getDeclaredConstructor().newInstance());         }     } }

启动,再次访问http://localhost:8090/index.html

同时,我们可以访问http://localhost:8090/login图片

到此,第四个版本也搞定了。

但是前面四个版本都有一个共同的问题,全部使用的是BIO。

BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

所以,大家在网上看到的手写tomcat的,也有使用线程池来做的,这里希望大家能get到为什么使用线程池来实现。另外,其实在tomcat高版本中已经没有使用BIO了。

而 HTTP/1.1默认使用的就是NIO了。

但这个只是通信方式,重点是我们要理解和掌握tomcat的整体实现。

"编写Tomcat的方法是什么"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

数据 服务 方法 内容 版本 信息 服务器 代码 客户 接口 文件 浏览器 浏览 头部 容器 线程 资源 之间 参数 客户端 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技产业 中国互联网科技股 广州宏创互联网科技 天下捷融互联网科技 苏州大垚互联网科技 火传媒上海互联网科技 长亮科技 互联网金融 滨湖区互联网智能科技产品供应 欧洲十大科技互联网公司 银川新华互联网科技职业技术培训学校图片 科技与互联网哪个更好 互联网媒体平台算高科技产业 深圳市优智互联网科技有限公司 互联网科技和信息 青海科技产业互联网发展 小米互联网科技园入驻 宇信科技在杭州互联网排第几 工业互联网科技成果转化 北京品多多互联网科技有限公司 重庆科技产业互联网有哪些 华服互联网科技怎么样 互联网科技卖菜 中国互联网科技发展 中国科技互联网公司收入 互联网科技寓意 注册互联网科技公司经营范围大全 互联网科技人工智能实验室 2021世界互联网领先科技成果发布 好易买互联网科技 上海旺乐尼互联网科技有限公司
0