TCP头格式
- 源端口号
- 目标端口号
- 序列号:在建⽴连接时由计算机⽣成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送⼀次数据,就「累加」⼀次该「数据字节数」的⼤⼩。⽤来解决⽹络包乱序问题。
- 确认应答号:指下⼀次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。⽤来解决不丢包的问题。
- 控制位:
- ACK:该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建⽴连接时的 SYN 包之外该位必须设置为 1 。
- RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。
- SYN:该位为 1 时,表示希望建⽴连接,并在其「序列号」的字段进⾏序列号初始值的设定。
- FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双⽅的主机之间就可以相互交换 FIN 位为 1 的 TCP 段
- 窗口大小
- 校验和
- 紧急指针
- 选项
-
为什么需要 TCP 协议? TCP ⼯作在哪⼀层?
IP 层是「不可靠」的,它不保证⽹络包的交付、不保证⽹络包的按序交付、也不保证⽹络包中的数据的完整性。如果需要保障⽹络数据包的可靠性,那么就需要由上层(传输层)的 TCP 协议来负责。因为 TCP 是⼀个⼯作在传输层的可靠数据传输的服务,它能确保接收端接收的⽹络包是⽆损坏、⽆间隔、⾮冗余和按序的。
什么是TCP?
TCP 是⾯向连接的、可靠的、基于字节流的传输层通信协议。
⾯向连接:⼀定是「⼀对⼀」才能连接,不能像 UDP 协议可以⼀个主机同时向多个主机发送消息,也就是⼀对多是⽆法做到的;
- 可靠的:⽆论的⽹络链路中出现了怎样的链路变化,TCP 都可以保证⼀个报⽂⼀定能够到达接收端;
- 字节流:消息是「没有边界」的,所以⽆论我们消息有多⼤都可以进⾏传输。并且消息是「有序的」,当「前 ⼀个」消息没有收到的时候,即使它先收到了后⾯的字节,那么也不能扔给应⽤层去处理,同时对「᯿复」的报⽂会⾃动丢弃。
建⽴⼀个 TCP 连接是需要客户端与服务器端达成上述三个信息的共识
- Socket:由 IP 地址和端⼝号组成
- 序列号:⽤来解决乱序问题等
- 窗⼝⼤⼩:⽤来做流ᰁ控制
TCP 和 UDP 区别
| | tcp | udp | | —- | —- | —- | | 连接 | TCP 是⾯向连接的传输层协议,传输数据前先要建⽴连接 | UDP 是不需要连接,即刻传输数据。 | | 服务对象 | TCP 是⼀对⼀的两点服务,即⼀条连接只有两个端点。 | UDP ⽀持⼀对⼀、⼀对多、多对多的交互通信 | | 可靠性 | TCP 是可靠交付数据的,数据可以⽆差错、不丢失、不重复、按需到达 | UDP 是尽最⼤努⼒交付,不保证可靠交付数据 | | 拥塞控制、流量控制 | TCP 有拥塞控制和流量控制机制,保证数据传输的安全性 | UDP 则没有,即使⽹络⾮常拥堵了,也不会影响 UDP 的发送速率。 | | 首部开销 | TCP ⾸部⻓度较⻓,会有⼀定的开销,⾸部在没有使⽤「选项」字段时是 20 个字节,如果使⽤了「选项」
字段则会变⻓的。 | UDP ⾸部只有 8 个字节,并且是固定不变的,开销较⼩。 | | 传输方式 | TCP 是流式传输,没有边界,但保证顺序和可靠。 | UDP 是⼀个包⼀个包的发送,是有边界的,但可能会丢包和乱序。 | |
分片不同 | TCP 的数据⼤⼩如果⼤于 MSS(最长TCP报文段长度) ⼤⼩,则会在传输层进⾏分⽚,⽬标主机收到后,也同样在传输层组装 TCP
数据包,如果中途丢失了⼀个分⽚,只需要传输丢失的这个分⽚。 | UDP 的数据⼤⼩如果⼤于 MTU(最大传输单元)⼤⼩,则会在 IP 层进⾏分⽚,⽬标主机收到后,在 IP 层组装完数据,接着再
传给传输层,但是如果中途丢了⼀个分⽚,在实现可靠传输的 UDP 时则就需要᯿传所有的数据包,这样传输
效率⾮常差,所以通常 UDP 的报⽂应该⼩于 MTU。 | | 应用场景 | 由于 TCP 是⾯向连接,能保证数据的可靠性交付,因此经常⽤于:
FTP ⽂件传输
HTTP / HTTPS | 由于 UDP ⾯向⽆连接,它可以随时发送数据,再加上UDP本身的处理既简单⼜⾼效,因此经常⽤于:
包总ᰁ较少的通信,如 DNS 、 SNMP 等
视频、⾳频等多媒体通信
⼴播通信 |
三次握手

