深入学习Tomcat----自己动手写服务器(附服务器源码)

2023-05-16

相信大多Web开发者对Tomcat是非常熟悉的,众所周知Tomcat是一款非常好用的开源Servlet容器,您一定对这个最流行的Servlet容器充满好奇,虽然它并不像一个黑盒子那样让人无法触摸但是Tomcat的源码的确让人看起来头疼。笔者就在这里和大家共同分析一个简单的Web服务器是如何工作的源码下载地址

Web服务器

Web服务器是一个复杂的系统,一个Web服务器要为一个Servlet的请求提供服务,需要做三件事:

1、创建一个request对象并填充那些有可能被所引用的Servlet使用的信息,如参数、头部、cookies、查询字符串等等。一个request对象是javax.servlet.ServletRequestjavax.servlet.http.ServletRequest接口的一个实例

2、创建一个response对象,所引用的servlet使用它来给客户端发送响应。一个response对象是javax.servlet.ServletRequestjavax.servlet.http.ServletRequest接口的一个实例。

3、调用servletservice方法,并传入requestresponse对象。这里servlet会从request对象取值,给response写值。

在正式展示代码之前还需要了解一些必须额HTTP的知识(如果您对此非常熟悉您可以直接看下面分析代码)

HTTP

HTTP的定义不知道的童鞋可以自己去度娘,这里主要要说的就是HTTP协议的格式

HTTP请求包括三部分

1、方法、统一资源标识符(URI)、协议/版本

2、请求的头部

3、主题内容

下面是一个HTTP请求的例子

POST /examples/default.jsp 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=Franks&firstName=Michael  

第一行表明这是POST请求方法,/examples/default.jspURIHTTP/1.1是协议以及版本。其中URI指明了一个互联网资源,这里通常是相对服务器根目录解释的,也就是说这个HTTP请求就是告诉服务器我需要这个文件目录如下:根目录/ examples/default.jsp

最后一行是HTTP的主题内容,Servlet会处理请求的主题内容,然后返回给客户端HTTP响应。

类似于HTTP请求,一个HTTP响应也包括上面三个部分。

1、方法、统一资源标识符(URI)、协议/版本

2、响应的头部

3、主题内容

下面是一个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 
 
<html> 
<head> 
<title>HTTP Response Example</title> 
</head> 
<body> 
Welcome to Brainy Software 
</body> 
</html>

第一行告诉协议版本,以及请求成功(200表示成功)

响应头部和请求头部一样,一些有用的信息。响应的主体就是响应本身HTML内容。

好了基本知识介绍完毕,下面开始解释代码

部分相关代码

import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.File;

public class HttpServer {

	public static final String WEB_ROOT = System.getProperty("user.dir")
			+ File.separator + "webroot";

	private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";

	private boolean shutdown = false;

	public static void main(String[] args) {
		HttpServer server = new HttpServer();
		server.await();
	}

	public void await() {
		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 (!shutdown) {
			Socket socket = null;
			InputStream input = null;
			OutputStream output = null;
			try {
				socket = serverSocket.accept();
				input = socket.getInputStream();
				output = socket.getOutputStream();

				Request request = new Request(input);
				request.parse();

				Response response = new Response(output);
				response.setRequest(request);
				response.sendStaticResource();

				socket.close();

				shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
			} catch (Exception e) {
				e.printStackTrace();
				continue;
			}
		}
	}
}

HttpServer类代表一个web服务器。首先提供一个WEB_ROOT所在的目录和它下面所有的子目录下静态资源。其次定义了一个中止服务的命令,也就是说当得到的请求后面跟/shutdown的时候停止服务,默认是把服务设置为开启。下面就是进入main函数了,首先实例化一个HttpServer类,然后就是通过await方法等待客户端发来的请求。如果客户端输入的URL不是http://localhost:8080/SHUTDOWN则表示不停止服务器,然后就是继续执行await方法中的内容,在await方法中最重要的就是定义两个对象,一个是request一个是response,下面就来说说RequestResponse类。

