HTTP 是一种 超文本传输协议(Hypertext Transfer Protocol)
,HTTP 是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范
HTTP 主要内容分为三部分,超文本(Hypertext)、传输(Transfer)、协议(Protocol)。
-
超文本就是不单单只是本文,它还可以传输图片、音频、视频,甚至点击文字或图片能够进行
超链接
的跳转。 -
上面这些概念可以统称为数据,传输就是数据需要经过一系列的物理介质从一个端系统传送到另外一个端系统的过程。通常我们把传输数据包的一方称为
请求方
,把接到二进制数据包的一方称为应答方
。 -
而协议指的就是是网络中(包括互联网)传递、管理信息的一些规范。如同人与人之间相互交流是需要遵循一定的规矩一样,计算机之间的相互通信需要共同遵守一定的规则,这些规则就称为协议,只不过是网络协议。
说到 HTTP,不得不提的就是 TCP/IP 网络模型,一般是五层模型。如下图所示
还有一种就是 OSI 七层网络模型,它就是在五层协议之上加了表示层和会话层
而 HTTPS 的全称是 Hypertext Transfer Protocol Secure
,从名称我们可以看出 HTTPS 要比 HTTPS 多了 secure 安全性这个概念,实际上, HTTPS 并不是一个新的应用层协议,它其实就是 HTTP + TLS/SSL 协议组合而成,而安全性的保证正是 TLS/SSL 所做的工作。
也就是说,HTTPS 就是身披了一层 SSL 的 HTTP。
那么,HTTP 和 HTTPS 的主要区别是什么呢?
-
最简单的,HTTP 在地址栏上的协议是以
http://
开头,而 HTTPS 在地址栏上的协议是以https://
开头
http://www.cxuanblog.com/
https://www.cxuanblog.com/
-
HTTP 是未经安全加密的协议,它的传输过程容易被攻击者监听、数据容易被窃取、发送方和接收方容易被伪造;而 HTTPS 是安全的协议,它通过 密钥交换算法 - 签名算法 - 对称加密算法 - 摘要算法 能够解决上面这些问题。
HTTP Get 和 Post 区别?
-
get 方法是不安全的,因为你在发送请求的过程中,你的请求参数会拼在 URL 后面,从而导致容易被攻击者窃取,对你的信息造成破坏和伪造;
post 方法是把参数放在请求体 body 中的,这对用户来说不可见。
-
get 请求的 URL 有长度限制,而 post 请求会把参数和值放在消息体中,对数据长度没有要求。
Get
方法的含义是请求从服务器获取资源,这个资源可以是静态的文本、页面、图片视频等。比如,你打开我的文章,浏览器就会发送 GET 请求给服务器,服务器就会返回文章的所有文字及资源。
GET 请求而
POST
方法则是相反操作,它向URI
指定的资源提交数据,数据就放在报文的 body 里。
http请求常见的字段?
Host 客户端发送请求时,用来指定服务器的域名。
Connection
字段最常用于客户端要求服务器使用 TCP 持久连接,以便其他请求复用。
HTTP/1.1 版本的默认连接都是持久连接,但为了兼容老版本的 HTTP,需要指定 Connection
首部字段的值为 Keep-Alive
。
Connection: keep-alive
Content-Type 字段
Content-Type
字段用于服务器回应时,告诉客户端,本次数据是什么格式。
Content-Type: text/html; charset=utf-8
Content-Encoding 字段
Content-Encoding
字段说明数据的压缩方法。表示服务器返回的数据使用了什么压缩格式
Content-Length 字段
服务器在返回数据时,会有 Content-Length
字段,表明本次回应的数据长度。
User-agent http客户端程序信息
TCP 和 UDP 的不同点,方便理解,方便记忆。
tcp全连接 和 半连接
实际上,丢弃连接只是 Linux 的默认行为,我们还可以选择向客户端发送 RST 复位报文,告诉客户端连接已经建立失败。
tcp_abort_on_overflow 共有两个值分别是 0 和 1,其分别表示:
-
0 :表示如果全连接队列满了,那么 server 扔掉 client 发过来的 ack ;
-
1 :表示如果全连接队列满了,那么 server 发送一个
reset
包给 client,表示废掉这个握手过程和这个连接; -
如何增大 TCP 全连接队列呢?
-
TCP 全连接队列足最大值取决于 somaxconn 和 backlog 之间的最小值,也就是 min(somaxconn, backlog)。从下面的 Linux 内核代码可以得知:
-
somaxconn
是 Linux 内核的参数,默认值是 128,可以通过/proc/sys/net/core/somaxconn
来设置其值; -
backlog
是listen(int sockfd, int backlog)
函数中的 backlog 大小,Nginx 默认值是 511,可以通过修改配置文件设置其长度;可以执行
ss
命令查看:
如何模拟 TCP 半连接队列溢出场景?
模拟 TCP 半连接溢出场景不难,实际上就是对服务端一直发送 TCP SYN 包,但是不回第三次握手 ACK,这样就会使得服务端有大量的处于 SYN_RECV
状态的 TCP 连接。
这其实也就是所谓的 SYN 洪泛、SYN 攻击、DDos 攻击。
下命令计算当前 TCP 半连接队列长度:
如何防御 SYN 攻击?
这里给出几种防御 SYN 攻击的方法:
-
增大半连接队列;
-
开启 tcp_syncookies 功能
-
减少 SYN+ACK 重传次数
方式一:增大半连接队列
在前面源码和实验中,得知要想增大半连接队列,我们得知不能只单纯增大 tcp_max_syn_backlog 的值,还需一同增大 somaxconn 和 backlog,也就是增大全连接队列。否则,只单纯增大 tcp_max_syn_backlog 是无效的。
增大 tcp_max_syn_backlog 和 somaxconn 的方法是修改 Linux 内核参数:
增大 backlog 的方式,每个 Web 服务都不同,比如 Nginx 增大 backlog 的方法如下:
最后,改变了如上这些参数后,要重启 Nginx 服务,因为半连接队列和全连接队列都是在 listen() 初始化的。
方式二:
tcp_syncookies
的方式可以应对 SYN 攻击的方法:
net.ipv4.tcp_syncookies = 1
tcp_syncookies 应对 SYN 攻击
-
当 「 SYN 队列」满之后,后续服务器收到 SYN 包,不进入「 SYN 队列」;
-
计算出一个
cookie
值,再以 SYN + ACK 中的「序列号」返回客户端, -
服务端接收到客户端的应答报文时,服务器会检查这个 ACK 包的合法性。如果合法,直接放入到「 Accept 队列」。
-
最后应用通过调用
accpet()
socket 接口,从「 Accept 队列」取出的连接。
方式三:减少 SYN+ACK 重传次数
当服务端受到 SYN 攻击时,就会有大量处于 SYN_REVC 状态的 TCP 连接,处于这个状态的 TCP 会重传 SYN+ACK ,当重传超过次数达到上限后,就会断开连接。
那么针对 SYN 攻击的场景,我们可以减少 SYN+ACK 的重传次数,以加快处于 SYN_REVC 状态的 TCP 连接断开。
TCP 连接断开
TCP 四次挥手过程和状态变迁
天下没有不散的宴席,对于 TCP 连接也是这样, TCP 断开连接是通过四次挥手方式。
双方都可以主动断开连接,断开连接后主机中的「资源」将被释放。
客户端主动关闭连接 —— TCP 四次挥手
-
客户端打算关闭连接,此时会发送一个 TCP 首部
FIN
标志位被置为1
的报文,也即FIN
报文,之后客户端进入FIN_WAIT_1
状态。 -
服务端收到该报文后,就向客户端发送
ACK
应答报文,接着服务端进入CLOSED_WAIT
状态。 -
客户端收到服务端的
ACK
应答报文后,之后进入FIN_WAIT_2
状态。 -
等待服务端处理完数据后,也向客户端发送
FIN
报文,之后服务端进入LAST_ACK
状态。 -
客户端收到服务端的
FIN
报文后,回一个ACK
应答报文,之后进入TIME_WAIT
状态 -
服务器收到了
ACK
应答报文后,就进入了CLOSE
状态,至此服务端已经完成连接的关闭。 -
客户端在经过
2MSL
一段时间后,自动进入CLOSE
状态,至此客户端也完成连接的关闭。每个方向都需要一个 FIN 和一个 ACK,因此通常被称为四次挥手。
这里一点需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。
TIME_WAIT 等待 2 倍的 MSL,比较合理的解释是:网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以报文一来一回需要等待 2 倍的时间。
比如,如果被动关闭方没有收到断开连接的最后的 ACK 报文,就会触发超时重发 Fin 报文,另一方接收到 FIN 后,会重发 ACK 给被动关闭方, 一来一去正好 2 个 MSL。
2MSL
的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时。在 Linux 系统里
2MSL
默认是60
秒,那么一个MSL
也就是30
秒。Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒。
为什么需要 TIME_WAIT 状态?
主动发起关闭连接的一方,才会有 TIME-WAIT
状态。
需要 TIME-WAIT 状态,主要是两个原因:
-
防止具有相同「四元组」的「旧」数据包被收到;比如延迟的数据包。
-
保证「被动关闭连接」的一方能被正确的关闭,即保证最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭;
原因一:防止旧连接的数据包
假设 TIME-WAIT 没有等待时间或时间过短,被延迟的数据包抵达后会发生什么呢?
接收到历史数据的异常
-
如上图黄色框框服务端在关闭连接之前发送的
SEQ = 301
报文,被网络延迟了。 -
这时有相同端口的 TCP 连接被复用后,被延迟的
SEQ = 301
抵达了客户端,那么客户端是有可能正常接收这个过期的报文,这就会产生数据错乱等严重的问题。
所以,TCP 就设计出了这么一个机制,经过 2MSL
这个时间,足以让两个方向上的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的。
原因二:保证连接正确关闭
常见命令:
如何查看服务端进程 accept 队列的长度?
可以通过 ss -ltn
命令查看:
-
Recv-Q:当前 accept 队列的大小,也就是当前已完成三次握手并等待服务端
accept()
的 TCP 连接; -
Send-Q:accept 队列最大长度,上面的输出结果说明监听 8088 端口的 TCP 服务,accept 队列的最大长度为 128;
如何查看由于 accept 连接队列已满,而被丢弃的连接?
当超过了 accept 连接队列,服务端则会丢掉后续进来的 TCP 连接,丢掉的 TCP 连接的个数会被统计起来,我们可以使用 netstat -s 命令来查看:
上面看到的 41150 times ,表示 accept 队列溢出的次数,注意这个是累计值。可以隔几秒钟执行下,如果这个数字一直在增加的话,说明 accept 连接队列偶尔满了。
tcp_abort_on_overflow 共有两个值分别是 0 和 1,其分别表示:
-
0 :如果 accept 队列满了,那么 server 扔掉 client 发过来的 ack ;
-
1 :如果 accept 队列满了,server 发送一个
RST
包给 client,表示废掉这个握手过程和这个连接;
我们可以通过该 netstat -s
命令给出的统计结果中, 可以得到由于半连接队列已满,引发的失败次数:
上面输出的数值是累计值,表示共有多少个 TCP 连接因为半连接队列溢出而被丢弃。隔几秒执行几次,如果有上升的趋势,说明当前存在半连接队列溢出的现象。
netstat -a 查看所有连接
netstat -at 查看tcp的连接
netstat -au 查看udp的连接
netstat -s 查看各个协议的数据
netstat -an | grep 8080 查看端口
ss -ltn