TCP协议(传输控制协议)
在学习TCP之前,让我们把它的特性总结一遍:
- 面向连接
- 可靠
- 面向字节流
- 全双工
TCP协议段格式
-
tcp源端口号和目的端口号怎么理解?
在一台机器上,一个进程对应一个端口号,端口号的作用就是用来唯一标识一个进程。源目的端口标识发起通信的那个进程,目的端口号标识接收通信的那个进程。有了端口号,接收到的报文才能够知道将报文发送到哪个进程。
-
32位序号:表示发数据的主动方
-
32位确认序号:相当于ACK,是收数据的
-
4位首部长度:表示该TCP头部有多少个32位bit(有多少个4字节); 所以TCP头部大长度是15 * 4 = 60
-
6位标志位:
- URG:紧急指针(偏移量)是否有效
- ACK:确认号是否有效
- PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走 (检测缓冲区是否空闲)
- RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段
- SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段
- FIN:通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报文段
-
16位窗口大小:即接收缓冲区剩余空间的大小
-
16位校验和:发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含TCP首部, 也 包含TCP数据部分.
确认应答(ACK)
TCP将每个字节的数据都进行了编号,即为序列号。
每一个ACK都带着对应的确认序列号,意思是告诉发送至,我们已经接收到哪些数据,下一次你应该从哪里开始发。
超时重传机制
- 主机A发送数据给B之后,可能因为网络拥堵等原因,数据无法到达主机B
- 如果主机A在一个特定时间间隔内没有收到B发来的确认应答,就会进行重发。
但是,主机A未收到B发来的确认应答,也可能因为ACK丢失了
因此主机B会收到很多重新数据,那么TCP协议需要能够识别出那些包是重复的包,并且把重复的丢弃掉。
这时候我们可以利用前面提到的序列号,就可以很容易做到去重的效果。
那么,如果超时的时间如何确定?
- 最理想的情况下,找一个最小的时间,保证“确认应答一定能在这个时间内返回”
- 但是这个时间的长短,随着网络环境的不同,是有差异的
- 如果超时时间设的太长,会影响整体的重传效率(浪费资源)
- 如果超时时间设的太短,有可能会频繁发送重复的包(影响资源)
TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态的计算最大超时时间
连接管理机制
在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接(建立连接前先调用socket,创建文件描述符,然后accept -> 服务器端把链接打开)
服务端状态转换:
- [CLOSED -> LISTEN] 服务器端调用listen后进入LISTEN状态, 等待客户端连接;
- [LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段), 就将该连接放入内核等待队列中, 并向客户端 发送SYN确认报文.
- [SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文, 就进入ESTABLISHED状态, 可以进行 读写数据了.
- [ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用close), 服务器会收到结束报文段, 服务器 返回确认报文段并进入CLOSE_WAIT;
- [CLOSE_WAIT -> LAST_ACK] 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据); 当 服务器真正调用close关闭连接时, 会向客户端发送FIN, 此时服务器进入LAST_ACK状态, 等待后一个 ACK到来(这个ACK是客户端确认收到了FIN)
- [LAST_ACK -> CLOSED] 服务器收到了对FIN的ACK, 彻底关闭连接
客户端状态转换:
- [CLOSED -> SYN_SENT] 客户端调用connect, 发送同步报文段
- [SYN_SENT -> ESTABLISHED] connect调用成功, 则进入ESTABLISHED状态, 开始读写数据;
- [ESTABLISHED -> FIN_WAIT_1] 客户端主动调用close时, 向服务器发送结束报文段, 同时进入 FIN_WAIT_1;
- [FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认, 则进入FIN_WAIT_2, 开始等待服 务器的结束报文段;
- [FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段, 进入TIME_WAIT, 并发出LAST_ACK;
- [TIME_WAIT -> CLOSED] 客户端要等待一个2MSL(Max Segment Life, 报文大生存时间)的时间, 才会 进入CLOSED状态.
TIME_WAIT的原因是确保服务器收到了ACK;
先关闭客户端,后关闭服务器
理解TIME_WAIT状态
TCP协议规定,主动关闭连接的一方要处于TIME_ WAIT状态,等待两个MSL 的时间后才能回到CLOSED状态.
想一想, 为什么是TIME_WAIT的时间是2MSL?
MSL是TCP报文的大生存时间, 因此TIME_WAIT持续存在2MSL的话 就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失 (否则服务器立刻重启, 可能会收到 来自上一个进程的迟到的数据, 但是这种数据很可能是错误的);
滑动窗口
对每一个发送的数据段, 都要给一个ACK确认应答. 收到ACK后再发送下一个数据段. 这样做有一个比较大的缺点, 就是性能较差. 尤其是数据往返的时间较长的时候.
既然这样一发一收的方式性能较低, 那么我们一次发送多条数据, 就可以大大的提高性能(其实是将多个段的等待时 间重叠在一起了).
- 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。上图的窗口大小就是5000个字节(五个段)
- 发送前五个段的时候,不需要等待任何ACK,直接发送;
- 收到第一个ACK后,滑动窗口向后移动,继续发送第五个段的数据,依次类推;