日升时奋斗,日落时自省
目录
1、确认应答
1.1、序号编辑
2、超时重传
3、连接管理
3.1、三次握手
3.2、四次挥手
4、滑动窗口
5、流量控制
6、拥塞控制
7、延时应答
8、捎带应答
9、面向字节流
10、异常情况
TCP协议:
特点:有连接的、可靠传输、面向字节流、全双工
可靠性传输:是TCP内部的机制,和编码关系不大,咱们感知的不是很清楚
TCP详细图解
(1)首部长度
4位首部长度:一个TCP报头,长度是可变的,不是像UDP一样固定8个字节
因此,首部长度就描述了TCP报头具有多长,另外,选项之前的部分是固定长度(20字节)
首部长度 - 20字节 ==得到的就是选项部分的长度
20字节表示的就是 源端口号+目的端口号+序号+确定序号+窗口大小+验证和+紧急指针+首部长度的大框框
首部长度解释:4bit位
注:重要提示!!! 首部长度的单位 不是字节 是4字节
举例(针对选项和首部长度的联系):
如果首部长度值为5 ,表示整个TCP的报头长度是20字节(相当于没有选项)
选项=首部长度-20字节=20-20=0
如果首部长度值为10,表示整个TCP的报头长度是40字节
选项=首部长度-20字节=40-20=20字节
TCP报文=TCP报头(首部)+TCP载荷
(首部包含了选项及以上 载荷就是数据)
(2)保留位
保留(resevered)位:顾名思义 占个位置起来先不用,进行程序开发的时候,其中一个重点考虑的事情就是可扩展性。有些功能可能暂时不需要,但不代表以后不会再开发。
此处TCP的保留6位,也是为了以后的扩展考虑的,对于网络协议来说,扩展升级,是一件成本极高的事情,这里以UDP提一下这方面的弊端,UDP报文长度是2字节,因此一个包最大64kb,现在能不能把UDP协议升级一下,让UDP能够支持更大的长度,比如把报头长度使用4字节表示;此情况理论上可以,但是修改后的操作系统如何让全世界能够进行更新升级(升级设备涉及广泛 计算机、路由器、手机等等都需要)不是技术问题,而是使用问题,很难做到替代当前所有的设备
“保留位”的引入此时升级操作就会成本低不少,如果后续TCP引入了一些新的功能,就可以使用这些保留位字段
注:对于TCP本来的报头结构的影响是比较小的,老的设备即使不升级也能兼容
(3)选项
option(选项)=>optional(可选的,可有可无的)此处的选项相当于对这个TCP报文的一些属性进行解释说明的
TCP内部的工作机制:
TCP 可靠传输 是怎样做到可靠的
可靠传输不是说一定能把消息发给接收方(因为这个可靠跟网线没有关系,物理设备都断了,那就不可能发过去了)
此处是尽可能把数据传输过去,同时,如果还是传输不过去,至少能知道
提示:实现可靠传输的最核心机制
TCP进行可靠性传输,最主要就是靠这个确认应答机制
其实简单化就是 A 给 B 发了个消息,B收到之后就会返回一个 应答报文(ACK),此时,A收到应答之后,就知道了刚才发的数据已经顺利到达B了
更加复杂的情况:
很明显,此处这里的应答就错乱的,表达意思出现歧义
总结:网络“后发先至”现象客观存在,无法避免,因此应答报文到达的顺序也是可能发生变动的。此时就需要考虑如何规避这种顺序错乱带来歧义
如何解决上述“后发先至” ,看的出来以上出现问题的原因在于顺序上出错,办法很简单,给传输的数据,和应答报文都进行编号。
引入序号以后此时就不怕顺序乱了,及时顺序乱了,序号就是最好判断方法
那序号是怎样编号的,当然不是以上编辑的序号不是按照“一条两条”这样的方式来编号的...TCP是面向字节流的,TCP的序号也是按照字节编号的
TCP的字节的序号是依次累加的,这个依次累加的过程对于后一条数据说,起始字节的序号就是上一个数据的最后一个字节的序号,每个TCP数据报报头填写的序号只需要写TCP数据的头一个字节的序号即可。
TCP知道了头一个字节的序号,再根据TCP报文长度,可以知道每个字节的序号,确认序号的取值,是收到的数据的最后一个字节的序号+1
总述:
<1>TCP可靠传输能力,最主要就是通过确认应答机制来保证的
<2>通过应答报文,就可以让发送方清楚的知道传输是否成功
<3>引入了序号和确认序号,针对多组数据进行详细的区分
讨论确认应答的时候,只是讨论了顺利传输的情况,同样会产生问题:丢包???
丢包,涉及到两种情况:
<1>发的数据丢了
<2>返回的ACK丢了
发送方看到的结果,就是没有收到ACK区分不了是哪种情况,都是丢包的情况,针对丢包TCP是要处理的,因为丢包是一个概率性事件,(而且通常情况下,丢包概率已经很小了),重新发一下这个数据报,其实还是很大的概率成功传输(这里下面在做详细解释)
TCP直接引入重传机制:
在丢包的时候,就要重新再发一次同样的数据(到底当前这个传输,是丢包了,还是ACK走的慢,还没有被接收到)
此处TCP就直接引入了一个时间阈值,发送方发了一个数据之后,就会等待ACK,此时开始计时。如果在时间阈值之内,也没有收到ACK,无论是否ACK是在路上,还是彻底丢了,都视为是丢包了
超时重传,超过一定时间,还没响应,就重新传输
这个超时时间,具体是多少ms? 这个时间可以配置,并且不同系统上面的默认值可能存在差别
以上带有重复数据或者操作(关键时候)可能会带来重大影响,但是针对以上重复数据或者操作带来的影响
TCP对于这种重复数据的传输,是有特殊处理,去重,TCP存在一个“接收缓冲区”这样的存储空间,(接收方操作系统内核里的一段内存)每个TCP的socket对象,都有一个接收缓冲区(发送缓冲区)
主机B 收到的 主机A的数据
其实是B的网卡读取到数据了,然后把这个数据放到B的对应的socket的接收缓冲区中,此处更像是一个优先级队列,根据数据序号,TCP很容易识别当前接收缓冲区里的这两条数据是否是重复的
自己本有这个功能,如果重复了,则把后来的这份数据就直接丢弃了,保证了应该程序调用read读取到的数据,一定是不重复的
TCP使用这个接收缓冲区,对收到的数据进行重新排序,使应用程序read到的数据是保证有序的(与发送顺序一致)
总结:由于去重和重新排序机制的存在,发送方只要发现ACK没有按时到达,就会重传数据,即使重复了,即使顺序乱了,接收方会处理好的
相应问题:
超时重传次数:重传的数据是否存在再次丢包?有可能,所以可能会重传N次(此处的N不是无限的),重传不会一直进行,重传几次还没有结果其实也就没有必要了,可能此时的网络出现问题了
举例:
传输丢包概率假设为:5% 第二次传输丢包的概率:5%*5%=0.25%
第三次还丢包的话,可想而知 0.25%*5% 等于的数字是何等的小,连续丢包能丢几次呀,概率实在太小了,所以多次丢包大可能是网络出现问题了,因为正常丢包不会太多次
因此,重新传输到一定次数的时候,就不会再继续重传,会认为网络故障,接下来TCP会尝试重置连接(网络重连),如果重置还是失败了,彻底断开连接
注:超时重传次数,可以配置
重传的时候,第一次重传和第二次重传,超时时间间隔,并不一样,执行重传时间上不是均匀的,一般情况,重传的次数越大,超时时间间隔就越大,因为超时重传次数越多,重传成功的概率就越小,此时重传的太快也是浪费资源,不如多等等(总结:超时时间变大,重传的频率降低)
总述:
<1>可靠传输是TCP最核心的部分,TCP的可靠传输就是通过 确认应答 + 超时重传 两者都是具体体现,共同支撑整体的TCP可靠性
<2>其中确认应答描述的是传输顺利的情况
<3>超时重传描述的是传输出现问题的情况
连接管理:涉及到建立连接和断开连接
那连接(Connection)又是什么???如何做到连接的
以结婚类比:大体上结婚还是要有法律认可的结婚手段才是连接,法定结婚就是领证;领取的结婚证两个人一人一份,内容相同,但是针对两个人各自关注点不同
男方:认为了连接上了就是 这个结婚证上写 我老婆是这个女人
女方:认为了连接上了就是 这个结婚证上写 我老公是这个男人
这就是建立连接完成了
TCP 建立连接 :
断开连接:针对实际上来说,离婚就是断开连接
此处A和B把自己存储的连接信息(数据结构)删除,连接就是断开了
建立连接(三次握手)
通信双方各自要记录对方的信息,彼此之间要相互认同(图解)
上面看着也可以叫做“四次握手” 也能叫“三次握手”
只要不合并就可以叫做“四次握手”,针对这个就有两个问题???
为啥中间这两次要合并?不合并可以吗?
合并原因是:封装分用, 每次都要封装分用,都是要消耗成本的,所以必须合并,降低封装分用的成本
再问两次握手行不行???
答案:针对计算机肯定不行(图解)
总结:三次握手,本质上是“四次”交互,为了降低封装分用的成本,必须进行合并操作;通信双方,各自要向对方发起一个“建立连接”的请求,同时,再各自向对方回应一个ACK。
三次握手另外一个重要作用:验证通信双方各自的发送能力和接收能力是否正常,同时也一定程度的保证了TCP传输的可靠性(起到的不是关键作用,辅助作用)
此处再举一个实例:
三次握手的意义:
<1>让通信双方各自建立双方的“认同”
<2>验证通信双方各自的发送能力和接收能力是否可行
<3>在握手过程中,双方来协商一些重要的参数
以上都是简单以实例表现出出握手,以上实例每次的握手是一次通信同时也是一种协议
四次挥手就是断开连接,“挥手”和“握手”都只是形象的叫法,都是客户端服务器的数据交互
四次挥手和三次握手非常类似,都是通信双方各自向对方发起一个断开连接的请求,同时再各自给对方一个回应
以上是举例理解四次挥手 (下面详细解析)
TIME_WAIT保持连接不断开,那能保持多久??约定一个时间为 2MSL
如果经历了2MSL时间还是没有收到 重传的FIN 就认为这个ACK就正常到达了(认为对方没有重传FIN)
2MSL 指的是,互联网上两个节点之间数据传输消耗的最大时间,(约定俗成)MSL具体的准数是多少??一般情况下是60s(秒)
针对以上解释对四次挥手可能有点迷离:
四次挥手要做的事情这里简短的比喻一下:
<1>A发送 FIN
<2>B发送ACK
<3>B发送FIN
<4>A发送ACK
仅仅以上四次交互构成四次挥手(最后一个ACK没有丢包的情况下)
TCP依存可靠性,所以传输效率就降低了很多比不上UDP(无可靠性)(可靠性和传输效率本身是相互矛盾的),TCP也有一些其他方法来补救传输效率,但是补救后仍然不及UDP,只是为了减少传输效率上针对TCP的影响;
滑动窗口的本质就是降低了确认应答,等待ACK消耗的时间
缩短渠道批量发送,批量等待,把多份等待时间合并成了一份
滑动窗口基本理解就是如上图解析,但是有数据传输就有可能会丢包(以下解释丢包情况)
这是一种干预发送的窗口大小的机制
滑动窗口,窗口越大,传输效率就越高(一份时间,等的ACK就越多),当然窗口不能是无限大
<1>完全不等ACK,可靠性能否保障画上问号
<2>窗口太大,也会消耗大量的系统资源
<3>发送速度太快,接收方处理不过来,发了也白发
接收方的处理能力,就是一个很重要的约束依据,发送方法的速度,不能超出接收方的处理能力
流量控制要做的工作就是这个,根据接收方的处理能力,协调发送方的发送速度
那既然接收方的处理能决定发送的速度,如何探测接收处理能力??
直接看接收方接收缓冲区的剩余大小
流量控制和拥塞控制共同决定发送方的窗口大小是多少
流量控制:针对的是接收方的处理能力
拥塞控制:描述传输过程中的中间节点的处理能力
发送方按照滑动窗口的方式发送,此时“窗口大小”描述了发送速率
拥塞窗口和流量控制的窗口,共同决定了发送方实际的发送窗口(拥塞窗口和流量控制窗口的较小值)
同样也是提升效率的机制,也是在滑动窗口的基础之上
滑动窗口的关键,让窗口大小在有限的范围之内扩大一点,传输速度就快一点
因此要做的是,在接收方能够处理的前提下,尽可能的把把窗口大小放大一点
延时收到数据之后,不是立即返回ACK 而是稍微等会再返回
等待的时间里,接收方的应用程序,将接受缓冲区的数据给消费一下,此时剩余的空间会更大
也是提高效率的方式,在延时应答的基础上,引入的捎带应答
服务器客户端程序,最典型的模型,就是“一回一答” (此处举例解释捎带应答)
面向字节流 引入问题:粘包问题
重点:针对解决方法
<1>约定好分隔符
<2>约定每个包的长度
两个任选其中一个就可以了
异常情况不可抗拒因素
(1)进程关闭的情况
<1>进程崩溃了
进程没了,对应的PCB就没了,对应的文件描述符表就释放了,相当于socket.close()
此时内核会继续完成四次挥手,此时其实仍然是一个正常断开的流程
<2>主机关机(按照正常流程关机)
主机正常关机要先杀进程,然后才正式关机(杀死进程的过程中,也就是和上面一样触发四次挥手)
(2)进程来不及关闭
<1>主机掉电
<2>网络断开
假设是接收方掉电了(断网)
显然是来不及挥手,发送方仍然在继续发数据,发完数据要等待ACK,都断电了,ACK是真的等不到了,超时重传,再怎么重传,也收不到ACK,重传几次,还没有应答,尝试重置TCP连接,显然这个重置也会失败,最终面临的就是放弃连接
假设是发送方掉电了(断网)
接收方发现,没数据了,没数据是发送方挂了,还是发送方要组织下语言,稍等会再发???
接收方不知道现在发送方的特殊情况 那先等
接收方需要周期性的给发送方发送一个消息,确认下对方是否还工作正常
周期性发送消息:心跳包(确认通信双方是处在正常的工作状态)
<1>心跳,是周期性的
<2>如果心跳结束,那就是没了