TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。
cwnd
>= ssthresh
时,就会使用「拥塞避免算法」,TCP发送方会切换到拥塞避免模式,此时每次接收到一个ACK时,cwnd的大小只会增加一个MSS(最大报文段长度),而不是每次都加倍。这样可以避免发送过多的数据导致网络拥塞。ssthresh
和 cwnd
变化如下:
cwnd = cwnd/2
,也就是设置为原来的一半;ssthresh = cwnd
;ssthresh
设为 cwnd/2
,cwnd
重置为 1
cwnd
和 ssthresh
已被更新了,然后,进入快速恢复算法如下:
cwnd = ssthresh + 3
( 3 的意思是确认有 3 个数据包被收到了);连接 服务对象
可靠性 拥塞控制、流量控制
头部长度不同 分片不同
20
个字节,如果使用了「选项」字段则会变长的。在 TCP 三次握手的时候,Linux 内核会维护两个队列,分别是:
sock
加入到这个队列中,队列内的sock
都处于SYN_RECV
状态。sock
取出,放到全连接队列中。队列里的sock
都处于 ESTABLISHED
状态。这里面的连接,就等着服务端执行accept()后被取出了。socket
,新建一个socket对象
bind
,将 socket 绑定在指定的 IP 地址和端口;
listen
,进行监听,服务器端处于listen状态
socket
执行listen
时,内核都会自动创建一个半连接队列和全连接队列。accept
,等待客户端连接;accept 成功返回是在三次握手成功之后
accept方法
只是为了从全连接队列中拿出一条连接,本身跟三次握手几乎毫无关系。connect
,向服务端的地址和端口发起连接请求;connect 成功返回是在第二次握手
在两次握手的情况下,服务端没有中间状态给客户端来阻止历史连接,导致服务端可能建立一个历史连接,造成资源浪费。
TCP 协议的通信双方, 都必须维护一个「序列号」, 序列号是可靠传输的一个关键因素。一来一回,才能确保双方的初始序列号能被可靠的同步。
如果客户端发送的 SYN
报文在网络中阻塞了,重复发送多次 SYN
报文,那么服务端在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。
需要注意的是第三次握手是可以携带数据的,前两次握手是不可以携带数据的
tcp_syn_retries
内核参数决定;tcp_synack_retries
内核参数决定。那么当如果一个 IP 分片丢失,整个 IP 报文的所有分片都得重传,经过 TCP 层分片后,如果一个 TCP 分片丢失后,进行重发时也是以 MSS 为单位,而不用重传所有的分片,大大增加了重传的效率。
TCP协议中的ACK(Acknowledgment)报文通常不会重传。
ACK报文是TCP用于确认接收到数据的报文,用于确认对方发送的数据已经成功接收。在TCP的可靠传输机制中,发送方发送数据后,会等待接收方发送ACK报文进行确认。如果发送方在一定的超时时间内没有接收到ACK报文,则会认为数据没有成功送达,并进行重传(这里是对应报文不是ACK报文)。
在TCP协议中,数据的重传是由发送方负责的,而ACK报文的重传通常不会由接收方触发。一般情况下,接收方只需要发送ACK报文来确认已接收到数据,而不会因为没有接收到ACK报文而进行重传。接收方只有在需要通知发送方数据丢失或者需要进行流量控制等特殊情况下,才会发送特定的控制报文,如SACK(Selective Acknowledgment)报文或者窗口更新报文。
我们都知道 TCP 连接建立是需要三次握手,假设攻击者短时间伪造不同 IP 地址的 SYN
报文,服务端每接收到一个 SYN
报文,就进入SYN_RCVD
状态,但服务端发送出去的 ACK + SYN
报文,无法得到未知 IP 主机的 ACK
应答,久而久之就会占满服务端的半连接队列,使得服务端不能为正常用户服务。
避免 SYN 攻击方式,可以有以下四种方法:
FIN
标志位被置为 1
的报文,也即 FIN
报文,之后客户端进入 FIN_WAIT_1
状态。ACK
应答报文,接着服务端进入 CLOSE_WAIT
状态。ACK
应答报文后,之后进入 FIN_WAIT_2
状态。FIN
报文,之后服务端进入 LAST_ACK
状态。FIN
报文后,回一个 ACK
应答报文,之后进入 TIME_WAIT
状态ACK
应答报文后,就进入了 CLOSE
状态,至此服务端已经完成连接的关闭。2MSL
一段时间后,自动进入 CLOSE
状态,至此客户端也完成连接的关闭。你可以看到,每个方向都需要一个 FIN 和一个 ACK,因此通常被称为四次挥手。
这里一点需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。
tcp_orphan_retries
参数控制。2MSL
的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时。
TIME_WAIT 等待 2 倍的 MSL,比较合理的解释是: 网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待 2 倍的时间。
等待足够的时间以确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。
32768~61000
,也可以通过 net.ipv4.ip_local_port_range
参数指定范围。答案是可以
什么是 TCP 延迟确认机制?
当发送没有携带数据的 ACK,它的网络效率也是很低的,因为它也有 40 个字节的 IP 头 和 TCP 头,但却没有携带数据报文。 为了解决 ACK 传输效率低问题,所以就衍生出了 TCP 延迟确认。 TCP 延迟确认的策略:
当被动关闭方在 TCP 挥手过程中,如果「没有数据要发送」,同时「没有开启 TCP_QUICKACK(默认情况就是没有开启,没有开启 TCP_QUICKACK,等于就是在使用 TCP 延迟确认机制)」,那么第二和第三次挥手就会合并传输,这样就出现了三次挥手。
TCP 的 Keepalive 这东西其实就是 TCP 的保活机制
如果两端的 TCP 连接一直没有数据交互,达到了触发 TCP 保活机制的条件,那么内核里的 TCP 协议栈就会发送探测报文。
如果两端的 TCP 连接一直没有数据交互,达到了触发 TCP 保活机制的条件(默认是两个小时),那么内核里的 TCP 协议栈就会发送探测报文。
什么是粘包问题?
粘包的问题出现是因为不知道一个用户消息的边界在哪,如果知道了边界在哪,接收方就可以通过边界来划分出有效的用户消息。
一般有三种方式分包的方式:
总体来说分为以下几个过程:
TCP/IP 四层模型 是目前被广泛采用的一种模型,我们可以将 TCP / IP 模型看作是 OSI 七层模型的精简版本,由以下 4 层组成:
每一层的封装格式:
WebSocket 和 HTTP 协议都是基于 TCP 连接实现的,但是它们的长连接机制有一些区别。
HTTP 长连接通常使用 HTTP/1.1 中的 "keep-alive" 机制,在客户端和服务器之间完成一次请求/响应后,会继续使用已建立的 TCP 连接来发送和接收多个请求/响应。这种方式可以减少频繁地建立、断开 TCP 连接所带来的性能开销,从而提高网络传输效率。但是,使用 HTTP 长连接时,客户端和服务器之间的通信仍然需要按照请求-响应模式进行,且无法实现即时通信和实时数据传输。
WebSocket 的长连接是指在服务器和客户端之间建立一条持久的双向通信通道,该通道会一直保持打开状态,直到显示地关闭。这种连接不仅可以支持即时通信和实时数据传输,而且可以使得服务器可以主动向客户端推送消息,从而实现更高效的数据传输和通信。此外,WebSocket 还有一个心跳检测机制(即“ping/pong”),用于检测连接是否处于活动状态,并保证连接的稳定性。
综上所述,HTTP 的长连接主要是为了减少建立和断开连接所带来的性能开销,而 WebSocket 的长连接则是为了支持实时通信和实时数据传输,并保持连接的稳定性。此外,WebSocket 通过心跳检测机制可以更好地保护长连接,而 HTTP 长连接则没有这个机制。
TCP握手之后HTTPS需要四次握手建立连接。
1. ClientHello
首先,由客户端向服务器发起加密通信请求,也就是 ClientHello
请求。
在这一步,客户端主要向服务器发送以下信息:
(1)客户端支持的 TLS 协议版本,如 TLS 1.2 版本。
(2)客户端生产的随机数(Client Random
),后面用于生成「会话秘钥」条件之一。
(3)客户端支持的密码套件列表,如 RSA 加密算法。
2. SeverHello
服务器收到客户端请求后,向客户端发出响应,也就是 SeverHello
。服务器回应的内容有如下内容:
(1)确认 TLS 协议版本,如果浏览器不支持,则关闭加密通信。
(2)服务器生产的随机数(Server Random
),也是后面用于生产「会话秘钥」条件之一。
(3)确认的密码套件列表,如 RSA 加密算法。
(4)服务器的数字证书。
3.客户端回应
客户端收到服务器的回应之后,首先通过浏览器或者操作系统中的 CA 公钥,确认服务器的数字证书的真实性。
如果证书没有问题,客户端会从数字证书中取出服务器的公钥,然后使用它加密报文,向服务器发送如下信息:
(1)一个随机数(pre-master key
)。该随机数会被服务器公钥加密。
(2)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
(3)客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供服务端校验。
上面第一项的随机数是整个握手阶段的第三个随机数,会发给服务端,所以这个随机数客户端和服务端都是一样的。
服务器和客户端有了这三个随机数(Client Random、Server Random、pre-master key),接着就用双方协商的加密算法,各自生成本次通信的「会话秘钥」。
4. 服务器的最后回应
服务器收到客户端的第三个随机数(pre-master key
)之后,通过协商的加密算法,计算出本次通信的「会话秘钥」。
然后,向客户端发送最后的信息:
(1)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供客户端校验。
至此,整个 TLS 的握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的 HTTP 协议,只不过用「会话秘钥」加密内容。