import java.io.InputStream;
import java.io.IOException;

public class Request {

	private InputStream input;
	private String uri;

	public Request(InputStream input) {
		this.input = input;
	}

	public void parse() {

		StringBuffer request = new StringBuffer(2048);
		int i;
		byte[] buffer = new byte[2048];
		try {
			i = input.read(buffer);
		} catch (IOException e) {
			e.printStackTrace();
			i = -1;
		}
		for (int j = 0; j < i; j++) {
			request.append((char) buffer[j]);
		}
		System.out.print(request.toString());
		uri = parseUri(request.toString());
	}

	private String parseUri(String requestString) {
		int index1, index2;
		index1 = requestString.indexOf(' ');
		if (index1 != -1) {
			index2 = requestString.indexOf(' ', index1 + 1);
			if (index2 > index1)
				return requestString.substring(index1 + 1, index2);
		}
		return null;
	}

	public String getUri() {
		return uri;
	}

}

首先调用InputStream对象中的read方法获取HTTP请求的原始数据,然后在parseUri方法中获得uri也就是要请求的静态资源。说白了Request类的主要作用就是告诉服务器用户要的是什么也就是在http://localhost:8080后面出现的东西。

import java.io.OutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.File;

public class Response {

	private static final int BUFFER_SIZE = 1024;
	Request request;
	OutputStream output;

	public Response(OutputStream output) {
		this.output = output;
	}

	public void setRequest(Request request) {
		this.request = request;
	}

	public void sendStaticResource() throws IOException {
		byte[] bytes = new byte[BUFFER_SIZE];
		FileInputStream fis = null;
		try {
			File file = new File(HttpServer.WEB_ROOT, request.getUri());
			if (file.exists()) {
				fis = new FileInputStream(file);
				int ch = fis.read(bytes, 0, BUFFER_SIZE);
				while (ch != -1) {
					output.write(bytes, 0, ch);
					ch = fis.read(bytes, 0, BUFFER_SIZE);
				}
			} else {
				
				String errorMessage = "HTTP/1.1 404 File Not Found\r\n"
						+ "Content-Type: text/html\r\n"
						+ "Content-Length: 23\r\n" + "\r\n"
						+ "<h1>File Not Found</h1>";
				output.write(errorMessage.getBytes());
			}
		} catch (Exception e) {
			
			System.out.println(e.toString());
		} finally {
			if (fis != null)
				fis.close();
		}
	}
}

Response类代表一个HTTP响应。首先Response接收一个OutputStream对象,然后通过sendStaticResource方法对接收的Request进行处理,整个处理过程就是根据请求在服务器端进行寻找对应静态资源的过程。找到所需要的资源后发送给客户端然后让客户端显示出来。

运行程序

运行上面的HttpServer类,然后在浏览器的地址栏中键入下面的地址:http:localhost:8080/index.jsp,然后你会在浏览器中看到index.jsp页面。

在控制台可以看到类似于下面的HTTP请求

GET /index.jsp HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.75 Safari/535.7 360EE
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3


小结

上面自己动手写的这个所谓的服务器仅仅有三个类组成,从功能上来说他只能显示一些静态的资源,并不是全部功能。一个优秀的服务器还有很多细节要做,但是出于学习的目的大家现在有这些了解就足够了,后面还会有对服务器的详细介绍,敬请期待。

参考资料《How Tomcat Works》

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入学习Tomcat----自己动手写服务器(附服务器源码) 的相关文章

  • 我们必须将 .class 文件放在 Tomcat 目录中的位置

    我必须把我的 class文件在 Tomcat 目录中 在我的 Java Complete Reference 书中 他们告诉将其放入C Program Files Apache Tomcat 4 0 webapps examples WEB
  • 升级到 Tomcat 8 时出现 ClassNotFoundException

    我最近将 NetBeans IDE 从 v7 3 升级到 v8 突然我的应用程序在连接到数据库时在服务器启动时抛出异常 这两个版本的 IDE 之间的唯一区别是后者使用 Tomcat 8 异常日志 javax naming NamingExc
  • 带有 Spring 的 Tomcat

    我有一个在 Tomcat 中托管的 3 层应用程序 Web 服务和 DAO 层 如何整合Tomcat和Spring 我需要利用Spring的依赖注入 事务管理等 我只能想到实例化 ClassPathXmlApplicationContext
  • 更改 Spring Web 应用程序的默认会话超时

    我必须测试一个由 spring 和 jsp 编写的 Web 应用程序 应用程序的默认会话超时为 30 分钟 我想减少会话超时 为此 我改变了web xml文件输入tomcatInstallationLocation conf 但这不起作用
  • 泽西宁静例外

    我面对的是泽西岛 2 7 这是我的服务 package edu srv rest import javax ws rs GET import javax ws rs POST import javax ws rs Path import j
  • 如何在 Spring Mvc 项目中设置上下文根

    我在 Tomcat 服务器中使用 Spring MVC 项目 每次运行应用程序时 服务器上下文根都会更改 如何设置固定上下文根 我的项目名称是 DemoApplication 首先部署此上下文根 路径是 http localhost 808
  • XAMPP 上的 Tomcat 无法针对 JAVA_HOME 启动

    我已经阅读并尝试了很多关于 SO 的帖子 但没有人解决这个问题 我设置了 JDK 和 JRE 版本的环境变量 我在 USER 的用户变量 和 系统变量 这两个部分中添加了相同的值 我添加的变量是 JAVA HOME gt C PROGRA
  • 使用 log4j2 在 Tomcat 6 中记录服务器类

    我希望使用 log4j2 从我的 Web 应用程序和服务器 tomcat 6 本身进行日志记录 最好全部记录到同一个文件中 我的配置适用于 Web 应用程序 但不适用于服务器类 我想要的是将如下所示的行写入文件 它们当前仅写入控制台 Aug
  • HTTP 状态 404 - 请求的资源不可用

    在使用 MyEclipse IDE 中的 Tomcat 服务器和 Struts 2 框架时 我遇到了反复出现的问题 我将我的程序作为服务器应用程序运行 当它运行时 默认的index jsp 文件将成功打开 但应用程序的其他过去都不起作用 当
  • ExceptionHandler 不适用于 Throwable

    我们的应用程序是基于 Spring MVC 的 REST 应用程序 我正在尝试使用 ExceptionHandler 注释来处理所有错误和异常 I have ExceptionHandler Throwable class public R
  • Tomcat虚拟主机

    我有一个托管在服务器上的应用程序 我希望所有小客户共享一个虚拟主机 并且我想为每个大客户设置一个专用的虚拟主机 应用程序完全相同 我可以将虚拟点指向相同的代码库目录吗 我使用Tomcat 7作为应用程序服务器 请指教 Thanks 如果他们
  • 配置jmxremote时无法正常停止tomcat

    我添加了一个jmxremotecatalina bat中的配置 set JAVA OPTS Dcom sun management jmxremote port 9004 Dcom sun management jmxremote ssl
  • 使用 Servlet 3.0 上传文件时 request.getParameter() 是否仍然有效?

    我刚刚读过这个很好的答案 https stackoverflow com questions 2422468 how to upload files in jsp servlet 2424824 2424824来自 BalusC 关于如何使
  • maven + eclipse + tomcat:类未找到异常

    我有一个使用 servlet 的简单 Web 应用程序 我将其创建为 Maven 项目 然后将其导入到 Eclipse 中 文件 gt 导入 gt 现有的 Maven 项目 战争已正确部署到 tomcat 7 但是 当我在 Eclipse
  • Apache Tomcat/8.5.3 管理器应用程序 403 错误

    我有 tomcat 在 aws 上的 ubuntu 实例上运行 我可以成功访问If you re seeing this you ve successfully installed Tomcat Congratulations 页面 但是当
  • 如何更改部署在tomcat中的Web应用程序浏览器标题栏中显示的徽标?

    我在 tomcat 中部署了一个 Web 应用程序 当我在浏览器中打开应用程序时 地址栏中 URL 之前的图像和选项卡上应用程序标题之前的图像都显示 tomcat 图像 我想改变这一点并在这两个地方放入我自己的徽标 我怎样才能做到这一点 这
  • 作为 Windows 服务运行时,Tomcat 7 控制台和/或托盘图标不显示

    我已将 Tomcat7 安装为服务 service bat 它作为服务启动并运行良好 但当您使用 Tomcat7 exe 或startup bat 手动启动服务器时 我看不到通常出现的控制台 顺便说一句 我在 Windows 7 上 我从这
  • Spring Boot - 在部署时启动后台线程的最佳方式

    我在 Tomcat 8 中部署了一个 Spring Boot 应用程序 当应用程序启动时 我想在后台启动一个工作线程 Spring Autowires 具有一些依赖项 目前我有这个 SpringBootApplication EnableA
  • Tomcat 唯一 SessionId

    有没有办法配置 xml tomcat 6 x 来生成唯一的SessionId 无需扩展 ManagerBase StandardManager 我正在数据库表中捕获用户登录详细信息 会话 ID 在具有唯一约束的列中 并且收到唯一约束异常 您
  • Tomcat Intellij Idea:远程部署

    RackSpace 云服务器 Ubuntu 12 04 Intellij Idea 11 1 2 Windows 8 Tomcat 7 0 26 JDK 6 在 Intellij Idea 上 当我尝试在远程 Tomcat 7 服务器上运行

