TCP协议文章目录
- 1. UDP和TCP协议的比较
- 1.1 UDP协议
- 1.2 TCP协议
- 1.3 特点比较
- 2. TCP协议建立连接的三次握手
- 3. TCP协议断开连接的四次挥手
- 4. TCP协议的几个特性
- 4.1 确认应答
- 4.2 超时重传
- 4.3 连接管理
- 4.4 滑动窗口
- 4.5 流量控制
- 4.6 拥塞控制
1. UDP和TCP协议的比较
UDP和TCP作为被传输层广泛采用的两种协议,那么它们之间有什么区别呢?在不同的使用场景下又该怎样去选择呢?下面我们先来研究一下两种协议的报文格式。
1.1 UDP协议
1.2 TCP协议
1.3 特点比较
类型 | 特点 | 性能 | 应用场景 | 首部字节长度 |
---|---|---|---|---|
TCP | 有连接、可靠传输、面向字节流、全双工 | 传输效率相对慢、消耗资源较多 | 短信、邮件传输 | 20-60 |
UDP | 无连接、不可靠传输、面向数据报、全双工 | 传输效率相对快、消耗资源较少 | 语音、直播数据 | 固定8字节 |
ps:①TCP报头长度已经被占用了20字节,报头表示范围为4位,且单位是4字节,所以首部字节长度为20-60字节。②UDP的报头长度是固定的8字节,可以再看下1.1UDP的报文格式。
2. TCP协议建立连接的三次握手
这里的握手是一个形象的比喻,代表客户端和服务器端建立连接的过程。我们首先应当清楚为什么TCP协议在建立连接时要经过三次握手的过程才能真正的确立连接,为什么不是两次?又为什么不是四次?我们一起来向下探索。
🐱TCP协议的三次握手过程
TCP的三次握手过程是为了能够在传输数据前确保连接已经建立并且能够正常通信,这也是TCP协议可靠性的一种体现, TCP协议建立连接时三次握手的过程如下图所示:
这里的SYN和ACK就是1.2TCP报文中我们所提到的TCP核心字段中的内容,TCP协议可以根据这些核心字段中的内容做出相应的举动。
- 在这里,SYN是指的是一个同步报文段,由客户端发出到服务端,代表客户端要与服务器端建立连接,同时客户端已经申请了用于通信的资源;
- 服务器端收到客户端发送的SYN同步报文段后,回复给客户端一个SYN同步报文段代表自己也已经申请了用户和其进行通信的资源,同时恢复一个ACK确认报文段,这里的ACK可以理解为服务器端要告知客户端自己收到了建立连接的申请;
- 客户端收到服务器端发送的SYN同步报文段后会再给服务器端回复一个ACK确认报文段,告知服务器自己也收到了它的同步相应。
到这里,客户端和服务器端都能够得到自己的发送数据和接收数据的功能是正常的,这样的三次握手过程就实现了通信双方都能够确定对方与自己的连接是可靠的,也就完成了通信连接的建立,保证了通信的可靠性。
我们可以举一个下图中罗密欧和朱丽叶打电话联络感情的的栗子来更好的理解下TCP三次握手建立可靠性连接的这个过程:
🐱为什么握手不能是两次?又为什么不能是4次?
- 首先不能是两次的原因是:缺少了最后一次客户端的发送的ACK报文,服务器端无法确定自己的发送功能和客户端的接收功能是否正常,也就无法建立可靠性连接。
- 其次也可以是四次握手,但是,没必要。通过三次握手过程已经能够让双方知道自己的发送和接收功能以及对方的发送和接收功能是正常的了。如果将第二次握手客户端发送的ACK确认报文和SYN同步报文分开发送,还需要再经过一次发送数据的封装过程,这显然降低了我们数据的传输效率,舒适没必要。
3. TCP协议断开连接的四次挥手
我们知道,通过客户端和服务器端的三次握手过程就可以实现双方的可靠性连接,并且客户端和服务器端都存在着相应的通信资源。TCP的四次握手过程是为了确保能够在断开连接前完全的释放客户端以及服务器端彼此之间用于通信的资源。TCP断开连接时的四次握手的过程如下图所示:
这里的FIN和ACK同样也是我们在上边提到的TCP报文格式中的核心字段。TCP协议可以根据这些字段信息做出相应的举动。与TCP三次握手建立连接不同的是,这里的第一次FIN结束报文段客户端或者服务器端都可以先主动发出。
- 断开连接时,首先由一方(A)发出FIN退出报文段,告知B“我要断开连接了”;
- 另一方(B)接收到A发出的FIN退出报文段后回复给A一个ACK确认报文段,告诉A“我收到了”,之后再次回复给A一个FIN退出报文段告诉A“我准备好了”;
- A收到B的FIN退出报文段后,再次回复B一个ACK确认报文段,告知B“我关闭了”
- B收到A的ACK确认报文段后就真正的关闭通信资源。
到这里,TCP的四次挥手过程就能够让客户端和服务器端都关闭彼此之间用于通信的系统资源,彻底的断开连接并有效的释放资源。
🐱在这个过程中,如果最后一次ACK确认报文段丢包了,B就收不到A的确认请求,也就无法真正的关闭资源,这种情况应该怎么办?
- 我们的A在发送出最后一次ACK确认报文段后并不会立即关闭资源,而是等待2倍的MSL时间后才去真正的关闭通信资源。这时候如果A发送的ACK确认报文段丢包了,那么在MSL时间内收不到A的确认报文段ACK就会认为是自己的FIN退出报文段没有到达A,于是重新发送一个FIN报文段,A在2倍的MSL时间内可以再次接收到B发送的FIN退出报文段,于是也就可以再次回复ACK确认报文段,这就有效降低了A的最后一次ACK确认报文段丢包带来的影响。
【ps】MSL:表示网络中任意两点之间传输所需的最大时间,这个时间在系统中通常是可以配置的。
4. TCP协议的几个特性
4.1 确认应答
这是TCP通信协议能够保持可靠性的核心特性。所谓可靠性,就是指数据发送出去之后发送方能否知道接收方已经收到了消息。TCP实现确认应答的办法是接收方收到消息后,会返回给发送方一个ACK确认报文段,代表自己已经收到消息了。那么,发送的ACK确认报文段中如何表示自己收到的消息具体是哪些呢?这就用到了TCP协议报文中的32位序号和32位确认序号,如下图中:
🐱TCP中一种关于消息编号的方式是针对每一个字节都进行编号,这个步骤如下,也可以参照下边的图片解释:
- 消息的发送方将发送消息内容按照字节进行编号,将这个序号放在TCP报文中的32位序号的位置,发送给接收方;
- 接收方收到报文后,在解析时就可以根据这个编号填写自己返回的ACK确认报文段中的确认序号位置上的编号来回复给发送方自己具体收到了哪些消息。
4.2 超时重传
TCP中的超时重传是指消息的发送方如果在指定的时间内没有接收到消息的接收方返回过来的ACK确认报文段,就会认为是自己的消息或者对方的响应ACK在网络传输中丢包了,此时消息的发送方就会重新发送依次消息报文段。这个过程如下图所示:
🐱如果超时重传的消息又因为网络缘故丢包了,怎么办?
上面这种情况的概率是很低的,但是也难以完全避免。出现超时重传仍然丢包后,我们的消息发送方会继续消息重传,在尝试几次后,如果还是没有得到消息接收方返回的ACK确认报文段,就会认为彼此之间的通信网络出现了严重故障,主动断开TCP连接。
🐱如果导致超时重传的原因是消息的接收方响应的ACK报文段丢了,那么发送方重新发送消息,不会导致消息接收方接收的消息重复吗?
答案是不会的。这种情况TCP协议的制定者已经为我们规避掉了,如果是因为ACK确认报文段丢包导致的超时重传,那么消息的接收方在第二次收到消息内容时,会启动TCP内部的消息去重功能进行消息的管理,从而应用层感知不到这条再重复的消息,也就没有什么影响了。
【ps】关于TCP通信协议的消息去重:消息的接收方会将接收到的消息放在“接收缓冲区”中,这里的“接收缓冲区“可以视作是一个阻塞队列。当消息的接收方接收到消息后,会将消息放在这个接收缓冲区中,同时检查这个数据在消息缓冲区中是否已经存在了,如果存在就直接丢弃,确保应用层程序拿到的数据时不重复的。
4.3 连接管理
这就是我们在文章目录:
2. TCP协议建立连接的三次握手
3. TCP协议断开连接的四次挥手
提到的TCP建立可靠性连接的三次握手过程以及断开连接的四次挥手过程,详细内容及过程看这篇文章的第2和3部分。
4.4 滑动窗口
我们在以开始进行TCP和UDP传输层协议的比较时,就提到了UDP的通信效率是要高于TCP的,那么,TCP就宁愿久居人下吗?不!
滑动窗口机制就是TCP在保证可靠性传输的前提下,尽可能地提升传输效率的一种方法。滑动窗口的做法本质上就是批量的发送数据,批量的接收ACK确认报文段如下图所示:
🐕滑动窗口接收ACK报文段并不是等待接收到发送的一组消息(这组消息的个数又称为滑动窗口的窗口大小)对应的所有ACK确认报文段后才继续发送接下来的消息,而是收到发送消息对应的任一确认报文段ACK后,就根据这个确认报文段继续发送一条或者一组数据,然后重复上述过程,这个过程如下图所示:
🐱如果在重传的过程中,丢包了怎么办呢?这里的丢包分为两种情况:响应的ACK确认报文段丢包(数据包已经到达)和发送的数据丢包,我们一起来看下这两种情况:
- 响应的ACK确认报文段丢包:
我们在上边提到了TCP报文中有一个确认序号。这个确认序号代表着在这个序号之前的数据都收到了,因此ACK丢包的情况并没有什么影响,可以通过后续的ACK包来间接判断前面的数据包已经到达了,如下图所示:
- 发送的数据包丢包:
这种丢包可就有影响了,接收方因为丢包而导致数据缺失必然会影响应用层程序的执行。那么TCP是如何解决这种数据包丢失的问题呢?
当数据包丢失时,接收方由于无法获取到这个数据包,会在接收到其它数据包时返回一个包含丢失数据包应有的编号信息的ACK确认报文,这样,当发送方发送其他数据包时,接收方一直返回丢失数据包的编号信息来通知发送方”我需要编号XXX的数据报,快发给我!!“,终于,在接收方连续发送三次带有丢失数据包对应编号的ACK确认报文段后,发送方反应过来了,于是重新发送丢失的数据包;这样,接收方在拿到这个丢失的数据包后接着返回包含接下来需要的数据报编号信息的确认报文段ACK,发送方接收到ACK后便继续进行窗口滑动,继续发送数据。这就是窗口滑动时发送数据的数据包丢失时TCP的应对策略!也可以参照下面这张图片:
4.5 流量控制
流量控制机制是对滑动窗口机制的一种延伸。通过上边的滑动窗口的学习,我们知道滑动窗口的窗口大小越大,发送方发送数据就越快,数据的传输效率就相对越高。但是我们不能让发送发的窗口大小不受控制的增长,虽然发送方发送数据的效率高了,但是接收方万一处理不过来数据,其接收缓冲区倍填满了,发送方吴国还在继续发送数据,那这些数据就会被接收方直接丢弃,这不相当于做了无用功,发送方后来还得继续重发丢失的数据吗?为了解决这种问题,于是就有了这里的流量控制。
流量控制的关键在于能够衡量接收缓冲区的大小从而动态调整滑动窗口的窗口大小实现相对高效率的数据传输过程。这样的数据传输过程也可以理解为生产者消费者模型,发送数据的一方就作为数据的生产者,接收数据的一方就作为数据的消费者,而接收数据一方的接收缓冲区就作为模型的交易场所,当这个”交易场所“的剩余空间较大时,就可以认为消费者(消息的接收方)的消费能力是比较强的,从而可以让生产者(消息的发送方)加快生产效率;而当”交易场所“的剩余空间较小时,我们认为消费者(消息的接收方)的消费能力是有所欠缺的,这个时候就应当让生产者(数据的发送方)适当降低些生产效率。
🐱那么,接收方该如何告诉发送方自己的接收缓冲区还有多大容量呢?
答案是,通过TCP协议报文中的16为窗口大小来告知,我们终于又能够拿出镇楼图了,如下图所示:
4.6 拥塞控制
与流量控制类似,拥塞等待机制也是对滑动窗口机制的一种延伸。处理方案是,通过”试验“的方式,逐渐调整滑动窗口的窗口大小(即消息的发送速度),找到一个合适的大小的窗口值发送数据。
在不清楚网络环境的情况下,如果在一开始发送数据时就设置较大的窗口大小一次发送许多个数据,可能由于网络拥堵而造成"事倍功半"的效果。为了解决这种问题,于是就有了我们的拥塞等待机制。拥塞控制的处理方案即流程图如下: