目录
WebServer/src/main/java
WebSever :一个模拟Tomcat的轻量级web容器。
包结构:
HTTP协议
超文本传输协议:浏览器与服务端之间传输数据的协议,底层的传输协议为TCP。HTTP则为应用层协议,负责定义传输数据的格式。HTTP协议分为1.0与1.1两个版本。现在常用为1.1版本。协议规定客户端与服务端通讯方式为:一次请求一次响应.即:客户端发起请求,服务端接收到请求后向客户端发送响应。服务端不会主动发送内容给客户端。采取"一问一答"的形式。
HTTP对请求与响应分别定义了格式。并且,无论是请求还是响应中发送的字符(不含正文部分内容)都只能符合ISO8859-1编码字符(如:数字,字母,符号)。像中文等其他字符都需要经过处理后才可以发送。
HTTP请求格式
一个HTTP请求分为三部分组成:请求行,消息头,消息正文.
1:请求行
请求行分为三部分:请求方法 资源路径 协议(CRLF) ,
method url protocol(CRLF).
例如:GET /myweb/index.html HTTP/1.1(CRLF)
请求行以CRLF结束,CR:回车符,asc编码中对应数字13.LF:换行符,asc编码中对应数字10.
2:消息头
消息头由若干行表示,每行表示一个具体的头信息.每个头信息格式分为两部分:消息头名字:消息头的值(CRLF).name:value(CRLF)
每个消息头都以CRLF结尾。最后一个消息头结尾处会有两个CRLF,第一个表示最后一个消息头结束,第二个表示消息头部分结束。
例如:
Host: localhost:8080(CRLF)
Connection: keep-alive(CRLF)
Cache-Control: max-age=0(CRLF)
Upgrade-Insecure-Requests: 1(CRLF)
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36(CRLF)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8(CRLF)
Accept-Encoding: gzip, deflate, sdch(CRLF)
Accept-Language: zh-CN,zh;q=0.8(CRLF)(CRLF)
3:消息正文
正文部分不是必须部分,消息正文是2进制数据。是客户端在发送请求时发送给服务端客户提交的数据。这些数据可能是注册信息,上传的图片等。具体数据是什么类型以及这些2进制数据有多少字节会在消息头中具体说明。若消息头中没有说明消息正文内容则这个请求中是不含有正文的。
下面是浏览器发送给服务端的一个请求(不含有正文部分)
GET /index.html HTTP/1.1(CRLF)
Host: localhost:8080(CRLF)
Connection: keep-alive(CRLF)
Cache-Control: max-age=0(CRLF)
Upgrade-Insecure-Requests: 1(CRLF)
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36(CRLF)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8(CRLF)
Accept-Encoding: gzip, deflate, sdch(CRLF)
Accept-Language: zh-CN,zh;q=0.8(CRLF)(CRLF)
01010101.....
HTTP响应
HTTP响应格式也分为三部分:状态行,响应头,响应正文。
状态行格式:
protorol status-code status-reason
协议版本 状态码 状态描述
状态代码有五类:
1xx:信息响应类,表示接受到请求并继续处理
2xx:处理成功响应类,表示动作被成功接收兵处理
3xx:重定向类,为了完成指定的动作,必须接受下一步处理
4xx:客户端错误类,表示客户端请求包含错误的语法或不能
正确的执行
5xx:服务端错误类,服务端不能正确的处理一个正确的请求。
常见的:
200:一切正常
302:服务端要求客户端重定向到指定路径
404:用于请求资源未找到
500:服务端处理异常
响应头格式:
响应头的格式与请求中的消息头格式一致。
响应正文:
响应正文也是二进制数据,用于将客户端请求的资源等信息发送回给客户端。该正文具体表示的介质类型以及占用的字节长度会在响应头中有所描述。
一个HTTP响应大致内容:
HTTP/1.1 200 OK(CRLF)
Content-Type:text/html(CRLF)
Content-Length:224586(CRLF)(CRLF)
1101010101001.....2进制字节数据
webserver项目阐述:
(1) WebSever功能 :一个模拟Tomcat的轻量级web容器,它可以处理页面中的账号的注册、登录和修改密码操作。
(2)webserver项目原理:是基于超文本传输协议的,它是浏览器与服务端之间传输数据的协议,底层的传输协议为TCP。HTTP则为应用层协议,负责定义传输数据的格式。HTTP协议分为1.0与1.1两个版本。现在常用为1.1版本。协议规定客户端与服务端通讯方式为:一次请求一次响应.即:客户端发起请求,服务端接收到请求后向客户端发送响应。服务端不会主动发送内容给客户端。采取"一问一答"的形式。
(3)我先描述一下项目结构:先建一个Maven项目,工程名叫webserver,webserver下有java文件(改文件里写的是java代码)、conf配置文件(servlet.xml文件是注册、登录或者修改密码页面利用反射动态加载对应的Servlet,web.xml文件内容是映射关系mime-mapping标签,通过文件后缀名来获取文件类型,来告诉浏览器这个文件是一张图片、一个视频还是别的类型)和webapp网络应用文件(里面写的是各种html页面,包括首页页面、注册登录修改密码和404提示页面)。java文件下有四个包,核心包core(该包主要是用于启动服务端、处理请求和响应客户端主要流程控制、还有解析servlet.xml文件,根据请求去获取相应的servlet)、http包(解析web.xml文件存放到Map中,文件后缀名作为key,文件类型作为value、详细的解析请求和发送响应、还有声明空请求异常)和servlet包(各种Servlet,用与注册登录修改密码操作)。
(4)主要流程:先初始化服务端,利用ServerSocket实例化一个server对象,并且指定端口号,然后用server对象调用accept()方法监听客户端。一旦我们登录URL页面,服务端与客户端都会实例化一个socket进行网络之间的连接,浏览器会给服务器发送一个请求,服务器启动一个线程来解析请求,首先要获取输入流来读取客户端发送过来的内容,请求包括空格请求行、消息头、消息正文,请求行又根据空格拆分为请求方式、抽象路径和协议版本号。解析消息头,根据冒号空格拆分为消息头和消息头对应的值存放到Map中,提供根据消息头名获取对应的值的get方法。解析消息正文。
解析完请求后,再处理请求,根据抽象路径找到对应的Servlet来处理请求,还要判断是否为空请求,如果为空请求就要报空请求异常。c
响应客户端,包括发送状态行、发送响应头和发送响应正文,发送状态行需要获取输出流并指定字符集ISO8859-1,发送响应头需要根据正文实体文件的后缀名获取对应的值来说明正文类型。响应正文也是二进制数据,用于将客户端请求的资源等信息发送回给客户端。该正文具体表示的介质类型以及占用的字节长度会在响应头中有所描述。
com.webserver.core
WebServer.java
- Web容器用于管理所有部署在这里的webapp(网络应用)。
- 每个webapp包含:页面,素材,业务代码等。
- 我们俗称的一个网站实际上就是运行在web容器中的一个网络应用。
- Web容器的另一个作用是维持与客户端(浏览器)的连接与基于HTTP协议的交互操作,
- 可以根据用户请求来调用各webapp的内容并处理后予以响应。
package com.webserver.core;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class WebServer {
private ServerSocket server;
private ExecutorService threadPool;
/**
* 构造方法,用于初始化服务端
*/
public WebServer() {
try {
System.out.println("正在启动服务端...");
server = new ServerSocket(8088);
threadPool = Executors.newFixedThreadPool(50);
System.out.println("服务端启动完毕!");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 服务端开始工作的方法
*/
public void start() {
try {
while(true) {
System.out.println("等待客户端连接...");
Socket socket = server.accept();
System.out.println("一个客户端连接了!");
// 启动一个线程来处理客户端交互
ClientHandler handler = new ClientHandler(socket);
threadPool.execute(handler);
}
} catch (Exception e) {
e.printStackTrace