跳转至

TCP


Tcp(Transmission Control Protocol)传输控制协议
在不可靠的底层分组交换网上提交可靠的流交互服务。

不可靠网络存在的问题

  • 分组丢失
  • 数据破坏
  • 乱序
  • 时延
  • 重复

上述问题需要应用层自行处理

可靠交付特征

  • 面向数据流

    以字节为单位,每次传输指定长度的报文段(Segment)

  • 虚电路链接

    先建立"链接"再发送数据

  • 有缓存的传输

    TCP协议栈缓存应用报文,自定义发送报文长度

  • 无结构的数据流

    报文内容按数据流(若干字节)形式发送

  • 全双工链接

    通信双方可以同时收发

  • 报文确认和重传

    每个报文都有序列号,接受端需要发送确认消息 如果发送端在合理的往返时延(RTT)内未收到确认,那么假设数据包被已丢失并进行重传

TCP报文格式

hdr

字段 长度 描述
源端口 16bit 发送端端口
目的端口 16bit 接收连接端口
序列号 32bit 初始值为随机值
确认号 32bit 希望下次收到报文的seq值
数据偏移 4bit TCP负载相对头部起始的偏移地址;
即TCP头部长度,以4字节为单位,长度:[5-15],即[20-60]字节
保留 3bit 0
标志符号 9bit NS—ECN-nonce。ECN显式拥塞通知(Explicit Congestion Notification)是对TCP的扩展,定义于 RFC 3540 (2003)。ECN允许拥塞控制的端对端通知而避免丢包。ECN为一项可选功能,如果底层网络设施支持,则可能被启用ECN的两个端点使用。在ECN成功协商的情况下,ECN感知路由器可以在IP头中设置一个标记来代替丢弃数据包,以标明阻塞即将发生。数据包的接收端回应发送端的表示,降低其传输速率,就如同在往常中检测到包丢失那样。CWR—Congestion Window Reduced,定义于 RFC 3168(2001)。ECE—ECN-Echo有两种意思,取决于SYN标志的值,定义于 RFC 3168(2001)。
URG—为1表示高优先级数据包,紧急指针字段有效。
ACK—为1表示确认号字段有效
PSH—为1表示是带有PUSH标志的数据,指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满。
RST—为1表示出现严重差错。可能需要重新创建TCP连接。还可以用于拒绝非法的报文段和拒绝连接请求。
窗口 16bit 表示从确认号开始,本报文的发送方可以接收的字节数,即接收窗口大小。用于流量控制。
校验和 16bit 对TCP头部和TCP数据,以16位字进行计算所得
紧急指针 16bit 本报文段中的紧急数据的最后一个字节的序号
选项字段 [0-40]字节 起始1字节表明类型。
0:选项表结束(1字节)
1:无操作(1字节)用于选项字段之间的字边界对齐。
2:最大报文段长度(4字节,Maximum Segment Size,MSS)通常在创建连接而设置SYN标志的数据包中指明这个选项,指明本端所能接收的最大长度的报文段。通常将MSS设置为(MTU-40)字节,携带TCP报文段的IP数据报的长度就不会超过MTU(MTU最大长度为1518字节,最短为64字节),从而避免本机发生IP分片。只能出现在同步报文段中,否则将被忽略。
3:窗口扩大因子(3字节,wscale),取值0-14。用来把TCP的窗口的值左移的位数,使窗口值乘倍。只能出现在同步报文段中,否则将被忽略。这是因为现在的TCP接收数据缓冲区(接收窗口)的长度通常大于65535字节。
4:sackOK—发送端支持并同意使用SACK选项。
5:SACK实际工作的选项。
8:时间戳(10字节,TCP Timestamps Option,TSopt)发送端的时间戳(Timestamp Value field,TSval,4字节)时间戳回显应答(Timestamp Echo Reply field,TSecr,4字节)
19:MD5摘要,将TCP伪首部、校验和为0的TCP首部、TCP数据段、通信双方约定的密钥(可选)计算出MD5摘要值并附加到该选项中,作为类似对TCP报文的签名。通过 RFC 2385 引入,主要用于增强BGP通信的安全性。
29:安全摘要,通过 RFC 5925 引入,将“MD5摘要”的散列方法更换为SHA散列算法。

