TCP/IP协议
TCP/IP族
互联网进行通信时,需要相应的网络协议,TCP/IP 原本就是为使用互联网而开发制定的协议族。
因此,互联网的协议就是 TCP/IP,TCP/IP 就是互联网的协议。
TCP/IP协议分层模型
OSI 参考模型注重 “通信协议必要的功能是什么”
而 TCP/IP模型 则更强调“在计算机上实现协议应该开发哪种程序”。
OSI七层模型
1、物理层:负责0、1比特流与电压的高低,光的闪灭之间的互换;双绞线,无线,光纤
2、数据链路层:负责物理层面上的互连、节点间的通信传输;以太网,无线网,PPP
3、网络层:负责寻址和路由选择;ARP,ICMP、IPv4,IPv6,IPsec
4、传输层:起可靠传输作用,只在通信双方节点进行处理;TCP,UDP
5、会话层:负责建立和断开通信连接
6、表示层:负责数据格式的转换
7、应用层:提供应用程序通信细节;Telnet,SSH,HTTP,SMTP,SSL/TLS,FTP,SNMP
TCP/IP四层模型
1、应用层:对应OSI参考模型的高层,提供各种服务
2、传输层:为应用层实体提供端到端的通信功能,保证了数据包的顺序传送及数据的完整性
3、网际互联层:主要解决主机到主机的通信问题 ,有三个主要协议:
- 网际协议(IP)
- 互联网组管理协议(IGMP)
- 互联网控制报文协议(ICMP)
4、网络接入层: 负责监视数据在主机和网络之间的交换
三次握手
第一次握手:
客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN(c)
此时客户端处于 SYN_Send
状态
第二次握手:
服务器收到客户端的 SYN 报文之后,以自己的 SYN 报文作为应答,并指定自己的 ISN(s)
同时会把客户端的 ISN + 1 作为 ACK 的值,表示自己已经收到了客户端的 SYN
此时服务器处于 SYN_REVD
的状态
第三次握手:
客户端收到 SYN 报文之后,会发送一个 ACK 报文
把服务器的 ISN + 1 作为 ACK 的值表示已经收到了服务端的 SYN 报文
此时客户端处于 establised
状态
服务器收到 ACK 报文之后,也处于 establised 状态,此时,双方已建立起了链接
三次握手的作用
确认双方的接受能力、发送能力是否正常
指定自己的初始化序列号,为后面的可靠传送做准备
如果是 HTTPS 协议的话,三次握手这个过程,还会进行数字证书的验证以及加密密钥的生成
四次挥手
断开连接时会进行四次挥手
第一次挥手:
客户端发送一个 FIN 报文,报文中会指定一个序列号
此时客户端处于 FIN_WAIT1 状态。
第二次挥手:
服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值
表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态
第三次挥手:
如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
第四次挥手:
客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答 且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。
需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态。
服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
TCP-11种网络状态
含义 | 状态 |
---|---|
LISTEN | 侦听来自客户端的TCP端口连接请求 |
SYN-SENT | 在发送连接请求后等待匹配的连接请求 |
SYN-RECEIVED | 在收到和发送一个连接请求后等待对连接请求的确认 |
ESTABLISHED | 代表一个打开的连接,数据可以传送给用户 |
FIN-WAIT-1 | 等待远程TCP的连接中断请求,或先前的连接中断请求确认 |
FIN-WAIT-2 | 从远程TCP等待连接中断请求 |
CLOSE-WAIT | 等待从本地用户发来的连接中断 |
CLOSING | 等待远程TCP对连接中断的确认 |
LAST-ACK | 等待原来发向远程TCP的连接中断请求的确认 |
TIME-WAIT | 等待足够的时间以确保远程TCP接收到连接中断请求的确认 |
CLOSED | 没有任何连接状态 |
问题汇总
三次握手过程中可以携带数据吗?
很多人可能会认为三次握手都不能携带数据,其实第三次握手的时候,是可以携带数据的。
也就是说,第一次、第二次握手不可以携带数据,而第三次握手是可以携带数据的。
为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。
因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。
也就是说,第一次握手可以放数据的话,其中一个简单的原因就是会让服务器更加容易受到攻击了。
而对于第三次的话,此时客户端已经处于 established 状态,也就是说,对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据没啥毛病。
什么是半连接队列?
服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。
当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
SYN-ACK 重传次数的问题
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。
注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s
TIME_WAIT 状态
什么场景下会出现大量TIME_WAIT ?
TCP 连接中,主动发起关闭连接 的一端,会进入 TIME_WAIT 状态
高并发场景下会产生大量的TIME_WAIT(如端口扫描器),TIME_WAIT 连接存在,属于正常现象
TIME_WAIT 多少算多,会有什么影响 ?
每一个TIME_WAIT状态会占用一个本地端口,上限为65535
当大量的连接处于 TIME_WAIT 时,新建立 TCP 连接会出错,address already in use : connect 异常
如何优化TIME_WAIT问题?
使用快速回收和连接复用
查看各个状态的连接数量
netstat -an | awk '/^tcp/ {++State[$NF]}END{for(key in State)print key "\t" State[key]}'
#LISTEN 14
#ESTABLISHED 9
优化Linux网络内核参数
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1
#默认开启, 记录标记时间戳(开启tw_recylce和tw_reuse一定需要timestamps的支持)
tcp_tw_recycle 是怎么工作的 ?
回收(recycle),最快时间回收
如果开启了 tcp_tw_recycle, 则内核会记住客户端上次发来数据包的时间戳
如果发来的数据包时间戳小于内核记录的最后发来的数据包时间戳,那么将会丢弃此数据包
这种情况在 NAT 模式下多机器时间滞后或同时发送, 会有很大危险, 会造成难以排查的异常情况
tcp_tw_reuse 是怎么工作的 ?
复用(reuse),不改变 TIME_WAIT 状态
如果开启了 tcp_tw_reuse, 如果客户端发来的时间戳大于先前连接内核记录的最新时间戳
则 Linux 将重新使用状态中的现有连接以 TIME-WAIT 用于新的对外请求连接,
状态中的传出连接 TIME-WAIT可在仅一秒之后重复使用
TIME_WAIT 必须快速回收处理吗 ?
不必须,存在TIME_WAIT状态是正常现象,只有影响到建立新连接的情况下才需优化
实际上TIME_WAIT是TCP为了解决复杂的网络问题提出的一种解决方案
TIME_WAIT 是为了保证全双工的 TCP 连接正常终止和保证网络中迷失的数据包正常过期
更深入的请看这篇文章:https://juejin.cn/post/7018785895515947039
除了快速回收和连接复用,还有什么方法可以解决TIME_WAIT 的问题?
1)客户端,HTTP 请求的头部,connection 设置为 keep-alive,保持存活一段时间
net.ipv4.tcp_keepalive_time = 1200
#表示当keepalive起用的时候,TCP发送keepalive消息的频度;缺省是2小时,改为20分钟
2)服务端,缩减 TIME_WAIT 时间,net.ipv4.tcp_fin_timeout = 30
close_wait过多原因和解决方案
close_wait是出现在服务端的一个状态,用来关闭服务端连接的
当close_wait过多时服务端的线程可能被阻塞,没法及时发起关闭动作
解决方案
1 业务上使用完socket主动关闭
2 监控tcp连接,如果长时间没有活动,则主动关闭
3 程序上当socket读数据时,长度为0,则主动关闭
为什么客户端发送 ACK 之后不直接关闭,而是要等一阵子才关闭?
因为要确保服务器是否已经收到了我们的 ACK 报文,如果没有收到的话,服务器会重新发 FIN 报文给客户端。客户端再次收到 ACK 报文之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK 报文。
至于 TIME_WAIT 持续的时间至少是一个报文的来回时间。 一般会设置一个计时,如果过了这个计时没有再次收到 FIN 报文,则代表对方成功,就是 ACK 报文,此时处于 CLOSED 状态