TCP传输控制协议
TCP 是基于 Internet Protocal 的协议,位于运输层,在 TCP 中通信双方实体是对等的。
自动状态机模型
在该模型中,共存在 11 个状态,任意时刻任意一方都处于这 11 个状态之中。这些状态中包括:
- CLOSED 状态:初始状态。处于该状态时:
- 当当前实体需要主动通信时,打开并发送
SYN
到 SYN SENT 状态. - 当当前实体需要被动通信时,打开到 LISTEN 状态。
- 当当前实体需要主动通信时,打开并发送
- LISTEN 状态:监听状态。处于该状态时:
- 若想主动通信,发送 SYN 到 SYN SENT 状态。
- 若被动收到了 SYN,发送 SYN ACK 到 SYN RCVD 状态。
- SYN SENT 状态:已发送连接请求状态。处于该状态时:
- 若应用进程关闭该请求,或该请求未收到响应(超时),进入 CLOSED 状态。
- 若收到了 SYN 请求,说明同时打开,发送 SYN 及 ACK 到 SYN RCVD 状态。
- 若收到了 SYN ACK,发送 ACK 到 ESTABLISHED 状态。
- SYN RCVD 状态:已收到连接请求状态。处于该状态时:
- 若收到了 ACK,进入 ESTABLISHED 状态。
- 若发送 FIN,说明连接未建立即终止,进入 FIN WAIT 1 状态(主动关闭)。
- 若收到了 RST,重置并进入 LISTEN 状态。
- ESTABLISHED 状态:连接已建立状态。处于该状态时:
- 若发送 FIN,说明为主动关闭连接,进入 FIN WAIT 1 状态。
- 若收到 FIN,说明为被动关闭连接,发送 ACK 并进入 CLOSE WAIT 状态。
- FIN WAIT 1 状态:已发送关闭请求状态。处于该状态时:
- 若收到 ACK,说明关闭请求已被响应,进入 FIN WAIT 2 状态。
- 若收到 FIN,说明同时关闭,发送 ACK 并进入 CLOSING 状态。
- 若收到 FIN ACK,说明关闭请求被响应且请求关闭,发送 ACK 进入 TIME WAIT 状态。
- FIN WAIT 2 状态:已收到关闭响应状态,处于该状态时:
- 若收到 FIN,说明请求关闭,发送 ACK 并进入 TIME WAIT 状态。
- CLOSING 状态:剩余我方请求未响应状态。处于该状态时:
- 若收到 ACK,说明关闭请求已被响应,进入 TIME WAIT 状态。
- TIME WAIT 状态:2MSL 状态,处于该状态时:
- 若超过 2MSL 未收到请求,说明 ACK 安全送达,自动进入 CLOSED 状态。
- CLOSE WAIT 状态:被动关闭状态。处于该状态时:
- 若发送 FIN,则说明我方请求仍未响应,进入 LAST ACK 状态。
- LAST ACK 状态:等待关闭响应状态。处于该状态时:
- 若收到 ACK,说明关闭请求已响应,进入 CLOSED 状态。
该自动机转化模型如下所示:
参考文献
FAQs
TCP 和 UDP 可以共同占用一个端口号吗?
首先来看 ipv4 的 ip 包头格式:
里面包含了 Protocol 字段,说明在网络层便可以将传输层进行区分,从而将包交付给不同的协议守护进程。
一个正常的运行方式如下:
由此可见,TCP/UDP 可以使用相同的端口号,因为它们的报文在网络层向上交付时便已经做了区分。
为什么一个 80 端口可以建立很多连接?
一个连接,或者说一个 Socket 的唯一标识是:
也就是说,操作系统,接收到一个端口发来的数据时,会在该端口,产生的连接中,查找到符合这个唯一标识的并传递信息到对应缓冲区。
- 一个端口同一时间只能 bind 给一个 socket。就是同一时间一个端口只可能有一个监听线程(监听 listen 之前要 bind);
- 为什么一个端口能建立多个 TCP 连接,同一个端口也就是说 和 是不变的。那么只要 不相同就可以了。能保证连接唯一标识 的唯一性。
正如在应用、Socket/连接以及端口之间的关系图中所示的那样,一个应用可以有多个连接,所以一个应用可以有多个 socket,比如一个服务端需要同时对多个客户端提供服务,这就需要为每一个客户分配一个连接,也就是一个 socket;而对于 80 端口来说,应该支持多个 socket 绑定到这个端口,也就是多个连接连接到同一个端口。
同时,在 Socket 的编程上,TCP 主 socket 用来专门接收新的连接请求,以生成针对连接的 socket,也就是每次 accept 一个连接请求则生成一个针对这个连接的 socket,多个 socket 表示不同的连接,但是共用了同一个端口。
什么是 TCP 拆包/粘包问题?
拆包/粘包问题在 TCP 中会出现,在 UDP 中则不会出现,根本原因在于 TCP 是基于字节流的,而不是基于数据报的。
在讨论拆包/粘包问题时,我们应当时刻注意,这些指代的是应用层的数据,而不是传输层的数据。我们在通过应用层向 TCP 发送我们想要传的数据时,是以一块一块的形式交付给 socket 的,但是 TCP 在发送的时候,会把发送的信息以流的形式,并将其切割后再封装并交付给下层网络层,从而进行传输。当我们的应用层数据过小时,会发生粘包现象,因为 TCP 的发送缓冲区没有被填满,其需要等待更多的数据一并发送;而当我们的应用层数据过大时,会发生拆包现象,因为 TCP 需要将其拆分后才能进行包装以满足 MTU 的要求。