最大报文长度

一个链接两端每次能发送最大报文段的长度,即MSS(maximum segment size)。
如果太小,会降低网络利用率
如果太大,IP层会分片,从而降低网络性能;若分片丢失,需要重传,将降低网络的吞吐率

TCP建链

tcp

TCP断开

tcp

TCP状态机

tcp

主动打开与被动打开

  • 主动发起请求TCP请求(调用connect)的一侧叫主动打开
  • 主动开始监听(调用listen)的一侧叫被动打开

带外数据

带外数据(OOB)是指对紧急数据,中断或放弃排队中的数据流;接收方应立即处理紧急数据。
完成后,TCP通知应用程序恢复流队列的正常处理。

往返时延

RTT(Round-Trip Time 往返时延)
从发送端发送数据开始,到发送端收到来自接收端的确认,总共经历的时延

超时重传时间

RTO(Retransmission Timeout)
RTO值应该略大于RTT

  • RTO较大时,重发慢,网络传输效率低
  • RTO较小时,可能没丢就触发重传,增加网络拥塞,导致更多的超时

重传机制

TCP通过序列号确认机制(SEQ-ACK)保证数据传输可靠性
对确定失丢的报文重传

超时重传

当RTO时间内,未收到报文段确认,就触发重传
下一次重传时间为当前重传时间的2倍
直到收到确认超出最大重传次数

快速重传

若接收方未收到某一分组N,而收到其后的若干分组N+1,N+2...
然后接收方会多次发送"希望收到第N分组"(ACK是希望收到的下一个分组的SEQ)
发送方若接收到同一分组的多次(>=3)确认消息,就确定该分组丢失
不用等到RTO之后,才触发重传

快重传的问题

当某一分组丢失后,将重传该分组
丢失分组的之后的分组是否也丢失需要重传呢?

ASCK

SACK( Selective Acknowledgment 选择性确认)
接收方在TCP头部可选部分中增加SACK字段,将接收方缓存地图(哪些分组已经收到,哪些分组未收到)发给发送方
发送方仅重传丢失的分组
linux下通过设置net.ipv4.tcp_sack启用该功能。

D_SACK

D-SACK(Duplicate SACK)
D-SACK使用了SACK来告诉「发送方」有哪些数据被重复接收了。
此时发送方可以判断丢包原因:1.ack丢包 2.网络延时 3.报文在网络中被复制
linux下通过设置net.ipv4.tcp_dsack启用该功能。

滑动窗口

TCP是面向字节流的,同时需要接受确认,所以需要滑动窗口记录当前发送和接受情况。
TCP是全双工,所以收发双方均有滑动窗口。
发送方窗口大小是接收方未确认前可以发送数据的大小
接收方窗口大小是接收方确认前可以缓存发送方的数据的大小
window

发送窗口


  • 绿色部分:发送并且已确认的字节流
  • 蓝色部分:已发送但收到未确认的字节流
  • 橙色部分:即将发送的字节流
  • 粉色部门:未准备发送的字节


如上图所示,TCP把可发送的数据发送出去后,可用窗口耗尽,无法继续发送

如上图所示:当收到ACK确认消息后,滑动窗口往前移动,可用窗口增加,又可以继续发送数据

窗口计算


  • SND.WND:表示发送窗口的大小
  • SND.UNA:Send Unacknowledged指针,指向发送窗口的第一个字节。
  • SND.NXT:Send Next指针,指向可用窗口的第一个字节

可用窗口大 = SND.WND -(SND.NXT - SND.UNA)

接受窗口


  • 绿色:接收并确认的数据
  • 蓝色:尚未接收但允发送端发送数据,即可用接收窗口
  • 红色:尚未接收且不允许发送端发送的数据

窗口计算

  • RCV.WND:表示接收窗口的大小,它会通告给发送方。
  • RCV.NXT:是一个指针,指向可接受窗口的第一个字节

流量控制

