首先tomcat的原理是什么?
上面的图可以简化为:
这张图固然缺少了很多东西,但是却是tomcat实现功能的一个基本逻辑
简单的来说,就是用户发出一个请求,后台处理url,通过组合根地址,找到本地的资源地址,然后通过读取这个资源,将它的流输出到页面,这就是tomcat的简单原理
那如何实现呢?
简单的来说:Tomcat由三个基本类组成
- HttpServer
- Request
- Response
Request负责处理url
Response负责处理响应的信息
HttpServer负责阻塞IO,监听消息
既然职责已经分好了,那我们就用代码来实现这个简单的Tomcat吧
首先是HttpServer类:
public class HttpServer{
//检验是否运行
public boolean shutdown = false;
//阻塞接口的方法
public void acceptWait(){
SocketServer sever = null;
//配置server
try{
//配置服务器的端口,线程连接数,以及地址
server = new SocketServer(8080,1,"127.0.0.1");
}catch(Exception ex){
ex.printStack();
}
while(!shutdown){
try{
//创建socket对象
Socket socket = SocketServer.accept();
//获取流对象
InputStream input = socket.getInputStream();
OutputStream output = socket.getOutStream();
//根据输入流构建请求对象
Request request = new Request(input);
//解析对应的url
request.parse();
//根据对应的输入流构建响应对象
Response response = new Response(output);
//设置其本身的请求对象
response.setRequest(request);
//响应静态资源
response.sendStaticResource();
//判断url是否是/shutdown,如果是,则关闭容器
if(null!=request){
shutdown = request.getUrl().equals("/shutdown");
}
}catch(Exception ex){
ex.printStack();
continue;
}
}
}
//运行HttpServer组件
public static void main(String args[]){
HttpServer server = new HttpServer();
server.acceptWait();
}
}
下面构建Request对象
/*
request对象的基本职能就是接受一个输入流,
从socket中去解析对应的url,并且返回即可
*/
public class Request{
private String url;
private InputStream is;
//构建方法
public Request(InputStream is){
this.is = is;
}
//解析输入的url
public void parse(){
//1.首先需要从socket中的输入流获取信息并且转换为String
//1.1首先准备一个固定长度的StringBuffer和缓冲数组
StringBuffer request = new StringBuffer(Response.BUFFER_SIZE);
byte[] buffer = new byte[Response.BUFFER_SIZE];
//1.2从输入流中读取对应的字节长度
try{
int ch = is.read(buffer,0,ReSponse.BUFFER_SIZE);
}catch(Exception ex){
ex.printStack();
ch = -1;
}
//1.3假如有效
for(int i = 0; i < ch;i++){
request.append((char)buffer[i]);
}
//2.其次从String中获取对应的url信息
url = parseUrl(request.toString());
}
//Url的解析方法:这里要对socket的header进行解析
public void parseUrl(String requestString){
//设定索引
int index1,index2;
//需要解释一下的是,从socket输入流获取的信息其实长下面这样的
//而我们只需要"/hello/text.txt"这么一节就行了
/*
GET /hello/text.txt HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
*/
index1 = requestString.indexOf(' ');//注意是空格,因为头信息是根据空格分隔到的
//如果请求头有效
if(index1!=-1){
//下面的表示从index1+1的位置开始查找下一个" "的索引
index2 = requestString.indexOf(' ',index1+1);
if(index2 > index1) return requestString.subString(index1+1,index2);
}
//是在没有
return null;
}
}
下面展开response的方法
public class Response{
//默认大小
public static final int BUFFER_SIZE = 2048;
//默认根路径
public static final String WEB_ROOT = "D:";
//输出流
private OutputStream os;
//请求对象
private Request request;
//创建方法
public Response(OutputStream os){
this.os = os;
}
//设置request方法
public void setRequest(Request request){
this.request = request;
}
public void sentStaticResource() throws Exception{
//此处需要开启文件流,文件流最终是需要关闭的,所以必须先去做文件流的finally关闭
FileInputStream fls = null;
byte[] buffer = new byte[Response.BUFFER_SIZE];
try{
//创建文件
File file = new File(WEB_ROOT,request.getUrl());
//如果文件存在且不是一个文件夹,就创建文件流
if(file!=null && !file.isDirectory()){
fls = new FileInputSteam(file);
//读取字节个数
int ch = fls.read(buffer,0,BUFFER_SIZE);
while(ch!=-1){
os.write(buffer,0,ch);
ch = fls.read(buffer,0,BUFFER_SIZE);
}
}else{
//假如不存在
//编写响应内容
String retMessage = "<h1>"+file.getName()+" file or directory not exists</h1>";
//编写响应头
String returnMessage = "HTTP/1.1 404 File Not Found\r\n" +
"Content-Type:text/html\r\n"+
"Content-Length:"+retMessage.length() + "\r\n" +
"\r\n"+
retMessage;
//将响应信息的二进制信息写入到输出流中
output.write(returnMessage.getBytes());
}
}catch(Exception ex){
ex.printStack();
}finally{
if(fls!=null){
fls.close();
}
}
}
}
最后就是测试了
在D盘下新建text.txt文件,写入内容hello tomcat
最后的效果
最后需要注意的是
不要用谷歌浏览器去访问,因为我这边的Demo没有写200的响应头,所以谷歌是无法解析的,但是火狐浏览器是可以解析的
并且这个实验可以用maven做,不需要添加任何的组件
参考文章:
Tomcat原理以及详解:https://www.cnblogs.com/irockcode/p/6796531.html
阿里云社区手写tomcat:https://www.jianshu.com/p/bc9343a95554