TCP和UDP都是传输层的协议.
文章目录
- 1. TCP协议格式
- 2. TCP连接及断开连接管理
- 2.1 三次握手
- 2.2 四次挥手
- 3. TCP可靠性机制
- 3.1 确认应答
- 3.2 超时重传
- 4. 滑动窗口
- 5. 流量控制
- 6. 拥塞控制
- 7. 延迟应答
- 8. 捎带应答
- 9. 面向字节流
- 10. 异常情况
1. TCP协议格式
TCP的特点是有连接,可靠性,面向字节流,全双工.
- 源端口和目的端口
TCP的源端口与目的端口一样,表示端口号. - 32位序号和32位确认序号我们放在后面的知识里学习
- 首部长度,代表一个TCP报头.TCP的首部长度不固定,图中的首部长度为4字节,也就是16个比特位,如果首部长度为6,那么整个TCP报头就为48个比特位.
- 保留位,提高TCP的可扩展性.以防TCP想引入新的功能,操作的成本很高,这时就可以使用这些保留位字段,在不影响原有结构的基础上进行升级.
- 校验和与UDP原理相同.
- 窗口大小和紧急指针我们放在后面详细解释.
- 选项,这个部分是对TCP报文的一些属性进行说明.
2. TCP连接及断开连接管理
正常情况下,TCP连接要进行三次握手,断开连接要进行四次挥手.
TCP连接及断开连接问题,是面试中常考的问题,我们需要画图解释.
2.1 三次握手
如下图,是客户端与服务器建立连接的流程图.
图中我们要注意两个状态
A. 服务器的第二个状态,LISTEN,代表服务器服务器已经做好连接的准备,可随时与客户端建立连接.
B. 客户端与服务器的最后一个状态都是ESTABLISHED,代表此次连接建立成功,可以正常通信.
如果面试题中,要求描述三次握手的过程,我们只需画出下图.
三次握手的本质是,各自要向对方发起一次建立连接的请求,并在接收到对方的请求时立即返回一个ACK.
- 首先,必须由客户端主动发起建立连接的请求,发送的syn为同步报文段
- 服务器在收到建立连接请求后,会立即返回一个ack.
- 之后,由服务器发起建立连接的请求.
- 最后,客户端在接收请求后立即返回一个ack.
有同学会有疑问了,为什么一共有四次交互,却是三次握手呢?
由于第2,3步之间时间间隔很短,可以合并.所以协议将这两步封装到一起.所以成了三次握手.也可以节省开销.
那两次握手可不可以呢?答案是不行.为啥呢?
其实,三次握手还有一个意义,就是验证客户端和服务器的收发功能都没问题.
如下图,当客户端发送请求时,只有服务器返回ack了,才确定客户端的发送能力没问题,服务器也保证自己的接收能力没问题.
而只有当客户端在接收请求后返回ack时,才保证了服务器的发送能力没问题,客户端的接受能力没问题.
之后才可以进行数据的发送.
2.2 四次挥手
如下图,是断开连接四次挥手的具体流程.
这里有两个重要的状态
- 服务器端的CLOSED_WAIT,此时正等待close方法关闭socket.
- TIME_WAIT,客户端进入到TIME_WAIT状态时代表四次挥手已经完事了.但是有一点要注意,在服务器端还没收到ACK之前,TCP连接还不能断.由于最后这个ACK有丢包的可能,一旦这个ACK没发过去,服务器也就无法得知自己的FIN是否成功发送,在一段时间之后,服务器会再次尝试发送一次FIN,一旦连接断开了,就发不了了.
所以,客户端进入到TIME_WAIT状态后,会等待2MSL(MSL为两个节点之间数据传输消耗的最长时间,可人工规定),过了这个时间,确认未丢包后,则彻底释放连接.
面试中如问到这个问题,画出下图即可.
- 客户端的应用程序主动调用socket的close方法或者进程退出,会触发FIN.注意,断开连接可能由客户端发起,也可能由服务器发起.
- 客户端接收到断开请求,自动返回一个ACK.
- 服务器的应用程序执行到对应的close方法,会触发FIN.
- 客户端接收断开请求,自动返回ACK
那么这里第2,3步为什么不能合并呢?由于服务器端在接收到断开请求后,还能做一些事情(根据代码而定),所以第2,3步之间有一定的时间差,不能进行封装.
3. TCP可靠性机制
3.1 确认应答
确认应答是实现TCP可靠性的最核心机制.
- 应答报文,确认应答机制由应答报文(ACK)实现.A给B发个消息,当B收到消息后,会立即给A返回一个ACK,那么A收到ACK后就知道自己的消息发送成功了,来保证可靠性.
- 确认序号,另外,要保证消息的顺序不能乱.所以,我们需要给消息进行编号,为确认序号.序号按字节排序,第一个字节为序号1.
如下图所示,第一组数据报为1-1000字节,确认序号为1,第二组数据报为1001-2000字节,确认序号为1001,当确认序号为1001时,就确认了1-1000组的数据报已经成功发送.当确认序号为2001时,就代表2000之内的数据已正确发送.
3.2 超时重传
如果发送端迟迟没有收到接收端返回的ACK,就无法确认数据是否被正确传输.
TCP引入了重传机制,程序员可以根据具体情况,设置一个时间域值,在发送端发送数据之后,开始计时,一旦超过这个时间阈值,无论数据是否丢失,都视为数据丢失,发送端重新传送数据.
但这里会出现一个问题,一个消息传的很慢,超过时间阈值之后仍未传达.此时,发送端重新发送数据,那么这同一个消息可能会发送了两次.
如何解决这个问题呢?TCP的接收端有接收缓冲区,会以优先级队列的方式,将消息进行排序,将相同的消息进行去重.
4. 滑动窗口
由于每发送一条消息,就要返回一个ACK,之后才可以发送下一条消息,很消耗时间.因而,TCP设置了滑动窗口机制.
如下图,一次发送多条消息,之后,等待这一组数据的ACK.节省了很多时间
把不需要等待就能直接发送的数据的最大的量称为窗口大小.
需要注意的点是,在客户端等待一组数据的ACK时,并不需要等待所有ACK都到,再集中发下一组数据,而是到一部分就可以发下一部分.例如客户端收到1001的ACK,就代表1-1000之间的ACK已经全部被确认,就可以发送下一个1000数据报即(3001-4000)了.
若有1-1000出现丢包情况,则只需重传1-1000的数据报即可,之后在接收缓冲区进行重新排队.
5. 流量控制
滑动窗口有几个问题.
- 若一次性发太多消息,但接收端处理不过来,就白发了.发送端的发送速度不能超过接收方的处理能力.
- 窗口太大,也会消耗过多系统资源.
所以,要根据接收缓冲区的大小确定下一次发送的窗口大小.
每发送一组信息,就返回一次接收缓冲区大小,从而调整发送方的窗口大小.当窗口为0时,暂停发送.
之后,发送方定期发送窗口探测报文,触发ACK查看窗口大小的机制.
6. 拥塞控制
流量控制和拥塞控制共同决定发送方窗口大小.
滑动窗口有个问题,就是一开始的时候,并不知道窗口大小,若此时贸然发送大量数据,可能加重网络拥塞.
因而TCP引入拥塞控制,引用慢启动机制,先发送少量数据,确认一下当前网络的拥堵状态,在确定之后的窗口大小.
如下图,为拥塞窗口变化图.
- 刚开始,窗口大小为1,试探网络拥塞程度
- 发现未丢包,以指数形式扩大窗口
- 引入慢启动阈值,达到阈值,这里阈值为16,则停止指数增长,开始进行线性增长.
- 网络拥塞达到极限,窗口重新回到慢开始阶段.
- 进行新一轮的重复.
7. 延迟应答
延时应答也是提升效率的机制.是在收到数据后,不要立即返回ACK,而是延时以下,处理一下接受缓冲区里的数据,以便下一次的发送窗口能更大一些.
8. 捎带应答
由于上面的延时应答机制,客户端发送数据,服务器并不会立即返回ACK,而是会延迟一下.而此时,若到了服务器发送下一条消息的时机,服务器可把上次的ACK捎带上,一同发送给发送方.
9. 面向字节流
TCP面向字节流,接收缓冲区会把收到的数据放在一起,从而难以区分读到那算是完整的一条数据.这种情况称为粘包问题.
解决办法
- 约定好分隔符
- 约定好每个包的长度
10. 异常情况
- 进程终止,释放文件描述符表,正常断开连接.
- 主机关机,先关进程,再关机,与上一种情况相同
- 主机断电/网线断开,若是接收方断电了,发送方一直等不来ACK,超时重传几次依旧等不到,就放弃连接了.若是发送方断电,接收方发现没数据了,会周期性向发送方发送一个消息(心跳包),确认对方是否工作正常.