让发送方根据接收方实际接受能力控制发送的数据量

操作系统缓存区与滑动窗口的关系

发送窗口和接收窗口的字节流都在操作系统内存缓冲区中的
而操作系统的缓冲区,会被操作系统调整
当应用进程没办法及时读取缓冲区的内容时,也会对我们的缓冲区造成影响

窗口关闭

接收方将希望接受窗口大小置为0,即关闭窗口。 收到窗口关闭后启动定时器,定时器超时即发送窗口探测报文
定时器默认30-60秒,探测次数为3,如果3次探测后,窗口大小仍为0,则发送RST断开。

糊涂窗口综合症

接收方处理延迟,导致通知窗口越来越小,发送方使用该小窗口,每次发送很少数据。

接收方策略

窗口大小 < min(MSS,缓存空间/2)时,即发送窗口关闭通告
窗口大小 >= min(MSS,缓存空间/2)时,更新发送窗口

发送方策略

使用Nagle算法
当窗口大小 > MSS || 数据大小 >= MSS,则发送数据
收到之前发送数据的ack包后发送数据

拥塞

在网络出现拥堵时,可能会导致数据包时延、丢失等
如果重传就会增大网络负担,进入丢包重传的循环中
当发生报文丢失(对应的ACK没收到),就认为发生拥塞

拥塞窗口

在发送方使用拥塞窗口调节发送数据量
发送窗口大小 = min(拥塞窗口大小,接收窗口大小)
拥塞窗口随着网络拥塞的情况而改变
无拥塞时,拥塞窗口和接收方通告窗口一样大

拥塞处理

立即将拥塞窗口减半,直到为1
发送窗口内的报文段对应的重传定时器加倍。

慢启动

新链接或拥塞之后,拥塞窗口大小默认为1
发送方每收到一个ACK,拥塞窗口大小加1,随着发送的报文数呈指数级增加
慢启动门限 ssthresh (slow start threshold)
当 cwnd < ssthresh 时,使用慢启动算法
当 cwnd >= ssthresh 时,就会使用拥塞避免算法

拥塞避免

当慢启动的窗口大小达到 ssthresh 时
就会将发送窗口增大速率降低为1

cwnd

长链接

TCP长连接是指客户端和服务器建立连接后,持续保持连接状态,可以进行多次数据传输,
减少新建链接三次握手开销。 当同时设置socket属性和sysctl参数时,socket属性优先级更高。

socket属性

属性 说明
SO_KEEPALIVE 启用TCP keepalive机制,定期发送探测包检测连接状态
TCP_KEEPIDLE 设置开始发送keepalive探测包的空闲时间
TCP_KEEPINTVL 设置keepalive探测包的发送间隔
TCP_KEEPCNT 设置keepalive探测包的最大发送次数
SO_REUSEADDR 允许重用本地地址

Sysctl参数

Text Only
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#Keepalive 相关参数
# 指定空闲时间(无任何TCP消息交互)
net.ipv4.tcp_keepalive_time = 7200

# keepalive探测包发送间隔(秒)
net.ipv4.tcp_keepalive_intvl = 75

# keepalive探测包最大发送次数
net.ipv4.tcp_keepalive_probes = 9


# 连接超时参数
# SYN重传次数
net.ipv4.tcp_syn_retries = 6

# SYN-ACK重传次数
net.ipv4.tcp_synack_retries = 5

# 数据包重传次数
net.ipv4.tcp_retries2 = 15

# FIN-WAIT-2状态超时时间
net.ipv4.tcp_fin_timeout = 60

#TIME_WAIT 相关参数
# TIME_WAIT状态的超时时间
net.ipv4.tcp_time_wait_timeout = 60

# 允许TIME_WAIT状态的socket重用
net.ipv4.tcp_tw_reuse = 1

# 快速回收TIME_WAIT状态的socket
net.ipv4.tcp_tw_recycle = 0  # 建议设为0,避免NAT问题

连接队列参数
# 全连接队列大小
net.core.somaxconn = 1024

# 半连接队列大小
net.ipv4.tcp_max_syn_backlog = 1024