随机推荐

  • 关于AlreadyExistsError: Another metric with the same name already exists.的解决方案

    这项错误发生在我刚安装完tensorflow xff0c 想import keras包的时候发生了如下的错误 xff1a Tensorflow python framework errors impl AlreadyExistsError
  • uC/OSIII在Cortex-M3的任务切换和中断退出分析

    uC OSIII在任务中执行OSSched相关的函数和在中断退出后都会开始执行调度 xff0c 这是它的调度机制 而按uC OSIII书中所讲 xff0c 普通任务切换和从中断中退出后的任务切换应该是不同的函数 xff0c 因为普通任务切换
  • 每天一个adb命令:dumpsys命令详解

    dumpsys是一个能帮助我们对手机进行性能分析的命令 xff0c 它可以帮助我们获取电池 内存 cpu 磁盘 wifi等等信息 xff0c 具体能查询的信息可以通过命令 xff1a adb span class hljs built in
  • vs2017如何创建一个asax文件

    VS2017无法为网站创建Global asax文件 xff0c 导致出现错误WebForms UnobtrusiveValidationMode 需要 jquery ScriptResourceMapping 解决方案如下 xff1a 勾
  • Spring Security OAuth2.0认证授权

    文章目录 1 基本概念1 1 什么是认证1 2 什么是会话1 3什么是授权1 4授权的数据模型1 4 RBAC1 4 1 基于角色的访问控制 2 基于Session的认证方式3 整合案例3 1 SpringMVC 43 Servlet3 0
  • 浏览器不显示favicon.ico怎么办?

    原因1 xff1a 连接文件的路径不对 如上图路径的话href连接路径应该写成 xff1a href 61 34 img favicon ico 34 xff0c 具体如下 xff1a span class token operator l
  • VNCViewer连接树莓派失败、显示超时的部分原因

    刚入手树莓派 xff0c 在用VNCViewer这款软件实现树莓派的图形化桌面时遇到了一些坑 xff0c 在这里分享 xff0c 希望能对大家有所帮助 1 在文本框内输入IP地址之后一定要记得加上 端号 xff0c 如下图所示 这个端号在P
  • Kubernetes中文手册

    Kubernetes中文手册 https www kubernetes org cn kubernetes pod
  • JSP中文乱码问题终极解决方案

    在介绍方法之前我们首先应该清楚具体的问题有哪些 xff0c 笔者在本博客当中论述的 JSP 中文乱码问题有如下几个方面 xff1a 页面乱码 参数乱码 表单乱码 源文件乱码 下面来逐一解决其中的乱码问题 一 JSP 页面中文乱码 在 JSP
  • JS表白代码

    简单的JS弹窗表白代码 思路 xff1a 只有当用户输入1 xff08 表示喜欢你 xff09 才有进一步浏览的资格 如果用户输入2 xff08 不喜欢你 xff09 就会陷入死循环进行撒娇 xff0c 只有当用户输入1 xff0c 才可以
  • Quartz框架详解

    Quartz框架可以实现 异步定时任务 Quartz框架下载地址 注意1版本和2版本写法完全不一样 xff0c 本文采用的是2 x版本 下载完毕后进入进入lib文件夹 xff0c 然后将下面的几个jar引入项目 xff1a 基本实现步骤 x
  • 前端的端口问题

    本文 xff0c 将以通俗易懂的方式剖析 服务器 电脑 是怎么访问html文件 先说一下前置知识 xff1a 首先我们得知道一件事情 xff1a 电脑中每个运行的程序都对应着某个端口 xff0c 举个例子 xff1a 我们都知道mysql默
  • 湖北师范大学java习题汇编(超详细!已经进行了章节划分)

    表达式和流程控制语句 1 验证歌德巴赫猜想 一个充分大的偶数 xff08 大于或等于6 xff09 可以分解为两个素数之和 试编程序 xff0c 将 6至50之间全部偶数表示为两个素数之和 span class token keyword
  • OPENCV(五) 对给定的车牌进行字符分割

    下面有这样的一个车牌号 xff1a 现在的任务是将每一个字符区分开来 xff0c 并方框圈出来 完成这个功能需要以下的步骤 xff1a 1 灰度处理 span class token comment 读取图片 span image1 spa
  • SpringBoot整合forest(调用彩云API获取所有城市的实时天气)

    Forest简介 xff1a Forest是一个高层的 极简的轻量级HTTP调用API框架 相比于直接使用Httpclient您不再用写一大堆重复的代码了 xff0c 而是像调用本地方法一样去发送HTTP请求 环境配置 xff1a 因为本项
  • JAVA操作Kafka

    一 环境说明 1 电脑或你的服务器需要安装zookeeper和kafka 可以参考我的这篇博客 xff1a 请点击这里 xff01 2 项目中需要下面的依赖 xff1a span class token tag span class tok
  • Gradle使用本地maven仓库

    一 基本配置 在repositories下添加mavenLocal 方法 plugins span class token punctuation span id span class token string 39 java 39 spa
  • Docker容器编排

    一 简介和下载安装 1 简介 docker compose是Docker官方的开源项目 xff0c 可以管理多个docker容器组成的一个应用 你需要定义一个YAML格式的配置文件docker compose yaml xff0c 写好多容
  • 若依微服务(ruoyi-cloud)保姆版容器编排运行

    一 简介 项目gitee地址 xff1a https gitee com y project RuoYi Cloud 由于该项目运行有很多坑 xff0c 大家可以在git克隆拷贝到本地后 xff0c 执行下面的命令使master版本回退到本
  • 深入学习Tomcat----自己动手写服务器(附服务器源码)

    相信大多 Web 开发者对 Tomcat 是非常熟悉的 xff0c 众所周知 Tomcat 是一款非常好用的开源 Servlet 容器 xff0c 您一定对这个最流行的 Servlet 容器充满好奇 xff0c 虽然它并不像一个黑盒子那样让