💕"Echo"💕
作者:Mylvzi
文章主要内容:网络原理(一)
一. 应用层
应用层是和程序员联系最密切的一层,对于应用层来说,程序员可以自定义应用层协议,应用层的协议一般要约定好以下两部分内容:
- 根据需求,明确要传输哪些信息
- 明确信息传输的格式
以打开手机订外卖为例
需求:打开手机,想要搜索周边卖黄焖鸡米饭的餐馆
根据请求,明确传输信息:需要传输:用户名,经纬度(根据经纬度来搜索周边黄焖鸡米饭的店铺)
请求:(以下使用一种纯文本的数据组织格式)
用户名,len,lan;
响应:商家列表,包括商家名称,店铺照片,距离,评分等等
常见的数据组织格式有以下三种:
- xml(叉ml) 一种以标签作为信息传输的基本格式,和html类似
< userId > 用户名< /userId >
xml的劣势就在于可读性相较于纯文本格式更高,但是需要传递的数据量变多(标签也需要被传输),需要额外的带宽 - json格式 最常使用的一种格式,符合面向对象编程的思想,将要传输的信息看做一个Object对象,内部使用"键值对"这样的格式来组织数据,相较于xml格式,json可读性更好,更简洁,额外需要的带宽也减少
除了特别追求性能的场景,json广泛应用于各种应用层协议中用于组织数据 - protobuffer 简称pb格式是由谷歌开发的一种数据组织格式,会将需要传输的数据转化为二进制数据,追求最极致的数据传输速度,但是可读性很差
程序员可以自定义协议,同样的,也有一些现成的协议供我们使用,最常见的就是HTTP协议(超文本协议),HTTP会在后续章节作更详细的介绍
二.传输层
网络模型中的传输层关注起点和终点,即通信双方的具体位置,主要是利用端口号来标记位置的,计算机的具体位置不在传输层中记录,而是在网络层中的ip协议之中,不要把端口号和IP地址混为一谈!!!
端口号是应用程序/服务器的标识,在一个计算机中,存在着非常多的应用程序/服务器.通信时要明确是哪一个应用程序进行数据的传输.此时就可以利用端口号进行标识.端口好就像是身份证一样,每个服务器都有其单独的端口号!
端口号一般是两个字节的数据,数据表示范围为 0 -65535,其中 1- 1023被称为知名端口号.这些端口号被一些知名服务器给占用了,程序员指定端口号时不能和知名端口号相同,否则就会出发端口冲突
传输层中最常见的两种协议是TDP和UDP协议,UDP出现的时间更早,速度更快,但是有自己的缺陷,TDP出现较晚,可以看做是对UDP的一个升级版!!
TCP和UDP的区别:
TCP:有连接, 可靠传输, 面向字节流, 全双工
UDP:无连接, 不可靠传输, 面向数据报, 全双工
学习协议,重点就是掌握协议的报文格式,即数据组织管理的格式
1.UDP协议的具体格式讲解
首先一个完整的UDP报文格式由两部分组成:
- UDP报头
- UDP载荷
UDP载荷就是从应用层中打包好的应用层协议数据报,存储要传输的数据
UDP报头中存放与UDP协议相关的一些信息
报头和载荷之间的拼接本质上是字符串拼接(尽管内部的数据都是以二进制的形式存储的)
UDP报头大小为8个字节,具体可以分为四部分,每部分2个字节
1.源端口号:发送方的端口号
2.目的端口号:接收方的端口号
3.UDP报文长度:整个UDP报文的总长度,即UDP报头和UDP载荷的总长度,由16位的数据组成,数据范围是 0 -65535 ,也即最大的报文长度是64 kb,64kb的数据放在当下已经是非常小的一个数据量了,这也是UDP的一个局限性,每次最多传输的数据太小了!而TCP很好的解决了这个问题
4.校验和(checksum):这是网络传输中一个很重要的概念,用于保证数据传输的准确性.原始数据经过一定的算法简化为更加简单.体积更小的字符串,这个字符串就会保留在UDP的报头之中,用于接收方进行数据校验,要理解校验和还要明确一点,即数据在传输的过程中是会发生传输错误的,数据的传输底层上是通过光/电信号进行传输的.传输的过程中难免会受到周围环境的影响,导致光/电信号发生突变,进而数据传输就发生错误,发生错误就要检验错误,校验和就是一种检验错误的手段
以下是一个简单的校验和检验过程
- 发送方将数据整理打包为Data1,根据算法计算出校验和为checksum1
- 发送方将Data1和checksum1通过UDP传输给接收方
- 接收方接收数据,整理数据为Data2,接收checksum1
- 接收方按照相同的算法对Data2进行计算,得到校验和checksum2
- 如果checksum1和checksum2相同,那么Data1和Data2理论上是相同的(也有可能不同,但是是极小的概率,工程上忽略不计)
如果checksum1和checksum2不同,那么Data1和Data2一定不同.此时数据的传输就发生了错误,程序员可以采取其他措施来解决问题
常见的计算校验和的算法有:
1.CRC算法(循环冗余算法): 这也是UDP中使用的计算校验和的算法,这种算法比较简单粗暴,是对整个要传输的UDP报文的每个字节进行累加计算,将最终的结果保存在两个字节的校验和之中,尽管在计算过程中可能会出现溢出问题,但是也不影响校验和的计算.发送/接收双方就通过这样的算法来计算出各自的校验和,来检验数据传输的正确性
但是这种算法也存在缺陷,两个不同的原始数据计算出的校验和相同的概率较大(此时这种概率就无法避免了)
2.md5算法: 这是一种经典的计算校验和的算法,算法的具体计算过程不需要掌握,只需要掌握md5算法的三个特性:
- 定长:无论原始数据的长度是多少,经md5算法计算出的校验和的长度是固定的
- 分散:指的是两个内容相似的原始数据(只有一个字节的差距)最终得到结果也是相差很大的
- 不可逆:将原始数据加密为校验和很容易,但是想通过校验和得到原始数据,计算量特比大,以至于在理论上是不可行的
md5算法其实非常适合于哈希算法,在哈希表中,存储key首先要先将其转化为数组下标,再在对应的位置存入数据,我们要求根据key值得到的下标要尽可能的分散,只有这样,才能降低哈希冲突发生的概率
这里提供一个md5加密的网站
链接: link
基于UDP协议的应用层协议
- NFS:网络文件系统
- TFTP:简单文件传输协议
- DHCP:动态主机配置协议
- BOOTP:启动协议(用于无盘设备启动)
- DNS:域名解析协议
当然,也包括你自己写UDP程序时自定义的应用层协议。
在UDP的代码中也可以体现出UDP的四个特点:
1.无连接: UDP在传输额的过程中不会保留对端的信息,需要显式指定传输的目的地
2.不可靠传输:这个性质在代码中体现不出来
3.全双工: 构建一个socket对象,既可以发送消息又可以接收消息
4.面向数据报: 传输的基本单位是数据报DatagramPacket
2.TCP协议的具体格式讲解
TCP相较于UDP来说是一个更加复杂的传输层协议,内置了很多机制,其中TCP中最重要的机制就是可靠传输,可以说TCP设计的初心就是为了保证数据的可靠传输
先来看TCP数据报的格式
和UDP一样,TCP数据报也是由两部分组成:报头 + 数据载荷,相较于UDP来说,TCP的报头之中还有一些其他不一样的数据,下面进行一一讲解
源端口号和目的端口号和UDP中的是一样的,这里不做讲解
4位首部长度
首先我们要知道TCP的报头长度不是固定的,是可变的,TCP的报头长度通过调整选项内部的数据来改变包头的长度,报头长度最短是20字节(没有选项),最长是60字节(选项最长为40字节)
首部就是报头,4位首部长度就是4位报头长度,即通过4个比特位的数据是报头长度的数值部分,数据范围是 0 -15,这里的一个基本单位是4字节
保留6位
这是TCP设计者的一个小心机,我们知道UDP报头固定长度就是64kb,有时候用起来就很麻烦,且难以扩展,TCP预留6位就是为了将来哪一天能用到而做的预备
16位检验和(checksum) 和UDP的校验和相同
要了解TCP数据报报头其他部分的含义还需要通过TCP的特性来进行引入,其实报头剩余部分都是和TCP设计的初心–可靠传输相关
所谓的可靠传输不是保证数据百分之百的传输给接收方,这是不现实的.可靠,指的是负责任,想得周到,即对发送出去的数据负责任,具体体现在TCP在发送完数据之后,会判断接收方是否接收到数据,如果没有接收到就会采取一定的措施,而不是像UDP一样,发送完数据之后万事大吉,什么也不管了,不在乎对方是否接收到数据
可靠传输的实现主要是依赖于以下几个机制:
1.确认应答
确认应答是可靠传输的核心,发送方发送完数据之后,如果接收方成功接收到数据,就会返回一个ack(acknowledge)数据报(应答报文),表示当前接收方已经接收到了你传输过来的数据,发送方收到应答报文,就知道传输成功了,通过这样的一个机制就做到了信息传输的可靠
网络世界复杂繁琐,通信的过程并不是简单的一问一答,且因为网络的不稳定性,往往会产生一些特殊情况,比如"后发先至"这样的问题,确认应答机制对这些情况做出了预防
后发先至
在这种情况下,确认应答机制通过序号和确认序号这样的机制来避免上述情况的发生
TCP在传输数据的时候会对发送的数据添加序号,这样双方在接收和传输数据时就会根据序号是否相同来确保数据顺序的一致性,如下图
TCP报文中:
总结来说,确认应答机制需要注意的地方有以下两点:
- 要保证传输数据和应答报文顺序要一致
- 通过序号这种方式来避免"后发先至"的问题,确保应用程序能够按照正确的顺序来理解发送的消息
实际上,发送的消息的序号并不是简单的一个数据给一个序号这样的方式,而是以字节为单位进行序号的标记(TCP是面向字节流的)
下面是一个示例:
也就是说,确认序号会被ack数据报携带,通过ack数据报传输给发送方,那么如何判断一个数据报是ack数据报还是普通的数据报呢?
确认应答机制是TCP实现可靠传输的核心,辅以其他机制共同保障了TCP的可靠传输
2.超时重传
确认应答其实是一种比较理想的情况,他是建立在网络环境通畅的前提下,实际上,网路世界错综复杂,网络也常常是不稳定的 ,要考虑到数据可能因为网络阻塞导致丢包
的出现
丢包有两种情况:
但是站在发送方的角度,他是无法区分这两种情况的,发送方判断是否成功发送的依据是有没有接收到ack,所以,只要没有收到ack数据报,发送方就会触发超时重传!
重传是解决丢包问题的一个很好的解决方案,实际上一个丢包发生的概率是很低的,如果两次重传都失败了,可能网络出现了重大故障,此时就没有进行通信的必要了
说明:对于"返回的ack数据报丢失"这种情况,实际上要传输的数据报已经被成功传输过去了,但因为返回的ack丢失,发送方就要重新进行传递相同的数据报,这种方式确实是降低了效率,但是为了可靠性,做出这点牺牲还是必要的~
那什么时候重传呢?接收方通过设置一个等待时间作为触发超时重传的时机,超过等待时间还没有接收到ack,就会触发超时重传,关于等待时间有两点需要注意:
- 等待时间是可以配置的,不同的系统上的等待时间是不同的,我们可以通过调整内核中的一些参数来调整等待时间
- 等待时间是动态变化的,每次的等待时间是会
变长
的,但是不会无限制的增长,增长到一定时间就会触发tcp的重置连接
接收缓冲区
对于上述丢包的第二种情况来说,我们实际上是发送了两条相同的数据,站在发送方的角度是为了确保可靠性,但是站在接收方的角度,我实际上是收到了两条相同的数据,注意:此时接收方已经成功接收了
假设是转账的情形,第一次A向B转了500,B成功接收了,B的账户 +500,但是B的ack丢失了,导致A认为我这500元没有传输过去,触发超时重传,重新给B转了500,B的账户此时就 +1000了!这很明显是一个很严重的bug!!!
但是也不用担心,tcp已经帮助我们解决这种bug,tcp设置了一个接收缓冲区
来接收数据,A发送给B的数据不会直接被B读取,而是先存储到接收缓冲区内部,对于上述转账的情况,在第一次成功转账到B时,B的接收缓冲区内部就会存储此次传输的数据和序号,由于第二次传输的是相同的数据,这个数据已经被存储到接收缓冲区内部了,接收缓冲区就会删除这个重复的数据,这样就不会产生转账两次这样的操作了
tcp的接收缓冲区不仅能够去重
,还可以重排序
,确保B的应用程序读取消息的顺序和A发送消息的顺序是一致的(根据序号的大小进行排序)
3.三次握手,四次挥手
1.三次握手
握手(handshake)是tcp中通信双方建立连接的方式,通过发送一个简单的,没有业务数据的数据报来建立连接,以便进行后续的通信操作,连接的建立就像打招呼,是引起双方注意的一种方式,但是没有实际的内容
syn(synchronized)同步报文段,syn就是通信双方进行打招呼的方式,syn内部不含有任何的业务数据,就是为了引起对方的注意而发送的一个数据报,B接收到syn之后会返回syn和ack数据报,A接收到B的ack+syn数据报之后也会返回一个ack给B,通过这三次握手,A和B之间就建立了连接,各自保存了双方的信息
同样的,也可以通过tcp报头中的标志位来判断是否是syn数据报
三次握手的意义:
- 投石问路,确保当前网络是通畅的(如果不通畅,就没有进行数据传输的必要了)
- 保证通信双方接收和发送的能力均正常
这经常会涉及到关于tcp的一个经典的面试题:两次握手能否成功建立连接呢?不能
tcp是全双工的,通信双方都可以接受发送消息,两次握手不能保证通信双方的接收,发送消息的能力均正常,两次握手只能确保A的发送接收和B的接收能力正常,不能确保B的发送能力正常,只有当B收到A的ack之后,才能确保自己的发送能力正常 - 通信双方就一些重要的参数提前进行协商(比如确认序号)
通信双方在传输数据的时候会提前协商好一个比较大的,和上次传输不一样的序号,这样做的目的是为了防止出现"前朝的剑,斩本朝的官",在一次传输过程中,如果突然出现网络异常,传输的数据没有成功传输,而是堵在路上了,进行重连之后,上次传输的数据姗姗来迟,我们应该删除这个前朝的数据,可以通过序号来区别前朝的数据和当朝的数据,如果序号和当朝的序号差异大,就可以认定为这是前朝的数据,就可以进行删除!
一个更加详细的三次握手的示意图:
2.四次挥手
三次握手用于通信双方建立连接,四次挥手用于通信双方断开连接
以下是一个简单的四次挥手的过程
- 第一次挥手(FIN):发送关闭连接的一方向对端发送一个带有FIN标志位的TCP报文段,表示我不再发送数据,但是仍然可以接收数据
- 第二次挥手(ack):对端接收到关闭的请求之后会发送一个应答报文(ack)表示我已经接收到了断开连接的请求
- 第三次挥手(FIN):对端向发送方也发送一个带有FIN标志位的TCP报文段,表示我这一方也不再发送数据
- 第四次挥手(ack):发送方收到对端的应答报文和结束报文段,向对端发送一个ack报文段
上述四次挥手完成之后,连接不会立即断开,而是处于TIME_WAIT状态,等待2倍的最大报文段生存时间(2MSL)后,连接彻底关闭,释放资源
FIN(finish)报文段和syn报文段相同,也是一个不含有业务数据的报文段,可以通过tcp报头的标志位来判断是否是FIN报文段
此处为什么就不能像三次握手那样,将中间两次的报文段合二为一呢?实际上,究竟是通过几次挥手断开连接是不确定的,之所以是不确定的,原因在于发送FIN报文段和ack报文段的时机是不同的(三次握手的ack和syn都是内核进行触发的)
ack报文段的发送是通过系统内核进行发送的,而FIN报文段的发送依赖于应用程序的关闭,即调用socket.close()方法之后才会发送FIN报文段,如果在close之前还有许多要执行的代码,ack报文段就会先于FIN报文段进行发送(后面讲到的延时应答机制通过延迟ack的发送时间,可以让ack和FIN同时发送)
这里有一个问题,如果我的代码中忘记写close了或者没执行到close方法,是否意味着第二次的FIN报文段就不会成功发送?这是会的,四次挥手断开连接是一种正常的连接断开的方式,但是四次挥手没有完成也可以完成连接的断开,这属于异常断开(tcp会在没有收到确认报文之后触发重传,当达到最大的重传次数之后,会关闭连接)
四次挥手的详细图解(注意tcp的状态)
TIME_WAIT状态存在的意义是为了防止最后一个ack报文段丢失,留下的"后手"
如果A最后发送的ack数据报丢失,站在B的角度,B因为没有收到ack数据报,就会触发超时重传,重新发送一个FIN数据报给A,如果A不处于TIME_WAIT状态,而是直接关闭,那么B发送的FIN数据报就没有意义了.
让A处于TIME_WAIT状态,就是让A有时间去等待B重传的FIN数据报,接收到之后重新发送一个ack数据报给B,等待的时间是 2MSL(MSL是两个通信节点之间传输的最大时间,这个参数是可以进行配置的)
4.滑动窗口机制
滑动窗口机制是提高TCP传输效率的一种机制
TCP的可靠传输是会降低数据的传输效率的(多出了等待ack的时间),通过滑动窗口机制缩短了等待ack的时间,来提高数据传输的效率,具体提升的方式见下图:
传统的tcp传输数据的方式:
利用滑动窗口机制进行的传输:
传统的tcp进行数据的传输是一次只传输一条数据,等到该条数据的ack返回之后才进行下一条数据的传输.而滑动窗口机制通过批量传输数据的方式,缩短了等待ack的时间,提高了效率,单次可以发送最大的数据量被称`窗口大小
在滑动窗口机制下,确认应答机制是可以正常工作的,但是如果出现丢包
就要出发超时重传,这里的重传机制和上述的重传还有所不同
1.ack数据包丢失
B返回的确认序号为1001的ack数据报丢失,这种情况下不需要进行重传.原因在于确认序号为2001的ack数据报成功传输给A了,确认序号的意义就是当前序号之前的所有数据都已经成功接收,A收到2001的ack数据报,就知道序号为1-2000的所有数据都成功传输了
2.数据报丢失
这里的重传和之前的重传是不一样的,之前重传可能会出现传输重复数据的情况,但是滑动窗口机制下的重传并不会出现这种情况,效率相较于普通的重传更高,被称为快速重传
滑动窗口其实是一种使tcp根据通信数据量进行动态调整的一种机制
-
如果通信双方通信数据量大,通信频繁,就会采用滑动窗口机制和快速重传
-
如果通信双方通信的数据量小,通信不频繁,就采用普通的tcp传输和普通的超时重传
5.流量控制
前提:滑动的窗口越大,等待ack的时间就越少,传输的效率就越高,但是窗口越大传输效率就越高么?显然不是的,我们还要考虑接收方处理数据的能力,窗口越大,批量传输的数据就越多,接收方需要处理的数据就越多,如果超过了接收方可以处理数据的上限,就有可能出现丢包
的情况,此时发送方就需要进行重传,导致效率下降
这种情况的出现违背了tcp的初心–>可靠传输,提高效率的前提应该是在可靠性的基础之上进行效率的提升,不能一昧的提高效率,忽视可靠性
流量控制就是站在接收方的角度,来约束发送方的窗口大小的一种机制,发送方发送数据的速率不应该超过接收方处理数据的能力
数据由发送方传输到接收方时,不会直接被接收方的应用程序读取,而是先存储到接收方的接收缓冲区内部,这个接收缓冲区是内存的一个区域(tcp的socket创建),接收方读取数据时直接从接收缓冲区内部读取数据
接收缓冲区存在的意义就像是生产者消费者模型中的阻塞队列,降低了连接双方的耦合性
接收方处理数据的效率是通过接收缓冲区的剩余空间大小进行衡量的,剩余空间大小越大,处理数据的效率就越高,反之就越低
接收方在返回ack数据报是会返回当前接收缓冲区剩余空间的大小,发送方收到后,就会动态的调整窗口大小,来保证接收方有足够能力来处理数据
以下是流量控制机制示意图:
说明:
- 接收方每次传输ack时会传输当前接收缓冲区内部剩余空间的大小,发送方根据这个大小来动态的调整窗口大小,适应接收方处理数据的速率
- 当接收方返回的大小是0时,此时接收方的接收缓冲区已经爆满不能再处理额外的数据了,发送方收到后就停止传输数据,开始等待接收缓冲区有额外的空间
- 发送方通过发送窗口探测包的方式来判断接收缓冲区是否有空余空间,这个数据报不含有任何的业务数据,当接收缓冲区的大小不再为0时,发送方就继续传输数据
tcp的报头中的16位窗口大小就是接收缓冲区的大小
6.拥塞控制
流量控制考虑的是接收方处理数据的能力,关注的是通信终点处理数据的能力,但数据的传输还需要通过中间的网络节点进行传输,如果发送的数据超过了中间节点可以接收的数据上限,也会发生丢包,发送方还是需要重传
也就是说,我们还需要考虑中间节点处理数据的能力,接收方处理数据的能力可以通过接收缓冲区的大小来进行判断,但是中间节点处理数据的能力不容易判断,此时可以通过实验
的方式来动态的调整发送数据的速率(窗口大小)
具体的实验方式很简单,让发送方先以一个比较小的速率发送数据(小窗口),如果在这个过程中发送很顺利,没有出现丢包的情况,就逐渐增加发送速率(增大窗口),随着窗口的不断增大,就有可能出现丢包
,一旦出现丢包就降低发送速率,重新以一个比较小的窗口发送数据
慢启动曲线很好的演示了实验的过程
说明:
- ssthresh是慢启动阈值(Slow Start Threshold)的缩写,它是一个拥塞窗口的上限,是指数增长的终点,一旦超过ssthresh,就进入拥塞避免(Congestion Avoidance)模式(线性增长阶段)
- ssthresh会动态调整,具体来说会调整为上一次达到网络阻塞时的窗口大小的一半,这样做的目的是让发送方可以更高效率传输数据,延缓达到网络阻塞窗口大小的时间(增大传输轮次)
实际上,后来tcp对慢启动算法进行了一定的优化,具体优化体现在:在上一次达到网络阻塞时,下一次不会直接从特别小的窗口进行数据传输,而是直接从新的ssthresh值开始进行传输(省略了指数增长的情况),曲线如下图
流量控制和拥塞控制是对滑动窗口机制的一种限制,约束,使其不忘初心–可靠传输,最终的窗口大小是Math.min(流量控制的窗口大小,拥塞控制的窗口大小)
7.延时应答
进一步优化传输效率的方式,通过延时应答的方式,增大窗口大小,使发送方一次发送的数据更多(延缓ack的等待时间,让应用程序多处理一些数据,使接收缓冲区的内部剩余空间大小更大一些)
举个例子,如果接收方收到数据之后立即返回ack, 剩余空间的大小是10 如果延时应答 2ms 在这2ms内接收方可以从接收缓冲区内读取一定的数据 此时剩余空间的大小就变为 12了 接收缓冲区的剩余空间增大 窗口增大 传输效率增加
在上述四次挥手部分,我们说到断开连接不一定必须经过四次挥手,也有可能是三次.如果通过延时应答的方式延迟ack的发送时机,使其和FIN数据报一起发送,就构成了三次挥手断开连接
8.捎带应答
捎带应答是在延时应答的基础之上对传输效率的进一步优化
通信时,双方传输的都是含有业务逻辑的数据,通常都是"一问一答"这样的方式(全双工)
将业务数据报和确认信息(ack)合二为一,减少tcp封装分用的次数,进一步提高效率
9.粘包问题
粘包问题其实不是tcp特有一种问题,而是面向字节流传输数据的机制都存在的一种问题,tcp在传输的过程中是通过字节流进行传输的(两个字节流对象),传输的数据通过字节流发送,接收时也是接收一个一个的字节,但是在接收的时候你无法明确哪里到哪里是一个完整的要传输的信息,一次读取的字节个数也是不一定的,这就会造成读取错误的出现
相比之下,UDP这种面向数据报传输的协议就不会出现粘包问题:
如何解决粘包问题呢?核心在于:明确好应用层协议,确定好应用层数据报的边界
常见的做法有:
- 设置分隔符; 如设置 \n 为一个完整的应用层数据报的边界,接收方在读取数据的时候就会以 \n 作为每次读取数据的结束标志
- 引入长度; 发送数据的时候对每一个要发送的数据附加上数据的长度,接收方在读取数据时就会先读取到长度,根据这个长度来确定每次读取数据的范围
一些常见的应用层协议格式的数据报都是自带"结束标志的",如xml有开始,结束标签,json格式是通过{}作为传输数据的边界,protobuffer是通过二进制长度来确定边界的
10.异常处理
异常处理指的是,在通信时如果发生意外,tcp的解决方案
1.进程崩溃
进程崩溃其实就是进程终止,进程占有的文件描述符表被释放,相当于调用了socket.close()方法,接着就会触发FIN数据报,完成一次正常的四次挥手的断开连接的过程
tcp的连接可独立于进程存在,进程没了,tcp连接仍然可以存在(进程没了,通信双方可以继续接收双方发送的数据报)
2.主机关闭(正常关机)
主机关闭时,会先触发FIN,但是主机关闭意味着整个系统的关闭,虽然发送方可以发送FIN数据报,但是对端的FIN和ack有可能因为系统的关闭导致无法正常传输到发送端,站在对端的角度,就是数据报发送失败,要执行超时重传,重传几次之后发现都没有成功发送,就会自动关闭连接了
3.主机断电(非正常关机)
主机的突然断电是一瞬间的事情,对于通信双方来说来不及关闭进程,来不及触发FIN数据报,站在对端的角度主机断电可以分为两种情况
- 对端正在发送数据,此时突然断电,对端就无法收到ack数据报了,触发超时重传,重传多次之后还是没有收到,此时就会触发tcp的重置连接,对端会发送一个复位报文段给主机,如果还是没有收到确认信息就会断开连接
- 对端正在接收数据,此时突然断电,对端一直没有收到数据,站在对端的角度,他也不知道主机是消息发送完毕了还是和主机之间断开连接了,为了避免这种情况的出现,作为接收方的对端会周期性的发送一个不含有业务数据的数据报,如果发送之后主机并没有返回确认信息,就认为连接断开,此时相当于对端单方面的断开连接,这种机制也被称为心跳包机制,发送的不含有业务数据的数据报就是一个心跳包,这个包是接收方周期性进行发送的,如果没有收到确认信息,就认为是没有心跳了,就要断开连接了~
心跳包机制在以后的工作中其实很常见,比如可以通过心跳包机制在分布式系统中判断主机是否正常工作
4.网线断开
以上就是TCP中比较重要的十大机制(TCP不仅仅只有这十个机制,只是这十个比较重要)
这就是网络原理(一)的所有内容,主要讲述了网络模型中的应用层协议和传输层协议,重点是对传输层协议中的TCP的每个机制的理解!