文章目录
- 🌲TCP协议的概念
- 🌸TCP协议段格式
- 🌸TCP的特性
- 🌳TCP原理详解
- 🌸确认应答机制(安全机制)
- 🌸超时重传机制(安全机制)
- 🌸连接管理(安全机制)
- 🌸滑动窗口(效率机制)
- 🌸流量控制(安全机制)
- 🌸拥塞控制(安全机制)
- 🌸延迟应答(效率机制)
- 🌸捎带应答(效率机制)
- 🎍面向字节流的粘包问题
- 🌸思考:
- 🌴TCP异常情况
- ⭕TCP小结
🌲TCP协议的概念
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
🌸TCP协议段格式
- 源/⽬的端⼝号: 表⽰数据是从哪个进程来, 到哪个进程去
- 32位序号/32位确认号: 后⾯详细讲
- 4位TCP报头⻓度: 表⽰该TCP头部有多少个32位bit(有多少个4字节); 所以TCP头部最⼤⻓度是15 *4 = 60
- 6位标志位:
◦ URG: 紧急指针是否有效
◦ ACK: 确认号是否有效
◦ PSH: 提⽰接收端应⽤程序⽴刻从TCP缓冲区把数据读⾛
◦ RST: 对⽅要求重新建⽴连接; 我们把携带RST标识的称为复位报⽂段
◦ SYN: 请求建⽴连接; 我们把携带SYN标识的称为同步报⽂段
◦ FIN: 通知对⽅, 本端要关闭了, 我们称携带FIN标识的为结束报⽂段 - 16位窗⼝⼤⼩: 后⾯再说
- 16位校验和: 发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含TCP⾸部, 也包含TCP数据部分.
- 16位紧急指针: 标识哪部分数据是紧急数据;
- 40字节头部选项: 暂时忽略;
🌸TCP的特性
-
TCP提供一种面向连接的, 可靠的字节流服务;
-
在一个TCP连接中,仅有两方进行彼此通信。广播和多播不能用于TCP;
-
TCP使用校验和, 确认应答和超时重传机制来保证可靠传输;
-
TCP使用累积确认
-
TCP使用滑动窗口机制来实现流量控制,通过动态改变窗口的大小进行拥塞控制
🌳TCP原理详解
TCP对数据传输提供的管控机制,主要体现在两个方面:安全和效率。
这些机制和多线程的设计原则类似:保证数据传输安全的前提下,尽可能的提高传输效率。
🌸确认应答机制(安全机制)
确认应答机制图示如下
TCP将每个字节的数据都进⾏了编号. 即为序列号.
每⼀个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下⼀次你从哪⾥开始发.
🌸超时重传机制(安全机制)
有了确认应答后,我们的可靠性已经得到大大的提升,但是UDP中出现的丢包问题还是没有得到解决
意思也就是主机A向主机B发送数据,一定时间后,并没有收到应答,这里主机A没有收到应答报文有两种情况
• 主机A发送数据给B之后, 可能因为⽹络拥堵等原因, 数据⽆法到达主机B;
• 如果主机A在⼀个特定时间间隔内没有收到B发来的确认应答, 就会进⾏重发;
但是, 主机A未收到B发来的确认应答, 也可能是因为ACK丢失了;
这两种情况都当成第一种情况处理,客户端会进行重传数据
因此主机B会收到很多重复数据. 那么TCP协议需要能够识别出那些包是重复的包, 并且把重复的丢弃掉.
这时候我们可以利⽤前⾯提到的序列号, 就可以很容易做到去重的效果.(这里的主机B这里接收就像一个优先级的队列,我们会对传来的数据按照序列号进行排序,如果序列号相同,该队列还可以起到一个去重的效果)
那么, 如果超时的时间如何确定?
• 最理想的情况下, 找到⼀个最⼩的时间, 保证 “确认应答⼀定能在这个时间内返回”.
• 但是这个时间的⻓短, 随着⽹络环境的不同, 是有差异的.
• 如果超时时间设的太⻓, 会影响整体的重传效率;
• 如果超时时间设的太短, 有可能会频繁发送重复的包;
TCP为了保证⽆论在任何环境下都能⽐较⾼性能的通信, 因此会动态计算这个最⼤超时时间.
• Linux中(BSD Unix和Windows也是如此), 超时以500ms为⼀个单位进⾏控制, 每次判定超时重发的
超时时间都是500ms的整数倍.
• 如果重发⼀次之后, 仍然得不到应答, 等待 2500ms 后再进⾏重传.
• 如果仍然得不到应答, 等待 4500ms 进⾏重传. 依次类推, 以指数形式递增.
• 累计到⼀定的重传次数, TCP认为⽹络或者对端主机出现异常, 强制关闭连接.
🌸连接管理(安全机制)
由于这里内容较多,博主单独写了一篇进行介绍,大家可以博主写的TCP三次握手四次挥手详解进行学习观看。
🌸滑动窗口(效率机制)
博主在这篇TCP滑动窗口与流量控制和拥塞控制有详细讲解
🌸流量控制(安全机制)
博主在这篇TCP滑动窗口与流量控制和拥塞控制有详细讲解
🌸拥塞控制(安全机制)
博主在这篇TCP滑动窗口与流量控制和拥塞控制有详细讲解
🌸延迟应答(效率机制)
如果接收数据的主机⽴刻返回ACK应答, 这时候返回的窗⼝可能⽐较⼩.
假设接收端缓冲区为1M. ⼀次收到了500K的数据; 如果⽴刻应答, 返回的窗⼝就是500K;
• 但实际上可能处理端处理的速度很快, 10ms之内就把500K数据从缓冲区消费掉了;
• 在这种情况下, 接收端处理还远没有达到⾃⼰的极限, 即使窗⼝再放⼤⼀些, 也能处理过来;
• 如果接收端稍微等⼀会再应答, ⽐如等待200ms再应答, 那么这个时候返回的窗⼝⼤⼩就是1M;
⼀定要记得, 窗⼝越⼤, ⽹络吞吐量就越⼤, 传输效率就越⾼. 我们的⽬标是在保证⽹络不拥塞的情况下
尽量提⾼传输效率;
那么所有的包都可以延迟应答么? 肯定也不是;
• 数量限制: 每隔N个包就应答⼀次;
• 时间限制: 超过最⼤延迟时间就应答⼀次
🌸捎带应答(效率机制)
在延迟应答的基础上,我们发现,很多情况下,客户端服务器在应用层也是 “一发一收” 的。
意味着客户端给服务器说了 “How are you”,服务器也会给客户端回一个 “Fine, thank you”;
那么这个时候ACK就可以搭顺风车,和服务器回应的 “Fine,thank you” 一起回给客户端
🎍面向字节流的粘包问题
首先我们需要明确的是
我们在开发中,创建一个TCP的socket,同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区
• 调⽤write时, 数据会先写⼊发送缓冲
• 如果发送的字节数太⻓, 会被拆分成多个TCP的数据包发出;
• 如果发送的字节数太短, 就会先在缓冲区⾥等待, 等到缓冲区⻓度差不多了, 或者其他合适的时机发
送出去;
• 接收数据的时候, 数据也是从⽹卡驱动程序到达内核的接收缓冲区;
• 然后应⽤程序可以调⽤read从接收缓冲区拿数据;
• 另⼀⽅⾯, TCP的⼀个连接, 既有发送缓冲区, 也有接收缓冲区, 那么对于这⼀个连接, 既可以读数据,
也可以写数据. 这个概念叫做 全双⼯
由于缓冲区的存在,TCP程序的读和写不需要一一匹配,例如
- 写100个字节数据时,可以调用一次write写100个字节,也可以调用100次write,每次写一个字节;
- 读100个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以一次read 100个字节,也可以一次read一个字节,重复100次;
那什么是粘包问题呢?
首先要明确,粘包问题中的 “包” ,是指的应用层的数据包。
在TCP的协议头中,没有如同UDP一样的 “报文长度” 这样的字段,但是有一个序号这样的字段。
站在传输层的角度,TCP是一个一个报文过来的。按照序号排好序放在缓冲区中。
站在应用层的角度,看到的只是一串连续的字节数据。
那么应用程序看到了这么一连串的字节数据,就不知道从哪个部分开始到哪个部分,是一个完整的应用层数据包。
那么如何避免粘包问题呢?
归根结底就是一句话,明确两个包之间的边界
- 对于定长的包,保证每次都按固定大小读取即可;例如上面的Request结构,是固定大小的,那么就从缓冲区从头开始按sizeof(Request)依次读取即可;
- 对于变长的包,可以在包头的位置,约定一个包总长度的字段,从而就知道了包的结束位置;
- 对于变长的包,还可以在包和包之间使用明确的分隔符(应用层协议,是程序猿自己来定的,只要保证分隔符不和正文冲突即可)
🌸思考:
对于UDP协议来说,是否也存在 “粘包问题” 呢?
- 对于UDP,如果还没有上层交付数据,UDP的报文长度仍然在。同时,UDP是一个一个把数据交付给应用层。就有很明确的数据边界。
- 站在应用层的站在应用层的角度,使用UDP的时候,要么收到完整的UDP报文,要么不收。不会出现"半个"的情况。
🌴TCP异常情况
进程终止:进程终止会释放文件描述符,仍然可以发送FIN。和正常关闭没有什么区别。
机器重启:和进程终止的情况相同。
机器掉电/网线断开:接收端认为连接还在,一旦接收端有写入操作,接收端发现连接已经不在了,就会进行reset。即使没有写入操作,TCP自己也内置了一个保活定时器,会定期询问对方是否还在。
如果对方不在,也会把连接释放。另外,应用层的某些协议,也有一些这样的检测机制。
例如HTTP长连接中,也会定期检测对方的状态。例如QQ,在QQ断线之后,也会定期尝试重新连接
⭕TCP小结
为什么TCP这么复杂?因为要保证可靠性,同时又尽可能的提高性能。
可靠性:
• 校验和
• 序列号(按序到达)
• 确认应答
• 超时重发
• 连接管理
• 流量控制
• 拥塞控制
提⾼性能:
• 滑动窗⼝
• 快速重传
• 延迟应答
• 捎带应答
其他:
• 定时器(超时重传定时器, 保活定时器, TIME_WAIT定时器等)
基于TCP应用层协议
• HTTP
• HTTPS
• SSH
• Telnet
• FTP
• SMTP
当然, 也包括你⾃⼰写TCP程序时⾃定义的应⽤层协议;