- ⼀开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端⼝,处于 LISTEN 状态
- 客户端会随机初始化序号( client_isn ),将此序号置于 TCP ⾸部的「序号」字段中,同时把 SYN 标志位置为 1 ,表示 SYN 报⽂。接着把第⼀个 SYN 报⽂发送给服务端,表示向服务端发起连接,该报⽂不包含应⽤层数据,之后客户端处于 SYN-SENT 状态。
- 服务端收到客户端的 SYN 报⽂后,⾸先服务端也随机初始化⾃⼰的序号( server_isn ),将此序号填⼊TCP ⾸部的「序号」字段中,其次把 TCP ⾸部的「确认应答号」字段填⼊ client_isn + 1 , 接着把 SYN 和ACK 标志位置为 1 。最后把该报⽂发给客户端,该报⽂也不包含应⽤层数据,之后服务端处于 SYN-RCVD状态。
- 客户端收到服务端报⽂后,还要向服务端回应最后⼀个应答报⽂,⾸先该应答报⽂ TCP ⾸部 ACK 标志位置为1 ,其次「确认应答号」字段填⼊ server_isn + 1 ,最后把报⽂发送给服务端,这次报⽂可以携带客户到服务器的数据,之后客户端处于 ESTABLISHED 状态。
服务器收到客户端的应答报⽂后,也进⼊ ESTABLISHED 状态。
为什么是三次握⼿?不是两次、四次?
⽤于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗⼝⼤⼩称为连接。
接下来以三个⽅⾯分析三次握⼿的原因:
三次握⼿才可以阻⽌᯿复历史连接的初始化(主要原因)
三次握⼿才可以同步双⽅的初始序列号
三次握⼿才可以避免资源浪费四次挥手

客户端打算关闭连接,此时会发送⼀个 TCP ⾸部 FIN 标志位被置为 1 的报⽂,也即 FIN 报⽂,之后客户端进⼊ FIN_WAIT_1 状态。
- 服务端收到该报⽂后,就向客户端发送 ACK 应答报⽂,接着服务端进⼊ CLOSED_WAIT 状态。
- 客户端收到服务端的 ACK 应答报⽂后,之后进⼊ FIN_WAIT_2 状态。
- 等待服务端处理完数据后,也向客户端发送 FIN 报⽂,之后服务端进⼊ LAST_ACK 状态。
- 客户端收到服务端的 FIN 报⽂后,回⼀个 ACK 应答报⽂,之后进⼊ TIME_WAIT 状态
- 服务器收到了 ACK 应答报⽂后,就进⼊了 CLOSED 状态,⾄此服务端已经完成连接的关闭。
客户端在经过 2MSL ⼀段时间后,⾃动进⼊ CLOSED 状态,⾄此客户端也完成连接的关闭。
为什么挥⼿需要四次?
关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
- 服务器收到客户端的 FIN 报⽂时,先回⼀个 ACK 应答报⽂,⽽服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报⽂给客户端来表示同意现在关闭连接。
从上⾯过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK 和 FIN ⼀般都会分开发送,从⽽⽐三次握⼿导致多了⼀次。
TIME-WAIT 作⽤是等待⾜够的时间以确保最后的 ACK 能让被动关闭⽅接收,从⽽帮助其正常关闭。
需要 TIME-WAIT 状态,主要是两个原因:
- 超时重传
- 快速重传
- SACK
- D-SACK
滑动窗口
这样的传输⽅式有⼀个缺点:数据包的往返时间越⻓,通信的效率就越低。为解决这个问题,TCP 引⼊了窗⼝这个概念。即使在往返时间较⻓的情况下,它也不会降低⽹络通信的效率。那么有了窗⼝,就可以指定窗⼝⼤⼩,窗⼝⼤⼩就是指⽆需等待确认应答,⽽可以继续发送数据的最⼤值。流量控制
发送⽅不能⽆脑的发数据给接收⽅,要考虑接收⽅处理能⼒。如果⼀直⽆脑的发数据给对⽅,但对⽅处理不过来,那么就会导致触发重发机制,从⽽导致⽹络流量的⽆端的浪费。为了解决这种现象发⽣,TCP 提供⼀种机制可以让「发送⽅」根据「接收⽅」的实际接收能⼒控制发送的数据量,这就是所谓的流量控制。拥塞控制
前⾯的流ᰁ控制是避免「发送⽅」的数据填满「接收⽅」的缓存,但是并不知道⽹络的中发⽣了什么。
⼀般来说,计算机⽹络都处在⼀个共享的环境。因此也有可能会因为其他主机之间的通信使得⽹络拥堵。
在⽹络出现拥堵时,如果继续发送⼤量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是⼀重传就会导致⽹络的负担更重,于是会导致更⼤的延迟以及更多的丢包,这个情况就会进⼊恶性循环被不断地放⼤….所以,TCP 不能忽略⽹络上发⽣的事,它被设计成⼀个⽆私的协议,当⽹络发送拥塞时,TCP 会⾃我牺牲,降低发送的数据量。于是,就有了拥塞控制,控制的⽬的就是避免「发送⽅」的数据填满整个⽹络。为了在「发送⽅」调节所要发送数据的ᰁ,定义了⼀个叫做「拥塞窗⼝」的概念。

