TCP协议

什么是TCP

TCP 全称为 " 传输控制协议 (Transmission Control Protocol"). 人如其名 , 要对数据的传输进行一个详细的控制 ;
对于TCP,我们之前写的recv与send只是把数据放到了内核缓冲区(从缓冲区提取),而不是直接接受与发送


打开的一个socket文件描述符对应两个文件缓冲区

TCP****协议段格式

可以观察到TCP协议的报头格式还是十分复杂的。除了拥有UDP拥有的端口号、校验和,还有这其他丰富的段落选项可供使用。

格式解读(略)

源/目的端口号 : 表示数据是从哪个进程来 , 到哪个进程去 ;
32位序号/32位确认号 : 后面详细讲 ;
4位TCP报头长度 : 表示该 TCP 头部有多少个 32 位 bit( 有多少个 4 字节 ); 所以 TCP 头部最大长度是 15 * 4(单位4字节) = 60字节
6位标志位:
URG: 紧急指针是否有效
ACK: 确认号是否有效
PSH: 提示接收端应用程序立刻从 TCP 缓冲区把数据读走
RST: 对方要求重新建立连接 ; 我们把携带 RST 标识的称为 复位报文段
SYN: 请求建立连接 ; 我们把携带 SYN 标识的称为 同步报文段
FIN: 通知对方 , 本端要关闭了 , 我们称携带 FIN 标识的为 结束报文段
16位窗口大小 : 后面再说
16位校验和 : 发送端填充 , CRC 校验 . 接收端校验不通过 , 则认为数据有问题 . 此处的检验和不光包含 TCP 首部 , 也包含 TCP 数据部分 .
16位紧急指针 : 标识哪部分数据是紧急数据 ;
40字节头部选项 : 暂时忽略 ;
下面是对于机制详细的介绍。

确认应答**(ACK)**机制


TCP 将每个字节的数据都进行了编号 . 即为序列号 .

每一个 ACK都带有对应的确认序列号 , 意思是告诉发送者 , 我已经收到了哪些数据 ; 下一次你从哪里开始发。
发送数据之后,对方会向我们发送一个回复报文,这个报文的6位标志位设置了ACK。

超时重传机制


主机A发送数据给B之后, 可能因为网络拥堵等原因, 数据无法到达主机B;
如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发;
但是, 主机A未收到B发来的确认应答, 也可能是因为ACK丢失了;

因此主机 B 会收到很多重复数据 . 那么 TCP 协议需要能够识别出那些包是重复的包 , 并且把重复的丢弃掉, 这 就可以很容易做到去重的效果 . 这时候我们可以利用前面提到的序列号。
那么, 如果超时的时间如何确定?
最理想的情况下, 找到一个最小的时间, 保证 "确认应答一定能在这个时间内返回".
但是这个时间的长短, 随着网络环境的不同, 是有差异的.
如果超时时间设的太长, 会影响整体的重传效率;
如果超时时间设的太短, 有可能会频繁发送重复的包;
TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间。
Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时时间都是500ms的整数倍。
如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传。
如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增。
累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接。

连接管理机制

在正常情况下, TCP要经过三次握手建立连接, 四次挥手断开连接(重中之重)

进入连接--三次握手

最开始C与S都是关闭状态。

客户端 :fd = socket()分配一个网路套接字描述符,内核中打开了输入与输出缓冲区(资源消耗)。

服务端:listenfd = socket()同样分配一个文件描述符。服务端需要创建sockaddr_in结构,将端口、ip、协议类型等信息绑定。同时调用listen(),进入监听状态。

客户端:调用connet(),向进入监听状态的服务器发起连接请求。这时候TCP报文的报头6位标志位字段会设置为SYN。自己进入SYN_SENT状态。---第一次

服务端:接受到SYN报文之后,向对方发送带有SYN + ACK标志位的报文,表示收到了连接信息,向客户端发起连接请求(SYN),并同意了客户端的连接请求(ACK)自己进入SYN_RECV状态。 ---第二次。

客户端:接受到来了来自服务端的报文,再次向服务端发送了带有ACK标志位的报文,表示收到了对方发来的SYN,客户端率先进入连接维护状态 -- ESTABLISHED(服务端收到ACK之后,才会进入ESTABLISHED状态)。-----第三次

自此,三次握手完成。连接建立。服务器的accept()接口只是将连接从全连接队列提取出来。

三次握手周边问题

为什么要三次握手才能建立连接呢?为什么不能是一次两次呢?

ANSWER:1.全双工 + 2.减轻压力

1.保证全双工。

全双工就是为了保证通信两个方向上的可靠性。必须有一来一回的放心,才能保证两个方向的可靠性。

