转载自:https://www.cnblogs.com/gtblog/p/13657643.html
HTTP协议
一次http请求的过程
用户输入url,浏览器本地解析url,如果在host文件中存有对应ip则访问对应ip,否则将域名交给DNS服务器,DNS服务器返回对应IP地址,应用层向ip地址发送http请求,然后是传输层TCP的三次握手确认连接,第一次是客户端向服务器发送syn,第二次是服务器发送syn和ack到客户端,第三次是客户端发送syn与ack确认,此时TCP握手成功,然后到网络层,通过ARP协议,使用ip解析出MAC地址,然后通过MAC地址在数据链路层传输数据,服务器接收到数据包后,由web服务器处理该请求,查找客户端请求的资源,并返回响应报文。
断开连接需要TCP4次挥手,任意一方可开始,机器1发送fin=1到机器2表示数据传输完毕,断开请求,机器2返回ack=1确认收到关闭请求,等待机器2的信息传送完毕后,机器2发送fin=1到机器1,此时机器2会有定时器等待机器1返回ack=1,如果没有返回,会重发fin=1,机器1返回ack=1确认关闭请求。
http与https的区别
https需要去CA申请证书
http运行在TCP之上,所有传输内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输内容都是加密的
http默认端口80,HTTPS默认端口443
GET和POST区别
GET方法是从服务器获取资源
POST方法是向指定URI提交数据,数据放在body中
GET请求的URL有长度限制,而POST请求数据会放在消息体中,没有长度限制
GET请求会被浏览器主动cache,而post不会
GET请求在发送过程中产生一个TCP数据包,POST请求会产生两个数据包。对于GET请求,浏览器会将header和data一起发送,服务器返回响应,而POST请求,浏览器是先发送header,服务器响应100 continue,浏览器再发送data。
TCP协议
TCP三次握手与四次挥手,本文第一条
如何可以绕过三次握手?
Linux3.7内核版本之后,提供了TCP Fast Open功能,可以减少TCP连接建立的时延
客户端发送 SYN 报文,该报文包含 Fast Open 选项,且该选项的 Cookie 为空,这表明客户端请求 Fast Open Cookie;
支持 TCP Fast Open 的服务器生成 Cookie,并将其置于 SYN-ACK 数据包中的 Fast Open 选项以发回客户端;
客户端收到 SYN-ACK 后,本地缓存 Fast Open 选项中的 Cookie。
所以第一次发起请求时,还是需要正常的三次握手,之后如果再次向服务器建立连接的过程:
客户端发送 SYN 报文,该报文包含「数据」(对于非 TFO 的普通 TCP 握手过程,SYN 报文中不包含「数据」)以及此前记录的 Cookie;
支持 TCP Fast Open 的服务器会对收到 Cookie 进行校验:如果 Cookie 有效,服务器将在 SYN-ACK 报文中对 SYN 和「数据」进行确认,服务器随后将「数据」递送至相应的应用程序;如果 Cookie 无效,服务器将丢弃 SYN 报文中包含的「数据」,且其随后发出的 SYN-ACK 报文将只确认 SYN 的对应序列号;
如果服务器接受了 SYN 报文中的「数据」,服务器可在握手完成之前发送「数据」,这就减少了握手带来的 1 个 RTT 的时间消耗;
客户端将发送 ACK 确认服务器发回的 SYN 以及「数据」,但如果客户端在初始的 SYN 报文中发送的「数据」没有被确认,则客户端将重新发送「数据」;
此后的 TCP 连接的数据传输过程和非 TFO 的正常情况一致
之后发起 HTTP GET 请求的时候,可以绕过三次握手,这就减少了握手带来的 1 个 RTT 的时间消耗
/proc/sys/net/ipv4/tcp_fastopen
0 关闭
1 作为客户端使用 Fast Open 功能
2 作为服务端使用 Fast Open 功能
3 无论作为客户端还是服务器,都可以使用 Fast Open 功能
UDP与TCP的区别
TCP半连接队列和全连接队列
TCP三次握手的时候,Linux内核会维护两个队列:
半连接队列,也称SYN队列
全连接队列,也称accept队列
服务端收到客户端发起的SYN请求后,内核会把该连接存储到半连接队列,并向客户端返回ACK,服务端收到第三次握手的ACK后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到accept队列,等待进程调用accept函数时把连接取出来.
不管是半连接还是全连接队列,都有最大长度限制,超过限制时,内核会直接丢弃,或返回RST包
全连接队列:
查看TCP全连接队列的大小:服务端使用ss命令
ss
-l 显示正在监听(listening)的socket
-n 不解析服务名称
-t 只显示tcp socket
在LISTEN状态时(ss -lnt),Recv-Q/Send-Q表示的含义如下:
Recv-Q:当前全连接队列的大小
Send-Q:当前全连接最大队列长度
非LISTEN状态时(ss -nt)
Recv-Q:已收到但未被应用进程读取的字节数
Send-Q:已发送但未收到确认的字节数
当服务端并发处理大量请求时,如果TCP全连接队列过小,就容易溢出。发生了全连接队列溢出时,后续的请求就会被丢弃,出现服务端请求数量上不去的现象
丢弃连接是Linux的默认行为,我们可以选择向客户端发送RST复位报文,告诉客户端连接建立失败。
/proc/sys/net/ipv4/tcp_abort_on_overflow
这个文件默认为0
0:表示如果全连接队列满了,就扔掉连接
1:表示如果全连接队列满了,就发送一个reset包给client,表示废除握手过程和连接
通常情况下,tcp_abort_on_overflow应该设置为0,更有利于应对突发流量
如何增大TCP全连接队列?
TCP全连接队列最大值取决于somaxconn和backlog的最小值
somaxconn 是 Linux 内核的参数,默认值是 128,可以通过 /proc/sys/net/core/somaxconn 来设置其值;
backlog 是 listen(int sockfd, int backlog) 函数中的 backlog 大小,Nginx 默认值是 511,可以通过修改配置文件设置其长度;
nginx中backlog设置成5000的demo:
设置完后需要重启
半连接队列:
服务端没有命令单独查看半连接队列,但可以抓住TCP半连接队列的特点,就是服务端处于SYN_RECV状态的TCP连接,就是在半连接队列中
因此,计算TCP半连接队列长度的命令如下:模拟半连接队列的溢出场景,实际上就是一直对服务端发送TCP SYN包,但是不回第三次握手的ACK,这样就会有大量的处于SYN_RECV状态的TCP连接,其实这就是所谓的SYN洪泛,SYN攻击,DDos攻击
netstat -natp | grep SYN_RECV | wc -l
半连接队列的溢出处理是怎么做的?
如果半连接队列满了,并且没有开启 tcp_syncookies,则会丢弃;
若全连接队列满了,且没有重传 SYN+ACK 包的连接请求多于 1 个,则会丢弃
如果没有开启 tcp_syncookies,并且 max_syn_backlog 减去 当前半连接队列长度小于 (max_syn_backlog >> 2),则会丢弃;
半连接队列最大值
当 max_syn_backlog > min(somaxconn, backlog) 时, 半连接队列最大值 max_qlen_log = min(somaxconn, backlog) * 2;
当 max_syn_backlog < min(somaxconn, backlog) 时, 半连接队列最大值 max_qlen_log = max_syn_backlog * 2;
syncookies
如果 SYN 半连接队列已满,开启 syncookies 功能就可以在不使用 SYN 半连接队列的情况下成功建立连接
syncookies 是这么做的:服务器根据当前状态计算出一个值,放在己方发出的 SYN+ACK 报文中发出,当客户端返回 ACK 报文时,取出该值验证,如果合法,就认为连接建立成功,如下图所示。
syncookies 参数主要有以下三个值:
0 值,表示关闭该功能;
1 值,表示仅当 SYN 半连接队列放不下时,再启用它;
2 值,表示无条件开启功能;
因此,应对SYN攻击时,只需要设置为1即可
防御SYN攻击的方法:
增大半连接队列
通过源码得知,如果想增大半连接队列,不能单纯增加tcp_max_syn_backlog,还需一同增大somaxconn和backlog,也就是增大全连接队列,增大tcp_max_syn_backlog和somaxconn的方法就是修改Linux内核参数:路径分别为:/proc/sys/net/ipv4/tcp_max_syn_backlog
/proc/sys/net/core/somaxconn
开启tcp_syncookies功能
路径为/proc/sys/net/ipv4/tcp_syncookies
减少SYN+ACK重传次数
当收到SYN攻击时,就会有大量处于SYN_RECV的TCP连接,服务端此时会重传SYN+ACK,当重传次数达到上限后,就会断开连接,因此,我们可以通过减少SYN+ACK的重传次数,加快连接断开来抵御攻击
/proc/sys/net/ipv4/tcp_synack_retries