HTTP服务器接收到请求后将请求转交给Servlet容器,Servlet容器通过Servlet接口调用业务类
当用户请求某个URL资源时,
Tomcat有两个核心组件,连接器和容器
Coyote是Tomcat中连接器的组件名称,是对外的接口。客户端通过Coyote与服务器建立连接,发送请求并接收响应
组件 | 作用 |
EndPoint |
EndPoint 是 Coyote 通信端点,即通信监听的接⼝,是具体Socket接收和发 送处理器,是对传输层的抽象,因此EndPoint⽤来实现TCP/IP协议的 |
Processor |
Processor 是Coyote 协议处理接⼝ ,如果说EndPoint是⽤来实现TCP/IP协 议的,那么Processor⽤来实现HTTP协议,Processor接收来⾃EndPoint的 Socket,读取字节流解析成Tomcat Request对象,并通过Adapter将其提交到容器处理,Processor是对应⽤层协议的抽象 |
ProtocolHandler |
Coyote 协议接⼝, 通过Endpoint 和 Processor , 实现针对具体协议的处 理能⼒。Tomcat 按照协议和I/O 提供了6个实现类 : AjpNioProtocol , AjpAprProtocol, AjpNio2Protocol , Http11NioProtocol , Http11Nio2Protocol ,Http11AprProtocol |
Adapter |
由于协议不同,客户端发过来的请求信息也不尽相同,Tomcat定义了⾃⼰的 Request类来封装这些请求信息。ProtocolHandler接⼝负责解析请求并⽣成 Tomcat Request类。但是这个Request对象不是标准的ServletRequest,不 能⽤Tomcat Request作为参数来调⽤容器。Tomcat设计者的解决⽅案是引 ⼊CoyoteAdapter,这是适配器模式的经典运⽤,连接器调⽤ CoyoteAdapter的Sevice⽅法,传⼊的是Tomcat Request对象, CoyoteAdapter负责将Tomcat Request转成ServletRequest,再调⽤容器 |
Tomcat是一个有一系列可配置组件(conf/server.xml)组成的Web容器,Catalina是Tomcat的核心,其他模块都是为Catalina提供支撑的
可以认为整个Tomcat是一个Catalina实例,Tomcat启动时会初始化这个实例,Catalina实例加载server.xml完成其他实例创建,创建并管理一个Server,Server创建并管理多个服务,每个服务又可以有多个Connector和一个Container
Container组件包含如下分层父子关系的组件:
通过标签对conf/server.xml进行配置
主要标签结构
Server标签
Service标签
Executor标签
Connector标签
Engine标签
Host标签
可理解为一台服务器部署多个站点
Context标签
用于配置一个Web应用
docBase:Web应⽤⽬录或者War包的部署路径。可以是绝对路径,也可以是相对于 Host appBase的相对路径。
path:Web应⽤的Context 路径。如果我们Host名为localhost, 则该web应⽤访问的根路径为:
http://localhost:8080/web_demo。
启动类
package server;
import util.HttpProtocolUtil;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Bootstrap {
private int port = 8080;
private void start() throws IOException {
ServerSocket serverSocket = new ServerSocket(port);
System.out.println(">>>MiniCat Start...");
/**
* 1.0版本
* 浏览器请求 http://loacalhost:8080,返回"Hello MiniCat"
*/
/*while (true) {
Socket socket = serverSocket.accept();
OutputStream outputStream = socket.getOutputStream();
String data = "Hello MiniCat";
String response = HttpProtocolUtil.buildHttpHeader200(data.getBytes().length) + data;
outputStream.write(response.getBytes());
socket.close();
}*/
/**
* 2.0版本
* 封装Request和Response对象,返回html静态资源文件
*/
while (true) {
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
Request request = new Request(inputStream);
Response response = new Response(socket.getOutputStream());
response.outputHtml(request.getUrl());
socket.close();
}
}
public static void main(String[] args) {
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
请求对象
package server;
import java.io.IOException;
import java.io.InputStream;
public class Request {
private String method;
private String url;
private InputStream inputStream;
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;
}
public Request(InputStream inputStream) throws IOException {
this.inputStream = inputStream;
int available = 0;
while (available == 0) {
available = inputStream.available();
}
byte[] bytes = new byte[available];
inputStream.read(bytes);
String requestStr = new String(bytes);
String firstLineStr = requestStr.split("\\n")[0];
String[] firstLine = firstLineStr.split(" ");
String method = firstLine[0];
String url = firstLine[1];
this.method = method;
this.url = url;
}
}
响应对象
package server;
import util.HttpProtocolUtil;
import util.StaticResourceUtil;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Response {
private OutputStream outputStream;
public Response(OutputStream outputStream) {
this.outputStream = outputStream;
}
public void output(String content) throws IOException {
outputStream.write(content.getBytes());
}
public void outputHtml(String path) throws IOException {
String absolutePath = StaticResourceUtil.getAbsolutePath(path);
File file = new File(absolutePath);
if (file.exists() && file.isFile()) {
StaticResourceUtil.outputStaticResource(new FileInputStream(file), outputStream);
} else {
output(HttpProtocolUtil.buildHttpHeader404());
}
}
}
封装响应码工具类
package util;
public class HttpProtocolUtil {
public static String buildHttpHeader200(int contentLength) {
return "HTTP/1.1 200 OK \n" +
"Content-Type: text/html \n" +
"Content-Length: " + contentLength + " \n" +
"\r\n";
}
public static String buildHttpHeader404() {
String str404 = "404 Not Found
";
return "HTTP/1.1 404 Not Found \n" +
"Content-Type: text/html \n" +
"Content-Length: " + str404.length() + " \n" +
"\r\n" + str404;
}
}
静态资源访问工具类
package util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class StaticResourceUtil {
public static String getAbsolutePath(String path) {
String absolutePath = StaticResourceUtil.class.getResource("/").getPath();
return absolutePath.replaceAll("\\\\", "/") + path;
}
public static void outputStaticResource(InputStream inputStream, OutputStream outputStream) throws IOException {
int count = 0;
while (count == 0) {
count = inputStream.available();
}
int resourceSize = count;
outputStream.write(HttpProtocolUtil.buildHttpHeader200(resourceSize).getBytes());
int written = 0;
int byteSize = 1024;
byte[] bytes = new byte[byteSize];
while (written < resourceSize) {
if (written + byteSize > resourceSize) {
byteSize = resourceSize - written;
bytes = new byte[byteSize];
}
inputStream.read(bytes);
outputStream.write(bytes);
outputStream.flush();
written += byteSize;
}
}
}