11.HTTP
11.1.介绍
-
概念:Hyper Text Transfer Protocol 超文本传输协议
- HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”
-
传输协议:定义了客户端和服务器端通信时发送数据的格式
- 基于TCP/IP的高级协议
- 默认端口号:80(Https协议默认端口是443)
- 基于请求/响应模型的:一次请求对应一次响应
- 无状态的:每次请求之间相互独立,不能交互数据
-
历史版本:
- HTTP1.0:每一次请求响应都会建立新的连接
- HTTP1.1:复用连接
-
完整的HTTP事务
- 域名解析:在网络中,计算机只认识IP地址,不认识域名
- 浏览器搜索自身的DNS缓存,看是否有对应的条目,如果有且没有过期则解析到此结束。
- 如果没有找到对应的条目,那么Chrome会搜索操作系统自身的DNS缓存,如果找到且没过期则停止搜索解析到此结束.
- 如果在Windows系统的DNS缓存也没有找到,那么尝试读取hosts文件,看有没有该域名对应的IP地址,如果有则解析成功。
- 如果在hosts文件中也没有找到对应的条目,浏览器就会发起一个DNS的系统调用,就会向本地配置的首选DNS服务器发起域名解析请求,运营商的DNS服务器首先查找自身的缓存,找到对应的条目,且没有过期,则解析成功
- 发起TCP的三次握手(网络编程中已经介绍过)
- 拿到域名对应的IP地址后,User-Agent(一般是指浏览器)会以一个随机端口(1024 < 端口 < 65535)向服务器的WEB程序80端口发起TCP的连接请求。这个连接请求(原始的http请求经过TCP/IP4层模型的层层封包)到达服务器端后,进入到网卡,然后是进入到内核的TCP/IP协议栈,还有可能要经过Netfilter防火墙的过滤,最终到达WEB程序,建立了TCP/IP的连接。在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接
- 发起HTTP请求,向IP地址对应主机对应端口号发送http请求报文
- 在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。
- 在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。由于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”,要保持客户端程序的在线状态,需要不断地向服务器发起连接求。通常 的做法是即时不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道客户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。
- 进过TCP3次握手之后,浏览器发起了http的请求,使用http的GET方法,请求的URL是 / ,协议是HTTP/1.0。
- 服务器响应HTTP请求,其实质是将客户端发来的网络路径转换成服务器上的虚拟路径
- 服务器端WEB程序接收到http请求以后,就开始处理该请求,处理之后就返回给浏览器html文件。
- 浏览器解析HTML代码,并反复请求HTML代码中的资源
- 浏览器拿到index.html文件后,就开始解析其中的html代码,遇到js/css/image等静态资源时,就向服务器端去请求下载(会使用多线程下载,每个浏览器的线程数不一样),这个时候就用上keep-alive特性了,建立一次HTTP连接,可以请求多个资源,下载资源的顺序就是按照代码里的顺序,但是由于每个资源大小不一样,而浏览器又多线程请求请求资源,顺序并不一定是代码里面的顺序。
- 浏览器在请求静态资源时(在未过期的情况下),向服务器端发起一个http请求(询问自从上一次修改时间到现在有没有对资源进行修改),如果服务器端返回304状态码(告诉浏览器服务器端没有修改),那么浏览器会直接读取本地的该资源的缓存文件。
- 浏览器对页面进行渲染并呈现给用户
- 域名解析:在网络中,计算机只认识IP地址,不认识域名
11.2.HTTP请求报文
11.2.1.请求行
请求方式 | 请求url | 请求协议/版本 |
---|---|---|
GET | /login.html | HTTP/1.1 |
- 请求方式:
- HTTP协议常用的有2种
- get(如果没有设置,默认情况下请求方式都是get):
- 请求参数会在地址栏中显示,拼接在url中。会封装到请求行中(HTTP协议)。
- 请求参数大小是有限制的,url的参数部分最多拼接1kb。
- 不太安全。
- get主要用来获取数据。
- post:
- 请求参数不会再地址栏中显示。但是打开浏览器开发者模式可以看到。会封装在请求体中(HTTP协议)。
- 请求参数的大小没有限制。
- 较为安全。
- post主要用来提交数据。
- get(如果没有设置,默认情况下请求方式都是get):
- 其他请求方式:
- HEAD获取某个URI响应头信息,基本与GET相同但是不返回响应主体。
- PUT通过提供的URI获取到特定的内容主体,如果存在则修改内容,如果不存在则创建。
- DELETE通过URI删除指定内容
- TRACE返回接受到的请求,用来查看数据经过中间服务器时发生了哪些变动
- OPTIONS返回给定URL支持的所有HTTP方法
- CONNECT要求使用SSL和TLS进行TCP通信
- PATCH请求修改局部数据
- HTTP协议常用的有2种
11.2.2.请求头:
- 客户端浏览器告诉服务器一些信息
- 格式:
请求头名称: 请求头值
(即键值对形式)
11.2.3.请求空行
- 空行,就是用于分割POST请求的请求头,和请求体的。
11.2.4.请求体(正文):
- 封装POST请求消息的请求参数的
11.3.请求信息字符串格式
- Example:
POST /login.html HTTP/1.1 //请求行,分为三部分,分别为,请求方式,请求资源,请求协议/版本
Host: localhost //请求头,有若干个
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost/login.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1
//在请求头和请求体之间有一个空行,称为请求空行
username=zhangsan //请求体
//请求cskaoyan官网
GET http://www.cskaoyan.com/forum.php HTTP/1.1
Host: www.cskaoyan.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74
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
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
- HTTP请求的细节
- Accept:浏览器可接受的 MIME类型 / (大类型)/(小类型)
- MIME:互联网将各种资源进行了一个分类,比如分成文本类、音频类、视频类、图像类,每个类型下面又有很多的子类型,
- 如文本类型下面有html、txt
- 音频下面有mp3、flac
- 视频下面有mp4、mkv、avi
- 图片下面有jpg、png、bmp
- MIME用一个大类型/小类型的方式来表述互联网上面的任意的一个资源类型。
- 如最常见的text/html;image/png;image/jpg;
- MIME:互联网将各种资源进行了一个分类,比如分成文本类、音频类、视频类、图像类,每个类型下面又有很多的子类型,
- Accept-Charset: 浏览器通过这个头告诉服务器,它支持哪种字符集
- Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip
- Accept-Language: 浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。 可以在浏览器中进行设置。
- 其中的数字表示了某种语言的权值,1为最高,没有的话默认权值为1
- 语句之间用逗号分割
- Host:初始URL中的主机和端口
- Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面(防盗链:防止他人盗取链接,统计工作:统计访问的信息)
- Content-Type:内容类型
- If-Modified-Since: 与浏览器缓存相关,表示自从某个时间以来,服务器文件内容是否发生了修改。服务器利用这个头与服务器的文件进行比对,如果一致,则告诉浏览器从缓存中直接读取文件。
- User-Agent:浏览器类型。
- Content-Length:表示请求消息正文(只有请求体不为空的时候才会出现)的长度 (一般用于文件上传)
- Connection:表示是否需要持久连接。如果服务器看到这里的值为“Keep -Alive”,或者看到请求使用的是HTTP 1.1,HTTP 1.1默认进行持久连接
- Date:Date: Mon, 22 Aug 2011 01:55:39 GMT请求时间GMT
- Cookie:这是最重要的请求头信息之一 ,以后介绍
- Accept:浏览器可接受的 MIME类型 / (大类型)/(小类型)
11.3.HTTP响应报文
- 响应行
- 响应头
- 响应空行
- 响应体
- Example:
HTTP/1.1 200 OK //响应行,格式为HTTP版本号 状态码 原因叙述<CRLF> Server: Tengine //若干个响应头 Content-Type: text/html; charset=gbk Content-Length: 51527 Connection: keep-alive Date: Tue, 23 Feb 2021 12:11:44 GMT Set-Cookie: cZBD_2132_sid=Q6gG3J; expires=Wed, 24-Feb-2021 12:11:44 GMT; Max-Age=86400; path=/ Set-Cookie: cZBD_2132_lastact=1614082304%09forum.php%09; expires=Wed, 24-Feb-2021 12:11:44 GMT; Max-Age=86400; path=/ Vary: Accept-Encoding Ali-Swift-Global-Savetime: 1614082304 Via: cache31.l2cn3003[73,200-0,M], cache7.l2cn3003[74,0], kunlun6.cn1411[77,200-0,M], kunlun8.cn1411[78,0] X-Cache: MISS TCP_MISS dirn:-2:-2 X-Swift-SaveTime: Tue, 23 Feb 2021 12:11:44 GMT X-Swift-CacheTime: 0 Timing-Allow-Origin: * EagleId: af06eb9c16140823045625733e //响应空行,下边是响应体,主要是给浏览器一个html页面用于展示给用户 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=gbk" /> <title>王道论坛,专注于计算机考研的点点滴滴! .....省略后边
- HTTP响应的细节
- 响应行的原因叙述代码:
- 状态码用于表示服务器对请求的各种不同处理结果和状态,它是一个三位的十进制数。响应状态码分为5类,使用最高位为1到5来进行分类如下所示:
状态码 含义 200~299 表示成功接收请求并已完成整个处理过程 200 OK 206 300~399 (重定向)为完成请求,客户需进一步细化请求。例如,请求的资源已经移动一个新地址 400~499 客户端的请求有错误 500~599 服务器端出现错误 - 常用状态码
- 200(正常):表示一切正常,返回的是正常请求结果
- 206:表示分段的请求OK
- 301、302/307(临时重定向):指出被请求的文档已被临时移动到别处,此文档的新的URL在Location响应头中给出。(比如国外服务器跳转到国内)
- 304(未修改):表示客户机缓存的版本是最新的,客户机可以继续使用它,无需到服务器请求。
- 404(找不到):404服务器上不存在客户机所请求的资源。
- 400:服务器不支持这种请求方式
- 500(服务器内部错误):服务器端的程序发生错误
- HTTP响应的细节
- Location: 指示新的资源的位置
- Server: apache tomcat 指示服务器的类型
- Content-Encoding: gzip 服务器发送的数据采用的编码类型
- Content-Length: 80 告诉浏览器正文的长度
- Content-Language: zh-cn服务发送的文本的语言
- Content-Type: text/html; 服务器发送的内容的MIME类型
- Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT文件的最后修改时间
- Refresh: 指示客户端刷新频率。单位是秒
- Content-Disposition: attachment; filename=aaa.zip 指示客户端保存文件
- Set-Cookie: SS=Q0=5Lb_nQ; path=/search 服务器端发送的Cookie,和请求头Cookie一起使用
- Expires: 0:缓存相关,0指的是不缓存
- Cache-Control: no-cache (1.1) :缓存相关,no-cache指的是不缓存
- Connection: close/Keep-Alive :既可以放在请求里边,也可以放在响应里边
- Date: Tue, 11 Jul 2000 18:23:51 GMT :时间
11.4.HTTPS
- HTTPS = HTTP + Secure
- HTTP的缺点:
- 通讯过程完全是透明的,不加密
- 不验证通讯另一方的身份(卖茶叶的)
- 还没有完整性的校验,比如请求报文或者响应报文被修改,也不会发现。
- HTTPS相对于HTTP的改进:
- HTTPS采用加密算法来加密
- 对称加密:加密和解密使用的是同一把密钥。加密解密速度比较快
- 非对称加密:公钥进行加密,但是加密之后只可以用私钥来进行解密。速度比较慢
- 实际上,在真实的HTTPS通讯过程中,使用的是混合加密。
- 客户端向服务端发起一个请求,服务器返回一个公钥给客户端
- 客户端拿到公开密钥后,通过对称加密随机生成一个共享密钥,并使用公钥将此共享密钥进行非对称加密(也就是用私有密钥加密)
- 客户端将共享秘钥发送给服务端,服务端拿到加密后的共享密钥后,用私有密钥进行解密获取到客户端真实的共享密钥
- 客户端和服务端通过共享密钥加密需要传输的文本内容,后续与服务端交互就是采用共享密钥加密解密来完成
个人理解:实际上,这个混合加密方式,可以理解为,
- 首先,C(Client)向S(Sever)索要到了一把锁,而这把锁的钥匙只有S才有
- C拿到这把锁以后,又买了一把新锁,而这把新锁有两把钥匙,
- C买了个盒子,又买了一把新锁,并把新锁的其中一把钥匙放在了盒子里,用旧锁锁上,交给了S,自己留了一把新锁的钥匙
- S拿到旧锁,用自己的钥匙打开盒子上的旧锁,获得了C放在盒子里的,新锁的钥匙
- 从此以后,C和S就用新锁来传递信息
- HTTPS会验证通讯方的身份信息3
- 采用的是证书。
- HTTPS会进行完整性校验
- HTTPS采用加密算法来加密
11.5.HTTP Example
public class WebServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8090);
while (true){
Socket socket = serverSocket.accept();
//InputStream、OutputStream都是抽象基类,原本是不能实例化的
//实际上,getOutputStream()方法真正的返回类型是SocketOutputStream
//相当于多态,所以才能父类引用指向子类
InputStream cin = socket.getInputStream();
new Thread(()-> {
byte[] b = new byte[1024];
//byte[] b = new byte[10];
int len = 0 ;
//while(len!=-1) {
try {
len = cin.read(b);
} catch (IOException e) {
e.printStackTrace();
}
System.out.print(new String(b, 0, len));
//}
}).start();
cin.close();
}
}
}
输出结果如下:
GET / HTTP/1.1
Host: localhost:8090
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74
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
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: Idea-976a8fbc=b61a0e18-d64a-426c-8c22-0db2a6db5967
- 对于此程序,做出如下解读
- 所用方法:
- ServerSocket类的API:public Socket accept():侦听并接受到此套接字的连接。此方法在连接传入之前一直阻塞。
- Socket类的API:
- public InputStream getInputStream():返回此套接字的输入流。 底层是SocketInputStream
- public OutputStream getOutputStream():返回此套接字的输出流。 底层是SocketOutputStream
- SocketInputStream继承来的方法:
- public int read(byte[] b):从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。在某些输入可用之前,此方法将阻塞。
- 对于两个阻塞方法,如果不采用多线程形式,假设accept接收到了端口的访问,但是没有接收到请求报文,read方法阻塞,这个时候如果还有请求访问,是无法接收到的,所以应该采用多线程形式。
- 程序外层套一个while(true)循环,目的是为了持续监听端口请求,而在浏览器中输入localhost://8090,浏览器持续请求的过程中,while不断循环,而字节输入流不断地指向一个新的实例化的输入流对象,也就是说,其实在每次循环中,字节输入流读取只读取了一次,同时也只打印了一次。
- 这里,没有采用循环读取,为什么也能读取到完整的请求报文?
- 解释:因为byte数组足够大,可以一次读完所有请求报文(我们所测试的
localhost://8090
请求只有请求行和请求头,内容大概600字节左右,个人测试请求报文为601字节),所以当程序运行到if语句时,len的值为实际读到的字节数,因此直接输出即可。 - 注意IO流read方法的返回值问题,尤其注意,当剩余字符不够一次读取的时候,read方法究竟返回什么(IO流里已经介绍)
- 验证测试:将这个数组大小改为10,就可以看到控制台只打印了10个字符,证明确实没有循环读取,也可以打断点调试
- 解释:因为byte数组足够大,可以一次读完所有请求报文(我们所测试的
- 所用方法:
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/181056.html