声明: 1. 本文为我的个人复习总结, 并非那种从零基础开始普及知识 内容详细全面, 言辞官方的文章
2. 由于是个人总结, 所以用最精简的话语来写文章
3. 若有错误不当之处, 请指出
TCP 是面向连接
的、可靠
的、基于字节流
的传输层通信协议
面向连接:
⼀定是⼀对⼀
才能连接, 不能像 UDP 协议可以⼀个主机同时向多个主机发送消息
可靠:
序列号可以保证数据 不重
、 不漏
、有序
;
ack确认应答, ack=1000代表前999的数据全都接收到了
重传机制、滑动窗口、流量控制、拥塞控制 等机制
字节流:
消息是「没有边界」的
Socket + 序列号 + 窗口大小
用于保证可靠性
和流量控制
维护的某些状态信息
四元组(源地址 + 源端口 + 目的地址 +目的端口)
理论Max=客户端ip数(2^32) * 客户端端口数(2^16)
实际上要受到两个限制:
内存限制
每个 TCP 连接都要占用一定内存
文件描述符限制(新建 或 打开 的文件)
每个Socket都是一个文件
连接
TCP 是⾯向连接的传输层协议,传输数据前先要建⽴连接。
UDP 是不需要连接,即刻传输数据。
服务对象
TCP 是⼀对⼀的两点服务,即⼀条连接只有两个端点。
UDP ⽀持⼀对⼀、⼀对多、多对多的交互通信
可靠性
TCP 是可靠交付数据的,数据可以⽆差错、不丢失、不᯿复、按需到达。
UDP 是尽最⼤努⼒交付,不保证可靠交付数据。
⾸部开销
TCP ⾸部⻓度较⻓,会有⼀定的开销,⾸部在没有使⽤「选项」字段时是 20 个字节,如果使⽤了「选项」
字段则会变⻓的。
UDP ⾸部只有 8 个字节,并且是固定不变的,开销较⼩。
传输⽅式
TCP 是流式传输,没有边界,但保证顺序和可靠。
UDP 是⼀个包⼀个包的发送,是有边界的,但可能会丢包和乱序。
分⽚不同
TCP 的数据⼤⼩如果⼤于 MSS ⼤⼩,则会在传输层进⾏分⽚,⽬标主机收到后,也同样在传输层组装 TCP
数据包,如果中途丢失了⼀个分⽚,只需要传输丢失的这个分⽚。
UDP 的数据⼤⼩如果⼤于 MTU ⼤⼩,则会在 IP 层进⾏分⽚,⽬标主机收到后,在 IP 层组装完数据,接着
再传给传输层,但是如果中途丢了⼀个分⽚,在实现可靠传输的 UDP 时则就需要᯿传所有的数据包,这样
传输效率⾮常差,所以通常 UDP 的报⽂应该⼩于 MTU。
拥塞控制、流量控制
TCP 有拥塞控制和流ᰁ控制机制,保证数据传输的安全性。UDP 则没有,即使⽹络⾮常拥堵了,也不会影响 UDP 的发送速率。
TCP:
FTP ⽂件传输
HTTP / HTTPS
UDP:
TCP会粘包: 若连续几次需要send的数据都很少,通常TCP会根据Nagle优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据.
UDP不会粘包: 每个UDP段都是一条消息 不能一次提取任意字节的数据 不会使用块的合并优化算法, 每个UDP包中就有了消息头(消息来源地址,端口等信息), 有消息保护边界, 提取相应包的数据
该算法的思路是延时批处理, 要满足以下两个条件才进行发送
TCP 有可变⻓的「选项」字段,⽽ UDP 头部⻓度则是不会变化的,⽆需多⼀个字段去记录 UDP 的⾸部⻓
度。
UDP 「包⻓度」是冗余的, 是padding的
SYN队列, 完成了2次握手
Accept队列, 完成了3次握手
SYN=1
,序列号seq=x
。第一次握手前客户端的状态为CLOSE
,第一次握手后客户端的状态为SYN-SENT
。此时服务端的状态为LISTEN
。SYN=1
,ACK=1
,序列号seq=y
,确认号ack=x+1
。第二次握手前服务端的状态为LISTEN
,第二次握手后服务端的状态为SYN-RCVD
,此时客户端的状态为SYN-SENT
。(其中SYN=1
表示要和客户端建立一个连接,ACK=1
表示确认序号有效)ACK=1
,序列号seq=x+1
,确认号ack=y+1
。第三次握手前客户端的状态为SYN-SENT
,第三次握手后客户端和服务端的状态都为ESTABLISHED
。此时连接建立完成。为了数据的可靠传输
防止历史连接的建立, 减少不必要的资源开销
让双方同步初始化序列号
序列号能保证数据包进行 不重复、不缺少、按序 的传输
三次握手即可建立安全可靠的连接, 无需第四次握手
MTU :⼀个⽹络包的最⼤⻓度
MSS :除去 IP 和 TCP 头部之后,⼀个⽹络包所能容纳的 TCP 数据的最⼤⻓度;
粒度更细, 进⾏重发时也是以 MSS 为单位,⽽不⽤᯿传所有的分⽚,⼤⼤增加了重传的效率。
攻击者短时间伪造不同 IP 地址的 SYN 报⽂,服务端每接收到
⼀个 SYN 报⽂,就进⼊ SYN_RCVD 状态,但服务端发送出去的 ACK + SYN 报⽂,⽆法得到未知 IP 主机的
ACK 应答,久⽽久之就会占满服务端的 SYN 接收队列(未连接队列),使得服务器不能为正常⽤户服务
解决方案:
修改 Linux 内核参数,控制队列⼤⼩
和当队列满时应做什么处理
FIN=1,seq=u
),并停止再发送数据,主动关闭TCP连接,进入FIN-WAIT-1
(终止等待1)状态,等待B的确认。ACK=1,ack=u+1,seq=v
),B进入CLOSE-WAIT
(关闭等待)状态,此时的TCP处于半关闭状态,A到B的连接释放。FIN-WAIT-2
(终止等待2)状态,等待B发出的连接释放报文段。FIN=1,ACK=1,seq=w,ack=u+1
),B进入LAST-ACK
(最后确认)状态,等待A的确认。ACK=1,seq=u+1,ack=w+1
),A进入TIME-WAIT
(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL
(最大报文段生存时间)后,A才进入CLOSED
状态。B收到A发出的确认报文段后关闭连接,若没收到A发出的确认报文段,B就会重传连接释放报文段。最后一次的ACK, 是为了确保客户端收到了FIN
主动关闭连接的(客户端),才有 TIME_WAIT 状态
ACK 和 FIN ⼀般都会分开发送,从⽽⽐三次握⼿导致多了⼀次
关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
服务器收到客户端的 FIN 报⽂时,先回⼀个 ACK 应答报⽂,⽽服务端可能还有数据需要处理和发送,等
服务端不再发送数据时,才发送 FIN 报⽂给客户端来表示同意现在关闭连接
MSL 是报⽂最⼤⽣存时间
如果在 TIME-WAIT 时间内,因为客户端的 ACK没有传输到服务端,客户端⼜接收到了服务端᯿发的 FIN 报⽂,那么 2MSL 时间将重新计时
CLOSE_WAIT是被动关闭形成的,当客户端发送FIN报文,服务端返回ACK报文后进入CLOSE_WAIT。
TIME_WAIT是主动关闭形成的,当第四次挥手完成后,客户端进入TIME_WAIT状态。
TCP 有保活机制, 每隔⼀个时间间隔 发送⼀个探测报⽂
监听的 socket 和真正⽤来传送数据的 socket,是「两个」 socket
久久收不到ACK应答, 以时间作为驱动
超时重传时间 RTO 的值 是⼀个动态变化的值:
每当遇到⼀次超时重传的时候,说明网络可能比较拥堵, 都会将下⼀次超时时间间隔设为先前值的两倍
可能原因:
以数据驱动, 当收到三个相同的 ACK 报⽂时, 便会进行重传, 但不清楚丢的是哪个, 所以此批次的全部重传
通过SACK字段可以清楚具体是哪个数据包丢失了
告诉「发送⽅」有哪些数据被重复接收了(即数据包没丢, 而是ack丢了; 网络延时收到旧的数据包)
窗⼝⼤⼩就是指⽆需等待确认应答,⽽可以继续发送数据的最⼤值。
通常窗⼝的⼤⼩是由接收⽅的窗⼝⼤⼩来决定的
背压机制, 让「发送⽅」根据「接收⽅」的实际接收能⼒控制发送的数据量
发送窗⼝和接收窗⼝中所存放的字节数,都是放在操作系统内存缓冲区
中的,⽽操作系统的缓冲区,会被操作系统调整
如果发⽣了先减少缓存,再收缩窗⼝,就会出现丢包的现象
为了防⽌这种情况发⽣,TCP 规定是不允许同时减少缓存⼜收缩窗⼝的,⽽是采⽤先收缩窗⼝ 过段时间再减少缓存
即停止发送数据
之后会有窗⼝探测报⽂, 探测是否可以开启窗口
慢启动
拥塞避免
拥塞发⽣
快速恢复
快速重传时才有