Tomcat是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,也是一个servlet容器的优秀解决方案,做Java web开发的基本上都使用过,但是tomcat大多时间对于我们是一个黑盒,出了问题无所适从,配置文件知道怎么写,但不知道为什么这么写,原理是什么.

本系列文章可以让你:

1.了解tomcat内部原理,比如tomcat怎么接收请求,怎么处理,怎么封装request,怎么开始,怎么使用lifeCycle,日志是怎么生成的,session的原理,为什么session会过期,什么是                                engine,host,context,wrapper,tomcat是怎么加载你写的servlet的等等等等等等等等

2.理解设计模式,tomcat使用了外观模式,责任链模式,线程池等设计

3.不编了..自己挖掘吧

本着开源的思想,每章的代码都可以访问我的gitHub获取,每章的结尾会放上地址,我的环境为idea+maven,不过并没有多少依赖

本文的部分代码和思想来源于《深入剖析Tomcat》即《How Tomcat works》和Tomcat4源码

1.http协议

了解http协议是必须的,这里我只简单介绍一下,更多的东西可以参考《深入体验Java Web开发内幕--核心基础》或者更专业的资料

客户端(浏览器,以后统称客户端)会向服务器建立连接,然后发出请求,请求包括一个请求行,0或若干请求头、请求实体,可能是这样的

          

请求行:GET getPrice.jspx HTTP/1.1

