HTTP协议
HTTP协议 超文本传输协议 由万维网制定(w3c)
是浏览器与服务器通讯的应用层协议,规定了浏览器与服务器之间的交互规则以及交互数据的
格式信息等。
HTTP协议对应客户端与服务端之间的交互规则有以下定义:
要求浏览器与服务端之间必须遵循一问一答的规则,即:浏览器与服务端建立TCP连接后需要
先发送一个请求(问)然后服务端接收到请求并予以处理后再发送响应(答)。注意,服务端永远
不会主动给浏览器发送信息。
HTTP要求浏览器与服务端的传输层协议必须是可靠的传输,因此是使用TCP协议作为传输层
协议的。
HTTP协议对于浏览器与服务器之间交互的数据格式,内容也有一定的要求。
浏览器给服务端发送的内容称为请求 Request
服务端给浏览器发送的内容称为响应 Response
请求和响应中大部分内容 都是文本信息(字符串)。并且这些文本数据使用的字符集为:
ISO8859-1.这是一个欧洲的字符集 ,里面是不支持中文的!!而实际上请求和响应出现
的字符也就是英文、数字、符号。
请求Request
请求是浏览器发送给服务端的内容,HTTP协议中一个请求由三部分构成:
分别是:请求行,消息头,消息正文。 消息正文部分可以没有。
1:请求行
请求行是一行字符串,以连续的两个字符(回车符和换行符)作为结束这一行的标志。
回车符:在ASC编码中2进制内容对应的整数是13.回车符通常用cr表示。
换行符:在ASC编码中2进制内容对应的整数是10.换行符通常用lf表示。
回车符和换行符实际上都是不可见字符。
请求行分为三部分:
请求方式(SP)抽象路径(SP)协议版本(CRLF) 注:SP是空格
GET /index.html HTTP/1.1
2:消息头
消息头是浏览器可以给服务端发送的一些附加信息,有的用来说明浏览器自身内容,有的用来告知服务端交互细节,有的告知服务端消息正文详情等。
消息头由若干个组成,每行结束也是以CRLF标志。
每个消息头的格式为:消息头的名字(:SP)消息的值(CRLF)
消息头部分结束是以单独的(CRLF)标志。
例如:
GET /index.html HTTP/1.1
Host: localhost:8088
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Sec-Fetch-User: ?1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
一、第一版
WebServer Web容器,模拟Tomcat的基础功能
Web容器的主要职责是
1:管理下面所有部署的webapp(web应用)每一个web应用都会包含自己的网页,资源,对应的业务代码(java代码,处理逻辑的)因此一个web应用可以看作是一个“网站”
2:与客户端(通常就是浏览器)维持TCP的连接以及基于HTTP协议的交互,使得浏览器可以通过网络访问某个web应用的功能。
public class WebServer {
private ServerSocket server;
public WebServer(){
try{
System.out.println("正在启动服务端...");
server = new ServerSocket(8088);
System.out.println("启动服务端完毕!");
}catch(Exception e){
e.printStackTrace();
}
}
public void start(){
try {
System.out.println("等待客户端连接...");
Socket socket = server.accept();
System.out.println("一个客户端连接了!");
InputStream in = socket.getInputStream();
int d=0;
while((d= in.read())!=-1){
System.out.println((char)d);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
WebServer server = new WebServer();
server.start();
}
}
二、第二版
本版本主要测试从请求中读取一行字符串的操作
一个请求中的请求行和消息头有一个共同点,都是以CRLF结尾的一行字符串。因此实现了
读取一行字符串的操作后就可以复用他来完成解析请求行和消息头的工作。
由于服务端要处理多客户端,所以仍然使用线程处理客户端的交互:ClientHandler。
实现:
1:在com.webserver.core包下新建类:ClientHandler
2:在WebServer中接收客户端连接后启动线程
3:在ClientHandler中完成测试读取一行字符串的操作
三、第三版
本版本进行对请求的解析工作
思路:
设计一个类:HttpRequet,使这个类的每一个实例表示浏览器发送过来的一个具体请求内容
这个类中设计若干属性,分别表示一个请求中的各项信息。这样一来当我们读取一个请求内容
后用一个HttpRequest表示后,可以通过获取该对象的属性值来获取对应的信息便于后续处理
请求的工作。
实现:
1:创建一个新的包:com.webserver.http
在这个包中保存所有和HTTP有关的类
2:在http包中创建HttpRequest 即:请求对象
3:完成HttpRequest的功能
4:在ClientHandler中第一个环节解析请求时实例化HttpRequest,完成解析请求。
四、第四版
完成ClientHandler处理请求的环节
用户在浏览器上输入地址如:
http://localhost:8008/myweb/index.html
希望请求对应的页面,处理环节在作用就是根据用户请求的地址寻找这个文件,然后根据是否
找的做分支处理,以便后续第三步响应客户端时的操作。
实现:
1:在项目目录下新建一个目录webapps,用这个目录存所有部署的网络应用。每个网络应用以
一个子目录形式保存,并且目录名就是这个应用的名字。
2:在webapps目录下新建第一个子目录myweb作为我们第一个网络应用。
3:在myweb目录下新建高应用中的第一个页面:index.html
4:用户在浏览器请求如下路径:
http://localhost:8088/myweb/index.html
ClientHandler添加第二步处理请求的工作
4.1首先从request对象中获取请求的抽象路径,
即:uri属性的值。该值应当为:/myweb/index.html
4.2根据这个值创建一个File从"./webapps"目录下相对该uri的值找到对应文件
并通过判断该文件是否存在定义好分支。
五、第五版
此版本开始完成响应客户端的工作
上一个版本中我们已经在ClientHandler中添加分支,这里先完成资源已找到的响应过程。
此时我们应当将该资源已一个标准的HTTP响应格式发送给客户端使其展示该资源。
实现:
在ClientHandler的找到资源的分支中,通过socket获取输出流,发送一个响应并测试资源
存在的情况浏览器能否正确显示。
六、第六版
此版本继续开始完成响应客户端的工作
上一个版本中我们已经将用户请求的资源响应了,本版本我们来实现如果资源不存在时响应
404页面的工作。
实际上当用户请求的资源不存在,那么服务端发送的响应中状态代码为404,用来告知浏览器
请求的路径无效。并可以同时响应一个404页面进行展示。
实现:
1:在webaaps目录下新建一个目录root
2:在root目录下新建页面:404.html
转让给页面中只需要居中显示一行字:404!资源不存在!
注:该页面没有放在myweb目录下,而是放在root目录下的原因是无论用户将来请求哪个
网络应用下的资源都可能存在着资源没有找到的情况,因此响应404这个页面是一个
通用操作,所有将来把所有通用的页面放在root下共用即可。
3:在ClientHandler处理请求环节中资源不存在的分支里,响应404.
状态行内容为;HTTP/1.1 404 NOT FOUND
响应头不变,还是Content-type与Content-Length,注意,长度应当是404页面的长度
响应正文则是将404页面的内容发送到客户端。
启动程序后做如下测试:
http://localhost:8088/myweb/indes.html 注:index1.html在myweb目录下没有
此时应当能看到404页面显示在浏览器中