跟我一块儿动手实现Tomcat(一):实现静态Web服务器

前言

最近笔者读了《深刻剖析tomcat》这本书(原做:《how tomcat works》),发现该书简单易读,每一个
章节按部就班的讲解了tomcat的原理,在接下来的章节中,tomcat都是基于上一章新增功能并完善,
到最后造成一个简易版tomcat的完成品。因此有兴趣的同窗请按顺序阅读,本文为记录第一章的知识点
以及源码实现(造轮子)。
复制代码

如何实现

HTTP协议就是我们web服务器与浏览器交互的协议,具体的知识点以及背景本文就再也不累述。那么举一个简单的例子就好:css

  • 在浏览器输入http://www.baidu.com按下回车键。
  • 浏览器大概发送了如下的http请求到百度的服务器中:
GET /index.html HTTP/1.1
Host: www.baidu.com
...
复制代码
  • 百度web服务器在接收到咱们的请求的时候,找到对应的服务器资源并相应:
HTTP/1.1 200 OK
...

<html>
<head>
<title>百度一下你就知道</title>
</head>
<body>
....
</body>
</html>
复制代码

那么其实经过上面的例子咱们能够发现,静态(这里指的是html/图片/css等)web服务器的实现也是比较简单的:html

代码实现

在这里使用java socket api 实现简单的静态web服务器。
复制代码
  • 新建一个main方法,核心代码以下:
//开启socket server 8080端口监听.
ServerSocket server = new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));

try (Socket accept = serverSocket.accept();
InputStream inputStream = accept.getInputStream();
OutputStream outputStream = accept.getOutputStream()) {
//解析用户的请求
Request request = new Request();
request.setRequestStream(inputStream);
request.parseRequest();
//生成响应对象并响应静态资源
Response resp = new Response(outputStream, request);
resp.accessStaticResources();
} catch (IOException e) {
LOGGER.warn("catch from user request.",e);
}
//关闭服务器
serverSocket.close();
复制代码
  • Request 对象java

    主要功能:将用户请求(socket的inputStream流)解析为字符串,提取请求中的URI
    复制代码

解析字符串代码以下:git

StringBuilder requestStr = new StringBuilder();
int i;
//new 一个 byte缓冲数组
byte[] buffer = ArrayUtil.generatorCache();
try {
i = inputStream.read(buffer);
} catch (IOException e) {
e.printStackTrace();
i = -1;
}
//将读取到的byte转为String
for (int j = 0; j < i; j++) {
requestStr.append((char) buffer[j]);
}
//解析请求的字符串,提取请求的URI
this.parseURI(requestStr.toString());
复制代码

那么请求的信息被咱们解析成字符串了,咱们怎么知道他想请求什么静态资源呢?github

那咱们把解析字符串打印一下:web

System.out.println(requestStr.toString());
复制代码

GET /index.html HTTP/1.1

Host: 127.0.0.1:8080
Connection: keep-alive

Cache-Control: max-age=0

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36

Upgrade-Insecure-Requests: 1

Accept-Encoding: gzip, deflate, br

Accept-Language: zh-CN,zh;q=0.9
api

能够很明显的看到,加粗的地方就是咱们要提取的URI,那么怎么提取呢?细心的咱们发现了,/index.html 这段字符串先后都有一个空格!行,那咱们能够直接用String的indexOf方法解析,参考代码以下:数组

// 获取/index.html 前面的那个空格索引
int oneSpace = requestStr.indexOf(" ");
//获取/index.html 后面的那个空格索引
int twoSpace = requestStr.indexOf(" ", oneSpace + 1);
//截取得到用户请求URI
uri = requestStr.substring(oneSpace + 1, twoSpace);
复制代码
  • Response 对象浏览器

    上面Request对象已经把用户想请求的资源解析出来了,那么Response的功能就是找到这个文件,
    使用Socket的outputStream把文件做为字节流输出给浏览器,就能够将咱们的HTML显示给用户啦~
    复制代码

    那么这个项目咱们的静态文件放在那里呢?来看看咱们的项目结构:tomcat

-main  
-java java代码
-resources
-webroot 存放咱们静态资源的文件夹
复制代码

由于是只使用MAVEN构建项目,咱们也没使用Spring等框架,如何定位到webroot这个文件夹呢?参考了网上的代码:

String WEB_PROJECT_ROOT = HttpServer.class.getClassLoader().getResource("webroot").getFile().substring(1);
复制代码

前面的疑惑都解决了,接下来咱们就直接把对应的文件找到给写回去就完事了~
伪码以下:

//根据请求URI找到用户对应请求的资源文件
File staticResource = new File(HttpServer.WEB_PROJECT_ROOT + request.getUri());
//资源存在
if (staticResource.exists() && staticResource.isFile()) {
outputStream.write(this.responseToByte(200,"OK"));
write(staticResource);
//资源不存在,使用默认的404返回
} else {
staticResource = new File(HttpServer.WEB_PROJECT_ROOT + "/404.html");
outputStream.write(this.responseToByte(404,"file not found"));
write(staticResource);
}
复制代码

其中,responseToByte()这个方法只负责将响应行输出:

HTTP/1.1 200 OK
复制代码

资源不存在时我们就输出:

HTTP/1.1 404 file not found
复制代码

write()方法也很简单,将传入的file对象转成流并使用socket的outputStream输出

try (FileInputStream fis = new FileInputStream(file)) {
byte[] cache = new byte[1024];
int read;
while ((read = fis.read(cache, 0, 1024)) != -1) {
outputStream.write(cache, 0, read);
}
}
复制代码

看看效果

运行main方法,打开咱们的浏览器输出127.0.0.1/index.html按下回车,能够看到结果如图:


试试随便输入一个不存在的资源:

按下F12看看Http请求和响应分别是怎样的:

请求:
GET /abc.html HTTP/1.1
Host: 127.0.0.1:8080
其余请求头忽略...

响应:
HTTP/1.1 404 file not found

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404 not found!</title>
</head>
<body>
<h1>请求页面不存在!</h1>
</body>
</html>
复制代码

到这里,我们的Tomcat 1.0 web服务器就已经开发完成啦(滑稽脸),已经能够实现简单的html和css、图片等资源的访问等功能,下一章我们来实现如下简单的Servlet容器功能开发:

跟我一块儿动手实现Tomcat(二):实现简单的Servlet容器

PS:本章源码已上传github SimpleTomcat

相关文章
相关标签/搜索