请求头:Accept:*/*

Referer:www.asens.cn

Host:locathost

  

也可能是这样的

POST getPrice.jspx HTTP/1.1

Host:locathost

Connection:Keep-Alive

name=user&pass=123

  

服务器接到这样的请求后经过一系列的处理,生成响应返回给客户端,响应可能是这样的

HTTP/1.1 200 OK

Content-length:3242

Content-Type:text.html

我是实体内容

  客户端接收到响应后,浏览器就会出现这几个字:我是实体内容,通常情况下响应会返回一个html页面供人浏览

2.最简单的静态server

这个服务器只能访问静态文件,非常简单,只需要3个类

HttpServer,Request,Response,然后在项目根目录创建webroot文件夹作为资源路径

在客户/服务器通信模式中, 服务器端需要创建监听端口的 ServerSocket, ServerSocket 负责接收客户连接请求,先创建一个ServerSocket

 ServerSocket serverSocket = null;
int port = 80;
try {
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
}
catch (IOException e) {
e.printStackTrace();
System.exit(1);
}

   为了方便测试,我设置接听了localhost的80端口,socketServer能时刻待命,接收来自客户端的连接请求,socketServer的实现使用了连接池和线程池,不过具体的实现不在本文讨论范围,有需要的朋友可以自行研究.

 socket = serverSocket.accept();
input = socket.getInputStream();
output = socket.getOutputStream();

接收到客户请求后serverSocket会返回一个socket实例,然后获取InputStream和OutPutStream

        Request request = new Request(input);
request.parse(); Response response = new Response(output);
response.setRequest(request);
response.sendStaticResource(); socket.close();

  然后件inputStream流解析成request,当然这个request还是很原始的request,获取请求里面的uri,解析之后将request传入response

   public void parse() {
// Read a set of characters from the socket
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;
}

  request从流中读取信息,并在请求行中解析出请求的uri

如:GET index.html HTTP/1.1 ,uri就会被设置成index.html,在项目的根目录创建一个webroot的文件夹,创建一个index.html文件,uri和这个文件名要对应

在response中,将读取本地的webroot文件夹的request的uri对应名字的文件,通过socket的outputStream发送给客户端

   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 {
// file not found
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) {
// thrown if cannot instantiate a File object
System.out.println(e.toString() );
}
finally {
if (fis!=null)
fis.close();
}
}

  

运行httpServer的main方法,然后打开浏览器,输入localhost/index.html

这个纯静态的服务器还是很简单的,但是如果你是初学者的话,还是希望你手动实现一遍

GitHub地址:https://github.com/Asens/AsServer/tree/master/AsServerV1.0

在下一章我将会实现一个支持简单servlet的服务器

从零开始写一个Tomcat(壹)的更多相关文章

  1. 从零开始写一个Tomcat(叁)--请求解析

    挖坑挖了这么长时间也该继续填坑了,上文书讲到从零开始写一个Tomcat(贰)--建立动态服务器,讲了如何让服务器解析请求,分离servlet请求和静态资源请求,读取静态资源文件输出或是通过URLCla ...

  2. 从零开始写一个Tomcat(贰)--建立动态服务器

    上文书说道如何通过http协议建立一个静态的服务器来访问静态网页,但我们选择tomcat最主要的原因还是因为它能动态的执行servlet,这边文章将引导你实现一个能够运行servlet的服务器,这个简 ...

  3. 深入浅出React Native 3: 从零开始写一个Hello World

    这是深入浅出React Native的第三篇文章. 1. 环境配置 2. 我的第一个应用 将index.ios.js中的代码全部删掉,为什么要删掉呢?因为我们准备从零开始写一个应用~学习技术最好的方式 ...

  4. 从零开始写一个武侠冒险游戏-8-用GPU提升性能(3)

    从零开始写一个武侠冒险游戏-8-用GPU提升性能(3) ----解决因绘制雷达图导致的帧速下降问题 作者:FreeBlues 修订记录 2016.06.23 初稿完成. 2016.08.07 增加对 ...

  5. 从零开始写一个武侠冒险游戏-7-用GPU提升性能(2)

    从零开始写一个武侠冒险游戏-7-用GPU提升性能(2) ----把地图处理放在GPU上 作者:FreeBlues 修订记录 2016.06.21 初稿完成. 2016.08.06 增加对 XCode ...

  6. 从零开始写一个武侠冒险游戏-6-用GPU提升性能(1)

    从零开始写一个武侠冒险游戏-6-用GPU提升性能(1) ----把帧动画的实现放在GPU上 作者:FreeBlues 修订记录 2016.06.19 初稿完成. 2016.08.05 增加对 XCod ...

  7. 一起学习造轮子(二):从零开始写一个Redux

    本文是一起学习造轮子系列的第二篇,本篇我们将从零开始写一个小巧完整的Redux,本系列文章将会选取一些前端比较经典的轮子进行源码分析,并且从零开始逐步实现,本系列将会学习Promises/A+,Red ...

  8. 一起学习造轮子(一):从零开始写一个符合Promises/A+规范的promise

    本文是一起学习造轮子系列的第一篇,本篇我们将从零开始写一个符合Promises/A+规范的promise,本系列文章将会选取一些前端比较经典的轮子进行源码分析,并且从零开始逐步实现,本系列将会学习Pr ...

  9. 一起学习造轮子(三):从零开始写一个React-Redux

    本文是一起学习造轮子系列的第三篇,本篇我们将从零开始写一个React-Redux,本系列文章将会选取一些前端比较经典的轮子进行源码分析,并且从零开始逐步实现,本系列将会学习Promises/A+,Re ...

随机推荐

  1. python正则表达式之使用规则

         正则表达式在我看来是提供一个模板,将待匹配的字符串与模板匹配,匹配不到则返回为空,匹配成功根据需要返回匹配的字符串. 正则表达式比字符串本身的功能要强一点,当然性能上略有不如. 我们使用正则 ...

  2. bash shell学习-shell基础 (笔记)

    When you hoist the sails to cross the sea, you willride the wind and cleave the waves. "长风破浪会有时 ...

  3. Android学习----AndroidManifest.xml文件解析

    一个Android应用程序的结构: 一.关于AndroidManifest.xml AndroidManifest.xml 是每个android程序中必须的文件.它位于整个项目的根目录,描述了pack ...

  4. 初涉JavaScript模式 (8) : 函数 【概述】

    什么是函数 函数,是一个大型程序中的某部份代码,由一个或多个语句块组成.它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性.(维基百科) 函数的特点 第一类对象 在JavaScript世界中 ...

  5. javascript写的ajax请求

    <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> &l ...

  6. memcache锁

    锁的使用,一般情况是针对并发或者我们希望程序(crontab的job)串行处理,我们加锁的办法有很多,像文件锁,数据库锁,或者memcache锁,这里关注一下memcache锁,针对memcache锁 ...

  7. Nginx源码研究五:NGINX的配置信息管理

    配置信息是nginx系统重要的组成部分,配置信息的使用,实际上包含两层,一层是用户针对参数定义了值,例如下面nginx参数文件中的 keepalive_timeout 65,还有一部分是用户没有定义值 ...

  8. HTTP协议学习-01

    在学习一门新知识前还是先了解一下这个知识的一点点背景吧! http是属于协议层当中的最顶层的应用层,的面向对象的协议:它于 1990 年提出, 经过几年的使用与发展, 得到不断地完善和扩展. 目前在 ...

  9. IOS快速开发之常量定义

    ---恢复内容开始--- 在IOS开发中,有一些方法常常需要用的,但是有很长的方法名,这造成了代码长,写起来累,我们可以通过宏定义了解决这些问题 比如说在代码布局的时候会遇上这样的问题,我们要获取上面 ...

  10. [Ioi2005]River

    设f[i][j][k]表示i上游最近的一个伐木场为j且在i所在的子树里共建了k个伐木场(不包含在i的)的最小运费和 设v为u的儿子,dist[u]为u到0号点的距离. 则当i>=j时 f[u][ ...