目录
1.TCP 协议报文格式
1.1.端口号
1.2 首部长度 和 选项
1.3 保留位
1.4 检验和
1.5 32位序号和确认序号
2. TCP的重要机制
2.1 确认应答
2.2 超时重传
2.3 连接管理
2.3.1 三次握手
2.3.2 四次挥手
2.4 滑动窗口
2.5 流量控制
2.6 拥塞控制
2.7 延时应答
2.8 捎带应答
2.9 面向字节流
2.10 异常情况的处理
3. TCP 与 UDP 的区别
1.TCP 协议报文格式
1.1.端口号
与UDP报文结构相似,TCP报文中 端口号 也占2个字节,包括 源端口号 和 目的端口号。
端口号 就是用来区分一台主机上不同的应用程序。
源端口号信息的意义在于,发送的数据 来自 发送方主机的 哪个程序。
目的端口号信息的意义在于,要把数据发送到 接收方主机的 哪个程序。
1.2 首部长度 和 选项
首部长度 规定的是 TCP数据报 报头的长度,单位为4字节。选项也是报头的一部分。TCP报头,最短是20个字节,最长是60个字节。
当TCP报文的报头长度超过20字节时,超过的部分长度就来源于选项。因此TCP报头最短20字节(不包含选项),最长60字节(选项最多为40字节),选项都是以4字节为单位。
1.3 保留位
保留位是 预留的6位长度,以备不时之需。
1.4 检验和
与UDP协议报文中的检验和是一致的。
1.5 32位序号和确认序号
TCP报文中有序号和确认序号,这与TCP的机制相关,下面继续说明。
2. TCP的重要机制
传输层有许多协议,TCP和UDP是传输层中两大重要协议。
在探讨TCP协议之前,首先要了解 可靠传输 这一概念。
可靠传输,也就是 发送方 将数据 发送给 接收方之后,接收方收到数据 会给发送方发送一个应答报文,这样发送方就知道了自己发送的数据对方收到了。如果发送方没有收到接收方的应答报文,则发送方就会知道刚刚发送的数据出了问题,就会采取相应的措施,比如重传。这么一个通信过程,就称之为 可靠传输。
TCP协议相比于UDP协议来说,TCP最核心的特点就是 可靠传输。而TCP的可靠传输,是要靠TCP的许多机制来支持的,那么TCP有哪些机制可以帮助实现 可靠传输 这一特性呢?
2.1 确认应答
确认应答,是TCP的最核心机制,支持了TCP的可靠传输。
确认应答:发送方把数据发送给接收方之后,接收方会反馈给发送方一个应答报文(ACK),发送方收到了这个应答报文,就知道刚刚发送的数据对方收到了。
就比如A给B发送了一条消息“在吗”,B回复了“在”,这样A就知道B收到了自己的信息,就可以继续和B聊天了。
此时有一个问题,在网络传输数据的过程中,往往会发生 发送的数据出现"先发后至"的情况。那么这时,A给B发送了多个数据包,B能否 根据 发送方发送数据的先后顺序 进行 有序应答呢?
答案是肯定的,即使A发送的数据报,在网络传输的过程中出现了“先发后至”的情况,B也能够根据 发送方的发送顺序 来进行 有序应答。那么依靠什么来实现有序应答的呢?
就是 根据 TCP报文中的 序列号 和 确认序列号 来完成有序应答的。
A给B发送了1-100序号的数据,B收到后,告诉A自己收到了(ACK),可以接收A第201个序号的数据了。通过序列号,接收方B就可以针对A发送的每个数据进行对应的应答了。
TCP协议初心就是为了实现 可靠传输,而TCP实现可靠传输的最核心机制就是 确认应答。
那么,通信双方如何区别每个数据包是普通数据,还是应答报文呢?
答案就在TCP报文中的ACK标志位
如果ACK标志位为1,则这个数据就是一个应答报文,"确认序号字段"生效。
如果ACK标志位是0,则这个数据就是一个普通报文,"确认序号字段"就不生效。
当然,要实现TCP的可靠传输,并非只依靠 确认应答 就可以了,还需要TCP其它机制进行辅助。
2.2 超时重传
超时重传,顾名思义就是 超过一定的时间就进行重传。什么时候会触发超时重传呢?
数据包在网络进行传输的过程中,常常会出现"丢包"现象。
此时,"丢包"存在两种情况:
A给B发送了一个数据包,如果数据包在传输的过程中出现了"丢包"情况,那么A就无法收到B的应答报文了。站在发送方A的角度,A也无法分辨出到底是 数据包没传输到B,还是B的应答报文丢了。无论是上述的哪种情况,TCP都会触发重传机制,即A重新将该数据包发送给B。重传机制,大幅度提升了 数据能被成功传输给B的 概率。
至于什么时候再进行重传,等待时间是可配置的。
【总结】:当A发送数据给B,等待一定时间之后,若A还没收到B发来的应答报文(数据包发生"丢包"情况),此时就会触发超时重传,A就会重新给B发送该数据。如果多次重传,仍然没有收到B的应答报文,A就会直接放弃与B连接了。
对于以下这种情况,A成功将数据包发送给B了,但B发送的确认应答报文"丢包"了,于是A又重传了,那么此时B就会收到两份一样的数据包。那么B会怎么办呢?
TCP有一个"接收缓冲区",也就是一个 内存空间,会保存发送方 发送过来的数据,以及数据的序号。如果接收方发现当前读取的数据,和"接收缓冲区"中的数据有重复,就会把当前数据包丢弃。也就是说TCP的"接收缓冲区"有"去重"功能。TCP的"接收缓冲区"不仅有去重功能,还有 "整队"功能,也就是当A 发送的多个数据包 成功到达 B方时,接收方的"接收缓冲区"会 按其发送顺序 整理好 数据包的到达顺序。这一功能可以确保 发送数据的顺序 和 读取数据的顺序 是对应的,就不会出现"牛头不对马嘴"的情况了。
2.3 连接管理
TCP协议的特点是:有连接,可靠传输,面向字节流,全双工。
因此,使用TCP协议来进行网络通信时,通信双方会首先与对方建立好连接,然后再继续通信。
这里的 "建立连接" 也就是 通信双方 保存 对方相关信息 的一个过程。
连接管理 包括 "三次握手" 和 "四次挥手"。
"三次握手" 是 通信双方 建立连接 的过程;"四次挥手" 是 通信双方 释放连接 的过程。
2.3.1 三次握手
"三次握手"这么一个建立连接的过程,也就是通信双方在正式通信之前的一个打招呼过程。一般主动发起 建立连接的一方是 "客户端",被动接受的一方是"服务器",也就是接收方。在这个过程中,通信双方都会给对方发送一个 简单的,没有业务数据的 数据包。
比如,A要和B通信,在正式通信之前,A和B就会有一个"三次握手"的过程来建立连接,A会问"你在吗?",B会回复"我在,你还好吗?",A也会回复"我很好!"。这么一个过程就代表通信双方建立好了连接,可以正式通信(聊天)了。
三次握手的过程如下图:
这就完成了握手,双方也就建立好了连接。双方在三次握手的过程中,双方都会给对方发起SYN同步报文段(没有载荷的数据包),也都会给对方回复一个ACK。
其中接收方B的SYN和ACK可以合并成一个数据进行发送,因为SYN和ACK都是由内核触发的,同一个时机触发,可以合并发送。
"三次握手"是为了与对方建立连接,这个过程有什么意义呢?
TCP的核心特性是 可靠传输,确认应答 和 超时重传 是 实现可靠传输 的一个大前提,若通信双方的网络环境有故障,又如何能实现可靠传输呢?"三次握手"这么一个过程的意义就在于此:
(1)确认当前网络是否畅通
(2)确认 通信双方的 发送和接收能力 是否正常。
(比如A给B发消息,A问道“你在哪?”,B回答"我在家"(保证A的发送能力 和 B的接收能力是正常的),A回复“好的”(保证A的接收能力 和 B的发送能力是正常的)。)
(3)通信双方在"三次握手"的过程中,针对一些重要参数进行"协商"。
(要"协商"的信息之一就是 这次发送方要发送的数据序号是从几开始,一般都会与上次通信的数据序号有很大差异,避免接受方收到的是以前的旧数据。)
通信双方如何区分这是 同步报文段还是 普通的数据包,也是通过SYN标志位来区分的。
若SYN标志位为1,则"同步报文段"标志位生效。
若SYN标志位为0,则"同步报文段"标志位不生效。
2.3.2 四次挥手
"四次挥手" 就是 通信双方 释放连接 的一个过程。通信双方都可以主动发起释放连接。
四次挥手,为什么是四次而不是三次,主要是接收方发送的ACK与FIN一般不能合并发送。因为这里ACK和FIN触发的时机不一样。ACK是内核响应,B一旦接收到A发来的FIN,就会立刻回复ACK。而只有等到B这边代码调用了socket的close方法,才会触发FIN(应用程序的代码触发)。至于等B发出ACK多久后,B才会调用close方法就无法确定了。因此释放连接是"四次挥手"。
"四次挥手"这个过程,通信双方都会给对方发送FIN结束报文段 和 ACK应答报文,与"三次挥手"不同的地方就在于,B收到FIN后,会立刻回复ACK,等待应用程序调用close方法之后,再回复FIN,最后A再回复一个ACK,"四次挥手"的过程就完成了,A与B就彻底断开了连接。
哪一方主动断开连接,哪一方就会进入"TIME_WAIT"状态,这个状态的意义是 防止最后一个ACK丢失。
如果没有"TIME_WAIT"状态,且最后一个ACK丢失的话,站在B的角度,等待一定时间之后,仍没收到A发来的ACK,B就会以为自己发出的FIN报文丢失了,于是B会重传FIN。但A发送了ACK之后就释放连接了,无法再处理FIN报文了。那么B重传的FIN就没人处理了,则B重传FIN也就没有意义了。
A有了TIME_WAIT这个状态,发出最后一个ACK后,会等待一定的时间,确保ACK能成功发送给B。即使此时ACK丢失了,B也会重传FIN,A也能及时处理FIN,发送ACK给B。
至于TIME_WAIT会等多久,如果网络上两个节点通信消耗的最大时间是MSL,则TIME_WAIT的时间就是2MSL。
2.4 滑动窗口
确认应答,超时重传,连接管理,这三个机制都是在 帮助实现 TCP的 “可靠传输” 。也正是由于TCP的可靠传输这一特性,使得TCP的协议更加复杂,数据传输效率也会比较低。因此 滑动窗口 就起到了一个"亡羊补牢"的作用,尽量 提高TCP的数据传输效率。
如果没有"滑动窗口",TCP协议传输数据时,发送方只有等到应答报文后,再传输下一个数据。这个过程会有许多的等待时间,使得传输效率非常低。
有了"滑动窗口"这一机制,TCP就可以"批量传输"数据了。当然批量传输也存在一定上限,达到上限后,会统一等待ACK。在不等待ACK的情况下,批量最多发送的数据量 就称为 “窗口的大小”。
如下图,此时 A -> B 批量发送了5份数据,B也要给A回复5个ACK应答报文。当前 A已经达到了窗口大小,在收到ACK之前,不能往下发送数据了。因此,A在等待B回复第一个ACK。
现在的问题是,A是等收到5个ACK之后,再往下发送数据,还是收到一个ACK就可以往下发送一份数据呢?
答案是 不用等待所有的ACK到达,只要有一个ACK到达之后,A就可继续往下发送数据了。因此,滑动窗口越大,TCP的数据传输效率就越高,当然窗口大小也是有一定上限的。 ·
如果在数据传输的过程中,出现了"丢包"情况,会如何处理呢?
既然数据"丢包"了,那么就会有重传,此时的重传 与 上面的 超时重传 有一定的区别。
如果是 ACK丢失了:
此时不需要重传。如果1001,2001,3001的ACK丢失了,但是4001的ACK收到了,就说明4001之前的数据也都传输成功了
如果是 数据包 丢了:
此时A会发现B这边连续几个ACK都在索要2001的数据,A就知道2001数据丢失了,就会重传2001,当2001-3000数据成功到达B这边后,B就会索要6001的数据了。
这个重传的过程中,没有什么额外的操作,只要A发现B重复几次索要同一段数据,A就会知道该段数据丢失了,就会重传该段数据,这个重传的过程比较迅速,可以说是 快速重传。没有丢的数据就不用重传,A可以继续往下发送数据了。
如果通信双方的数据传输量不大,也不频繁的话,TCP就还是以 普通的确认应答 和 超时重传 的模式来传输数据。
如果通信双方的数据传输量很大且频繁的话,就会进入滑动窗口模式,以 快速重传 的方式来处理数据。
2.5 流量控制
虽然滑动窗口越大,发送数据就越快,数据传输效率越高。但当滑动窗口达到一定大小后,发送方发送的数据太快,接收方处理不过来了,这就会出现"丢包"的情况,数据发生丢包后,发送方还得去重传,这就得不偿失了。因此 TCP并不会让滑动窗口无限增大,这就有TCP的流量控制来处理了。 因为TCP的初心是为了实现"可靠传输",希望在此基础上 再尽可能 提高传输效率。
流量控制这一机制,则是站在接收方的角度,制约发送方的数据传输速率。也就是为了保证 发送方的发送速率 不超过 接收方的接收能力。
A发送的数据包 成功发送给B后,数据会先放在 接收方的"接收缓冲区"中,B就会从“接收缓冲区”中读取数据,B读过的数据,该数据就可以从接收缓冲区中删除了。
接收方 接收数据的能力 就 表现在 接收缓冲区中的 数据量。
接收缓冲区中数据越多,则接收方的数据处理能力就越低。也就是接收缓冲区的剩余空间越小,则说明接收方的数据处理能力越弱。反之,接收缓冲区的剩余空间越大,说明接收方处理数据能力越强。
接收方每次收到数据后,都会通过 ACK 把 接收缓冲区的剩余空间大小 返回给发送方,发送方就会按照 剩余空间这个数值 来调整下一轮的数据发送速率。
如果接收方反馈给发送方的 接收缓冲区剩余空间为0,则发送方就会暂停发送数据了。那么发送方会暂停多久才会再开始发送数据呢?
发送方暂停发送数据后,等待一定的时间,发送方会周期性的(防止"窗口探测包"丢包)给接收方发送一个"窗口探测包",不携带业务数据。"窗口探测包"是为了触发接收方发送ACK(这个数据包中就会有"接收缓冲区"剩余空间的数值),接收方的接收缓冲区不满时,接收方回复给发送方的ACK就会告诉发送方可以 继续发送数据了。
接收缓冲区的 剩余空间的数值保存在TCP数据包中:
2.6 拥塞控制
流量控制 这一机制是 制约发送方 的 发送数据的速率,而 "拥塞控制"则是考虑整个 通信路径 的情况。接收方的接收数据能力 可以 通过 "接收缓冲区" 来 衡量,而整个通信路径 结构更为复杂,导致 通信路径的状况 难以有 "具象化"的衡量标准。因此,只能通过 "实验"的方式,来确定当前的一个数据传输量。
拥塞控制 这一机制,就是通过"实验"的方式,来逐渐调整 发送方的数据传输速率:
最开始,TCP会让 发送方 以一个比较小的窗口来发送数据,等窗口大小达到一定程度后,可能中间的传输节点就会发生问题,就可能会出现"丢包"问题。发送方发现 数据"丢包"后,就会缩小发送窗口大小,如果仍在"丢包",就会 继续缩小。如果不"丢包"了,就会继续 尝试 增大窗口。在这个"实验"的过程中,发送方在不断调整窗口大小,从而达成一种 "动态平衡"的状态。
此图就是拥塞控制的调整窗口大小的过程。当窗口到达一定程度时,会降低窗口的增大幅度;当出现"丢包"时,会大幅度缩小窗口,设置新的窗口门限值,再慢慢增加窗口大小,从达到一种"动态平衡"的状态。 因此,"拥塞控制"的意义就在于 保证数据在传输的过程中尽可能避免"丢包"问题,且尽可能的提高传输效率。
2.7 延时应答
一般情况下,发送方A 发送数据包 给 接收方B 后,B会立刻返回一个 ACK。但有的时候,B不会立刻回复ACK,而是 等待一会儿 再返回ACK给A,本质上也是为了提升数据传输效率。
延时应答,会延时返回ACK,给 接收方 更多的读取 "接收缓冲区"中数据的 时间,有了这一段延时,接收方可以有更多读取数据 的时间,也就可以让"接收缓冲区"中的剩余空间更大(发送方的发送"窗口"大小 会根据 接收方"接收缓冲区"剩余空间大小 来 调整),也就可以让发送方有更大的发送"窗口",也就使得发送方可以调整到更大的数据传输速率。这就是为什么说延时应答本质上是提升数据的传输效率。
2.8 捎带应答
在日常上网的过程中,我们往往会访问 某个页面,这是一个 向服务器发出 访问请求的过程;服务器收到请求,也就会 作出 响应,我们才能成功看到想访问的页面。
请求 和 响应 的过程如下:
"捎带应答"机制 则是在 延时应答 的基础上 进一步提高 传输效率。
在请求与响应的过程中,当服务器收到 请求后,会立刻回复 给发送方 一个ACK,当服务器计算好响应后,再返回RESPONSE。
由于TCP的"延时应答"机制,ACK有时候可能不会立刻回复,而是等待一定的时间再回复ACK,也正是由于等待的这一段时间,服务器有可能也计算好了响应。因此,在这种情况下 服务器可以把 ACK与RESPONSE 一起返回给 客户端,提高了一定的传输效率。
2.9 面向字节流
TCP协议的特性之一是 面向字节流,也就是TCP传输的数据是以"字节"为单位。
那么通信双方进行交互的过程中 存在一个问题:当发送方 发送了多个数据包 放在"接收缓冲区"时,接收方会去读取 "接收缓冲区"中的数据,这个读取数据的过程 就可能会发生"粘包"的问题。
什么是"粘包"问题:
"接收缓冲区"中存放的是 发送方 发送的多个数据包,这些数据都是以字节为单位紧紧放在一起的。接收方的应用程序每次读取数据时,可以一次读取一个或多个字节的数据,目标是为了获得完整的数据包。但接收方的应用程序难以辨认清楚 数据从哪里到哪里是一个完整的数据包。
如下图,发送方发送了三个数据包,分别是"you" "is" "book",接收方的应用程序读取数据的时候,就不知道 从哪里到哪里是一个完整的数据包
因此,在接收方读取数据的过程中,要避免"粘包"问题。
那么如何避免"粘包"问题:通过定义好应用层协议,明确应用层数据包之间的边界。
以下是两种引入边界的方式:
(1)引入分隔符:在每个数据包(末尾添加一个结束符,当应用程序读到该符号时,就知道一个完整的数据包已经读取完毕了。
(2)引入长度:在每个数据包的数据开头,标明此数据包的数据长度,接收方读取数据时,都会根据该长度读取相应的字节数,进而就能读到一个完整的数据包了。
2.10 异常情况的处理
在数据传输的过程中,难免会出现各种意外情况,比如 进程崩溃,主机关机,主机断电,网络断开等。使用TCP协议传输数据时,遇到这些情况,TCP也会有相应的处理方式。
(1)进程崩溃
进程异常崩溃了,也就意味着进程结束了,则该进程的文件描述符表也就释放了,相当于调用了socket的close方法。此时就会触发正常的"四次挥手"的过程。
(2)主机关机
在关机的时候,也会触发 进程强制终止 的操作,也是正常的"四次挥手"过程,从而释放连接。但是,当进程销毁了,也就意味着主机也关机了。若在进行"四次挥手"的过程中,接收方发来的ACK和FIN迟到了,主机已经关机的话,就无法处理ACK和FIN了,也就是不会给对方回复一个ACK了。此时,站在对方的角度,没等到ACK,就会超时重传 FIN,若重传几次仍没有响应,自然就会放弃与对方的连接了(删除 之前保存对方的相关信息)。
(3)主机断电
主机断电,一般是意外情况,并非主动断电。那也就意味着 主机还没来得及 关闭进程,主机就关机了。那也因此,主机没有时间与对方进行"四次挥手"。
此时,有两种情况,发送方断电 和 接收方断电。
发送方断电:这种情况,接收方会在等待对方传数据过来,等了一定时间,仍没消息,无法确定发送方是什么情况。于是 接收方 会 周期性的 给 发送方 发送 不带业务数据的 数据包(TCP的"心跳包"机制),期望收到对方的应答。如果发送了多个该数据包,仍没有得到回应,接收方就会单方面释放连接了。
接收方断电:发送方 发送数据后,会等待对方回复ACK,等待一定时间后,仍没等待ACK,同样是触发超时重传,且触发TCP连接重置功能,发起"复位报文段"。如果复位报文段发过去之后,仍没有效果,也就会释放连接了。
(4)网络断开
与 主机断电 类似,若是发送方断网了,接收方会发送"心跳包",无回应,就 释放连接。若是接收方断网,发送方触发超时重传,且发起"复位报文段",无回应,就 释放连接。
3. TCP 与 UDP 的区别
TCP与UDP协议是传输层中的两个非常重要的协议 ,这两个协议有一些共同点和不同点:
相同点:都支持 全双工通信(通信双方都能给对方发送信息)
不同点:
(1)UDP面向数据报,TCP面向字节流。
也就是说UDP协议处理数据的基本单位是 数据报,TCP协议处理数据的基本单位是 字节。
(2)TCP是有连接的,UDP是无连接的。
意思是 使用TCP协议完成通信时,通信双方会保存对方的相关信息,建立好连接,然后再通信。而使用UDP协议通信双方不会保存对方的信息,不用建立连接,直接通信。
(3)TCP支持可靠传输,UDP是不可靠传输。
可靠传输,也就意味着 双方在通信时,发送方能够知道 自己发送的信息 对方是否收到了,如果对方没收到,自己也会采取相应措施(比如 重传)。
TCP的 确认应答 是 实现"可靠传输"的核心机制,超时重传 和 连接管理 是进一步保障"可靠传输"。因为TCP的机制相比于UDP要 复杂许多,也就意味着 传输效率 无法与UDP 相比,于是 TCP的滑动窗口、流量控制、拥塞控制、延时应答、捎带应答等机制 则是在尽量提高TCP协议的传输效率,尽量弥补 与UDP的传输效率的 差距。
UDP与TCP协议各有各的适用场景,各有各的优势。UDP的优势在于传输效率高,TCP的优势在于 "可靠传输"。
以上则是TCP协议的相关重要知识。