其实可以拆分为四次握手:请求---应答,请求---应答。只不过为了效率,将四次握手修改成了三次握手----提升效率:捎带应答。 为了提高效率,可以二合一(捎带应答

2.防止SYN洪水,减轻服务端的压力。

一次握手:

两次握手:

可以看到,S给C发送ACK之后,S就要进入established状态,即:S要去优先维护连接(一个服务器可是要跟很多客户端进行交互,那么服务器的压力太大。)----偶数次握手,会导致S先进入连接。

维护连接:1.缓冲区(socket阶段)2.半连接队列(SYN队列),状态为SYN_RECV(listen的第二个参数就是设置半连接的数量)与全连接队列,状态为ESTABLISHED(accept从全连接队列提取连接)

三次握手

客户端先建立连接,如果最后一次ACK丢失(握手失败),那么连接就先在C端,S端的压力就小得多。过一段时间收不到来自S端的通信,OS会自动断开连接。

如果C给S发送很多消息呢?

服务器收到放到缓冲区,如果不是发送的顺序,那就是乱序问题------不可靠。

给了防止发送完之后乱序,在报文的报头中存在一个叫做序号的东西。作用之一:保证数据的按序到达--排序重组

数据丢失就要重传。

断开连接---四次挥手

当客户端C想给服务端S发送断开连接请求的时候,仍然是C与S都会互发FIN标志位的报文,会经历如下步骤。

C:C向S发送带有FIN标志位的报文,进入FIN_WAIT_1状态(需要注意的是FIN_WAIT_1 FIN_WAIT_2都C端的状态)。----第一次挥手

S:发送ACK标志位的报文,进入CLOSE_WAIT状态,表示接收到了对方的断开请求。 ---第二次挥手

C:接收到ACK之后,进入FIN_WAIT_2状态。

S:如果有消息,继续给C发消息,没有消息,S给C发送带有FIN标志位的报文。自己进入LAST_ACK状态(表示最后接受一次ACK就会关闭连接 )---第三次挥手

C:接收到S的FIN报文之后,进入TIME_WAIT状态,向对方发送ACK报文,表示收到了断开请求。----第四次挥手

S:收到对方的ACK之后,断开连接。

C:这时候C不会立即断开连接,而是TIME_WAIT结束之后,才会进入CLOSED状态。


较粗的虚线表示服务端的状态变化情况 ;
较粗的实线表示客户端的状态变化情况;
CLOSED 是一个假想的起始点 , 不是真实状态

理解TIME_WAIT状态

现在做一个测试 , 首先启动 server, 然后启动 client, 然后用 Ctrl-C 使 server 终止 , 这时马上再运行 server, 结果是 :

这是因为 , 虽然 server 的应用程序终止了 , 但TCP协议层的连接并没有完全断开, 因此不能再次监听同样的 server 端口. 我们用 netstat 命令查看一下 :

TCP协议规定, 主动关闭连接的一方要处于TIME_ WAIT状态 ,等待 两个MSL(maximum segment lifetime)的时间后才能回到CLOSED状态.
我们使用Ctrl-C终止了server, 所以server是主动关闭连接的一方, 在TIME_WAIT期间仍然不能再次监听同样的server端口;
**MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同,**在Centos7上默认配置的值是60s;
可以通过 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看msl的值;

为什么是TIME_WAIT的时间是2MSL?

MSL是TCP报文的最大生存时间, 因此TIME_WAIT持续存在2MSL的话
就能保证 在两个传输方向上的尚未被接收或迟到的报文段都已经消失 (否则服务器立刻重启, 可能会 收到来自上一个进程的迟到的数据, 但是这种数据很可能是错误的 );-- 不能立即重启:让不靠谱的报文消失。
**同时也是在理论上保证最后一个报文可靠到达(**假设最后一个ACK丢失, 那么服务器会再重发一个FIN. 这时虽然客户端的进程不在了, 但是TCP连接还在, 仍然可以重发LAST_ACK)

相关推荐
小伍_Five1 小时前
透视网络世界:计算机网络习题的深度解析与总结【前3章】
服务器·网络·计算机网络
芷栀夏2 小时前
如何在任何地方随时使用本地Jupyter Notebook无需公网IP
服务器·ide·tcp/ip·jupyter·ip
G鲲鹏展翅Y2 小时前
jupyter-lab与实验室服务器远程链接
服务器·jupyter
LI JS@你猜啊3 小时前
Elasticsearch 集群
大数据·服务器·elasticsearch
网络安全(king)3 小时前
网络安全攻防学习平台 - 基础关
网络·学习·web安全
赵大仁3 小时前
在 CentOS 7 上安装 Node.js 20 并升级 GCC、make 和 glibc
linux·运维·服务器·ide·ubuntu·centos·计算机基础
vvw&3 小时前
Docker Build 命令详解:在 Ubuntu 上构建 Docker 镜像教程
linux·运维·服务器·ubuntu·docker·容器·开源
李白你好3 小时前
家用无线路由器的 2.4GHz 和 5GHz
运维·网络
嵌入(师)4 小时前
嵌入式驱动开发详解21(网络驱动开发)
网络·驱动开发
冷冰鱼4 小时前
【问题实录】服务器ping不通win11笔记本
运维·服务器