【0】README
0.1)本文部分描述转自“深入剖析tomcat”, 旨在学习 一个简单的web server 的基础知识;
0.2)for complete source code, please visit https://github.com/pacosonTang/HowTomcatWorks/tree/master/chapter1
【1】HTTP
【1.1】HTTP请求
1)一个HTTP请求包括以下3部分(parts):(干货——一个HTTP请求包括以下3部分(parts))
p1)请求方法——统一资源标识符(URI)——协议/版本;p2)请求头;p3)实体;
2)HTTP 请求的示例如下所示:(干货——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
// 这里是空行(CRLF)
lastName=Yun&firstName=Lin
对以上HTTP请求的分析(Analysis):(干货——对HTTP请求的分析)
A1)第一行:请求方法——URI——协议/版本(Post /examples/default.jsp HTTP/1.1)A2)HTTP1.1 支持的请求方法有:GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE;A3)URI:指定internet 资源的完整路径;而统一资源定位符(URL) 是 URI 的一种类型;A4)在请求头和请求实体间有一个空行:该空行只有 CRLF 符;CRLF 告诉HTTP 服务器请求实体正文从哪里开始;正文(lastName=Yun&firstName=Lin)
【1.2】HTTP响应
1)HTTP响应也包括3部分(parts):
part1)协议——状态码——描述;part2)响应头;part3)响应实体段;
2)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
//空行(CRLF)
<html>
<head>
<title>hello, world</title>
</head>
<body>
hello, world
</body>
</html>
对上述HTTP响应代码的分析(Analysis):
A1)第一行的200,表示状态码(请求发送成功);A2)响应头和响应实体正文间由只包含 CRLF 的一个空行分隔;
【1.3】Socket类
1)看个荔枝:(创建一个套接字,用于与本地HTTP Server 进行通信(127.0.0.1 表示一个本地主机),发送HTTP请求接收server 的响应信息);
// 创建一个套接字,用于与本地HTTP Server 进行通信(127.0.0.1 表示一个本地主机),发送HTTP请求接收server 的响应信息
// 采用tomcat 开启8080 端口
public class SocketTest {public static void main(String[] args) throws Exception {try(Socket socket = new Socket("127.0.0.1", 8080)){OutputStream os = socket.getOutputStream();boolean autoflush = true;PrintWriter out = new PrintWriter(socket.getOutputStream(), autoflush);BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));// send an HTTP request to the web serverout.println("GET /index.jsp HTTP/1.1");out.println("Host: localhost:8080");out.println("Connection: Close");out.println();// read the responseboolean loop = true;StringBuffer sb = new StringBuffer(8096);while(loop) {if(in.ready()) {int i = 0;while(i != -1) {i = in.read();sb.append((char)i);}loop = false;}Thread.currentThread().sleep(50);}// display the response to the out consoleSystem.out.println(sb.toString());socket.close();}}
}
【3】应用程序
0)intro:web服务器应用程序包括3个类: HttpServer, Request, Response;
1)我们先看运行结果(显然发出了两个HTTP 请求,浏览器发出一个获取静态资源的 HTTP请求, 而该静态资源又 发出获取图像资源的 HTTP 请求)(干货——显然发出了两个HTTP 请求):
E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java com.tomcat.chapter1.HttpServer
E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src
GET /index.html HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6GET /images/psu.jpg HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Accept: image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
Referer: http://localhost:8080/index.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6GET /SHUTDOWN HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
2)HttpServer, Request, Response 的源码如下所示:
public class HttpServer { // 接受HTTP 请求的web server/** WEB_ROOT is the directory where our HTML and other files reside.* For this package, WEB_ROOT is the "webroot" directory under the working* directory.* The working directory is the location in the file system* from where the java command was invoked.*/public static final String WEB_ROOT =System.getProperty("user.dir") + File.separator + "webroot";// shutdown commandprivate static final String SHUTDOWN_COMMAND = "/SHUTDOWN";// the shutdown command receivedprivate boolean shutdown = false;public static void main(String[] args) {System.out.println(System.getProperty("user.dir"));HttpServer server = new HttpServer();server.await();// 会在指定端口上等待HTTP请求。}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);}// Loop waiting for a requestwhile (!shutdown) {Socket socket = null;InputStream input = null;OutputStream output = null;try {socket = serverSocket.accept();input = socket.getInputStream();output = socket.getOutputStream();// create Request object and parse,创建 HTTP请求对象Request request = new Request(input);request.parse(); // 解析HTTP请求字符串// create Response object,创建HTTP 响应对象Response response = new Response(output);response.setRequest(request);response.sendStaticResource(); // 发送静态资源到client// Close the socketsocket.close();//check if the previous URI is a shutdown commandshutdown = request.getUri().equals(SHUTDOWN_COMMAND);}catch (Exception e) {e.printStackTrace();continue;}}}
}
public class Request { // 封装 HTTP 请求字符串的类private InputStream input;private String uri;public Request(InputStream input) {this.input = input;}public void parse() {// Read a set of characters from the socketStringBuffer 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;}}
public class Response { <span style="font-family: 宋体;">// 封装 HTTP 响应字符串的类</span>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 {// file not foundString 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 objectSystem.out.println(e.toString() );}finally {if (fis!=null)fis.close();}}
}
补充)本文总结了一张上述应用程序的调用流程图
对上图的分析(Analysis):
A1)HttpServer:
step1)创建服务器套接字,等待接收 client 发出HTTP 连接请求;
step2)连接成功后,利用套接字创建输入输出流;
step3)创建Request对象, 向Request构造函数传入输入流,并利用request实例对象解析 HTTP 请求;
step4)创建Response对象,向Response构造函数传入输出流,且设置 Response中的request变量引用,调用response对象的sendStaticResource方法发送静态资源到 client;
A2)Request:HTTP请求对象,其parse方法用于读取HTTP请求头;其parseUri方法用于解析client 请求的 uri;
A3)Response:HTTP响应对象,其sendStaticResource方法 读取request解析出的uri对应的资源文件,并发送该文件数据到client端;
<html>
<head>
<title>How Tomcat Works</title>
</head>
<body>
<img src="./images/psu.jpg">
<br>
hello, my name is xiao tangtang.
</body>
</html>
4)最后po 出 整体的文件目录架构