活动发起人@小虚竹 想对你说:
这是一个以写作博客为目的的创作活动,旨在鼓励大学生博主们挖掘自己的创作潜能,展现自己的写作才华。如果你是一位热爱写作的、想要展现自己创作才华的小伙伴,那么,快来参加吧!我们一起发掘写作的魅力,书写出属于我们的故事。我们诚挚邀请你参加为期14天的创作挑战赛!
提醒:在发布作品前,请将不需要的内容删除。
各位看官,大家早安午安晚安呀~~~
如果您觉得这篇文章对您有帮助的话
欢迎您一键三连,小编尽全力做到更好
欢迎您分享给更多人哦
今天我们来学习【Java网络编程】-【TCP】三次挥手,四次挥手详解--UDP和TCP协议详解
目录
2.5.2.通信直接断开了(进程都没来得及杀)(想回应但是回应不了)
图解:(TIME_WAIT )的作用以及四次挥手的图解
大哥和小弟在心里(兄弟在心中)

首先我们要明白学习一个协议最后重要的就是了解这个协议的报文格式
协议本身就是一个简单的规则
所谓的报文格式就是按照一定的格式进行字符串拼接(当然这里是二进制拼接)
1.UDP协议
UDP的报文格式比TCP还是要简单不少的,因此我们要讲解的点不多,主要是以下两点:
1.UDP报文长度的一些问题
2.UDP校验和

1.1.UDP报文长度
问题一:
大家可以看到UDP报文长度一共就64kb,这对于以前的计算机还不错,但是如今看来就很捉襟见肘了,所以能不能把UDP进行升级处理(报文长度变长)?
当然可以!
但是代价是巨大的,如果要升级,就要通信双方都要去升级(全世界都要去升级,而且UDP是操作系统内核实现的,操作系统也要进行升级),所以说成本太大的我们还不考虑升级
除非未来出现一个新的协议把UDP完全替代掉。
问题二:
还有的人说:既然UDP不能一次性传输,能不能把数据进行拆包和组包?
答:
这个方案不太行(开发成本太大),我们如何进行拆包,万一数据丢失了,我们再进行如何校验,又如何组包?这里面有很大的开发和测试成本!!!
但是TCP就可以解决这个问题(不过带来的是性能的下降)
1.2.校验和
1.2.1什么是校验和,校验和有什么用?
网络通信中,往往都会因为一些信号干扰的因素,导致我们传输的数据出现问题,这个就需要校验和帮助我们检查一下数据的正确完整性~
首先我给大家的例子:
我给小明说一个我的游戏账户密码(很长),我就念给他听了,他说记到纸上了
然后他疏忽了少写了一个字母,这个时候就很尴尬(他回家了登录不上游戏了)
加一个校验和~
但是如果我跟他说一个20个字母,他就可以检查到他只写了19个字母,当时就知道错误了
这个校验和就可以起到检查数据是否正确的作用~~
具体解释:

1.2.2.校验和是怎么计算出来的?
计算校验和有很多算法,这里UDP使用的算法是CRC算法(循环冗余算法)
解释:
把当前要计算校验和的数据的每一个字节都进行累加,把这个结果保存到校验和中(校验和一共两个字节),就算溢出也没关系。
但是这个CRC算法不是很靠谱,导致两个不同的数据得到一样的CRC校验和的概率不小
就譬如前一个字节少1,后一个字节多1(有不小的概率出现)
1.2.3:MD5
要计算校验和MD5就是一个很好的算法(和他类似的还有sha1算法)
我们讲解一下MD5的特性
- 定长:无论你原始数据多长,计算出来的md5都是固定长度(这一点就很好,毕竟校验和本身就不应该很长)
- 分散:两个原始数据很接近的数据,只要一个字节不同,md5计算出来的值也会差距很大 (因此MD5也很适合做hash算法,把一个几乎相同的key尽量做到分散,降低hash冲突)
- 不可逆:给一个原始数据计算出MD5 很容易,但是反过来极其困难(理论上是不可行的)
1.3.基于UDP实现应用层协议
NFS:网络文件系统
TFTP:简单文件传输协议
DHCP:动态主机配置协议
BOOTP:启动协议(用于无盘设备启动)
DNS:域名解析协议(后面我们会讲解这个)
当然,也包括你自己写UDP程序时自定义的应用层协议
2.TCP协议
TCP相关特性
1.有连接 Socket clientScoket = serverSocket.accept();
用过ServerSocket api的accept方法把内核里已经建立好的连接拿过来**(通过三次握手建立的连接)**
2.可靠传输(我们接下俩要讲述的重点)
3.面向字节流(我们通过字节流 读/写 网卡抽象成文件里面的数据)
4.全双工(可以同时互相发送消息)
2.1.TCP报文格式
图解:(没讲到的后续会进行讲解)
2.2.TCP可靠传输(TCP最开始的核心)
解释:(得知道对方收没收到消息,如果对方一直没回应,就进行补救)
因此为了保证可靠传输我们引入如下3个机制
1.确认应答
2.超时重传
3.连接管理(三次握手,四次挥手)
2.2.1.确认应答
确认应答:顾名思义,接收方会发送方返回一个应答报文(acknowledge ,ack)
如果发送方收到这个应答报文了,就知道自己的数据已经发送成功了。
但是,网络上不稳定的情况可能会导致我们的数据出现"后发先至"的情况
所以TCP将每个字节的数据都进行了编号------即为序列号
解释:(后面我们讲述到滑动窗口的时候我们还会讲解到)
有了序号,我们只需要确认回复给发送方最后一个数据(发送方就可以确定其他数据都来了),因为如果数据没全来的话,我们是不会给发送方发送1001的
如何区分一个数据包是一个普通的数据还是ack应答报文数据呢?
TCP的可靠传输:
确认应答 是TCP最核心的机制,再借助其他机制辅助最终完成可靠传输
2.2.2.超时重传(四个问题)
我们是可以进行通过确认应答来保证我们的数据传输到了
**第一个问题:**但是如果我们的数据包都丢了,怎么应答呢?
(网络上也会出现"堵车"的问题:如果数据包太多,就会在路由器/交换机上出现堵车,但是路由器针对"堵车"的处理会比较除暴,它不会把积压的数据保存好,而是把这些中的大部分数据都丢调)
答:
其实一直不应答也是一种应答(给女神发消息女神一直不回也是一种应答)哈哈哈哈
这个时候我们就需要进行重新传输了------超时重传
我们肯定是等待了一会发现接收方还一直不给我们返回ack(或者已经返回了ack我们没有收到)这两种情况都是认为对方没有收到,我们就要超时重传的功能了!!!
再来一个问题:
如果是对方已经收到这个数据还返回了ack,只是我们没有收到,这个时候发送的数据不就会重复了吗?
答:
TCP里面会有一个**"接收缓冲区"**就是一个内存空间,保存当前已经接收到的数据,以及数据的序号。
如果接收方发现,当前发送的数据是再接收缓冲区存在的数据,就会把这个数据丢弃掉,确保我们应用程序进行read的时候接受到的只是一条数据
图解
第三个问题:发送方何时进行重传?等待时间?
1.这个等待时间是可以进行配置的,不同的系统上面不同,也可以修改一些内核参数来引起这里的时间变化
2.等待的时间也会动态变化,没多经历一次超时,等待的时间都会变长(但是也不会无限拉长,如果时间长到一定程度,就会放弃TCP连接(触发TCP重新连接操作))
第四个问题:那这样TCP的可靠性不就会导致性能的下降了吗?
当然------等价交换是炼金术不变的原则
传输效率下降,复杂程度变高
传输层的协议主要是TCP和UDP但是还是有别的协议可以做到TCP和UDP之间的效果的
譬如(quic......)
2.2.3.连接管理(建立连接+断开连接)
三次握手和四次挥手终于要来啦~~~
握手其实就是打个招呼
TCP的三次握手:TCP在建立连接的过程中,需要通信双方一共"打三次招呼"才能够建立
A先给B发送一个**"同步报文段"** (没有数据的应用层数据包,没有载荷)(标志位为SYN为1的时候这个这个报文就是一个同步报文段)
然后B给A回复一个ack(这个时候只有A能确定它发送过去的数据能送到,但是B不知道B发送的数据报是什么情况,所以B也要给A发送一个"同步报文段"如果A也能给B的发送一个同步报文段,那么A就知道自己没问题了)
所以情况如下
这个时候A和B就记录了对方的信息,就构成了逻辑上的"连接"
建立连接的过程就是通信双方都给对方发起syn,也都要给对方反馈ack(本来是四次握手,但是中间两次恰好可以合并成一次)
三次握手的作用(就是建立连接的作用)?
1.建立连接肯定是确保网络通信是否畅通
2.让发送方和接受方确认自己发送能力和接收能力是否正常
(如果B不给A发送syn,A就不会回应。B也不知道自己的ackA收到没有,如果A回应了ack并且B收到了,B就能确定自己的发送能力和接受能力都是正常的)
3.让通信双方对于一些重要的参数进行协商
譬如:
(1)超时重传时间
(2)序列号( 序列号确保数据传输的顺序。在 TCP 通信中,发送方将数据分成多个数据包,并为每个数据包分配一个唯一的序列号(具体从那个数字开始也是需要协商的不是拍脑瓜子))
(3)窗口大小(我们等会滑动窗口会讲解)
............
给大家一个具体的例子(这就协商出来按照65w进行充电)
2.2.4.四次挥手
接下来我带大家看一下一张完整的TCP状态转换图
建立连接是客户端主动发起的
但是断开连接客户端和服务器都可以主动发起
FIN:结束报文段(就像建立连接的SYN(同步报文段)都是一个标志位)
问题一:问什么是四次挥手而不是三次挥手?(三次挥手在延时应答的情况下也可以实现)
问题二:小编一开始学的时候就想着(你都要结束了还搞这么正式干嘛?)
直接两次挥手不就得了?
但是我少考虑了很多
解释,很重要!
服务器还要处理数据,可能还要给客户端响应其他的数据(客户端这个时候还不能关闭)
等到服务器发送FIN的时候才能证明数据已经处理完毕了(这个时候就是三次握手了) 。但是服务器还不能关闭,万一客户端没收到FIN(客户端没收到FIN时不能关闭因为服务器可能还有数据要通信),服务器还要重传 。所以服务器还要等待客户端返回ack才能结束**。(这里就是四次握手了)**
图解:(TIME_WAIT )的作用以及四次挥手的图解
TIME_WAIT作用:防止最后一个ack走丢(小弟担心大哥又多等待了一会)
我们上述的机制都是在为可靠传输效力,但是可靠传输下来,牺牲的却是效率
那我们怎么来提高效率呢?接下来我带领大家学习以下几个机制~
2.3.提高效率
接下来给大家看一下我的总结思路:
2.3.1.滑动窗口
变成这样~~~
然后进行**"滑动"窗口(只要有ack回来就继续发)**
但是出现了丢包的情况该如何呢?
分为两种情况~
第一种情况:
就像别人问你上初中了没有,你直接说我已经大学了(那我肯定早就上过初中了呀)
第二种情况
等到1001~2000传输到了之后,7001这个确认序号说不定就该传输过去了(3001,4001,就不用传输回去了哈哈哈)
接下来我们要说的是窗口大小的问题(窗口不能无限的大)
2.3.2.流量控制
是窗口大小的问题(窗口不能无限的大),窗口太大了,服务器处理数据就处理不过来了
这个时候"负反馈"就很重要了调整窗口的大小
发送方的发送速率不能超过接收方的处理能力
接收方每次收到数据,都会通过ack(应答报文)返回给发送方,然后发送方就会按照这个数值调整窗口的大小(不能超过)
为啥要探测呀:
刚刚不让你传输了,现在让你传输了(怕我更新可窗口通知了你,但是通知你的数据报丢了,所以一会不通知你,你就要试探一下(发个窗口探测包),问问好了没)
但是传输路径上的节点的接收能力也要计算在内呀(不能量化,我们就一点一点的增大窗口,然后综合窗口大小,选小的)。
2.3.3.拥塞控制
但是传输路径上的节点的接收能力很难量化,所以我们就一点一点的增大窗口,然后综合窗口大小,选小的)。(实验的方式)
阈值(sstresh):指数增长的终点,线性增长的起点(大加速度变成小加速度)
首先:
客户端先按照比较低的速度发送数据(小一点窗口)
如果数据传输的很顺利(几乎没有丢包的情况) --> 把窗口尝试调大,大到一定的程度,中间节点可能就会出现了丢包问题。这个时候发送方发现丢包了 --> 就把窗口调小一点 。如果还是丢包就继续调小,不丢就增大,--> 最终达到动态平衡的效果
具体过程如图
改进措施如下:
总之:
流量控制:通过接收方返回的剩余缓冲区大小来确定窗口大小(主要考虑接收方处理能力)(定量)
拥塞控制:通过实验的方式一步一步动态稳定窗口的大小(主要考虑中间节点的处理能力)
(非定量)
最终窗口大小取二者中最小值
2.3.4.延时应答
而且我们通过延时应答也可以把四次挥手变成三次挥手
2.3.5.捎带应答(就是把本来两个数据段一起返回了)
就像四次挥手变成三次一样
刚好把response和ack一起返回
进一步提高了效率
本来要传输两个tcp数据包,我们要封装分用两次
但是用过上述的操作,就可以把两个数据包合并在一起了,现在我们封装分用一次就行了
2.4.粘包问题
很多面向字节流传输数据的时候都会出现这样的情况
但是UDP就没有这样的问题(UDP本来就是一个一个的数据包(有边界的))
所以我们解决粘包问题的关键就是怎么搞一个边界出来
这就让我想起来"循环队列"
当head 和 tail 重合的时候到底是这个数组满了还是数组是空的?
两个方案
1.搞一个标志位(浪费一个格子,当head = tail + 1的时候就是满的,head = tail就是空的)
2.引入有效长度(当length = array.length就是慢的 ,length = 0就是空的)
这个时候也是
1.搞一个标志位(引入分割符)
2.引入长度(譬如先读两个两个直接的数字表示这个数据包中的字节有多长)
2.5.异常的处理情况
一般分为两种情况
1.进程异常结束
2.进程都没来得及杀,通信就断了
2.5.1.进程异常结束
首先我要声明
TCP 连接的状态(如
ESTABLISHED(已经建立好连接)TIME_WAIT(延时等待)
等)由内核协议栈维护,而非应用程序。在 TCP 协议中,四次挥手是由操作系统内核实现的(severSocket.accept()只是拿到内核建立好的连接) ,而非依赖于应用程序显式调用 socket.close()。当进程异常终止时 (如崩溃或被强制杀死),操作系统会主动回收该进程占用的资源(包括文件描述符),并触发内核协议栈的 TCP 连接关闭流程。
在Java中,当创建一个Socket对象时,**底层操作系统会分配一个文件描述符来管理这个连接。**进程异常终止时,操作系统通常会负责清理进程占用的资源,包括关闭打开的文件描述符。
进程异常结束的两种情况:进程崩溃和主机关机了(用户自己关机的)
2.5.2.通信直接断开了(进程都没来得及杀)(想回应但是回应不了)
1.主机掉电(就像插头松了一样)(又分为客户端掉电或者服务器掉电)
(1)客户端掉电:本来还在正常的通信,结果客户端直接掉电了,服务器都懵了(客户端怎么还不返回ack?)然后就开始超时重传,结果还是一直不回(对方挂了?)。
再然后发送"心跳包",结果还是不回,然后就认为客户端挂了这个时候就可以单方面释放连接了。
图解:
注解:
服务器不是这个时候才会发送"心跳包",而是周期的就会发送心跳包(不带业务的数据包),期望回复,多次没回复就认为对方挂了
(2)服务器掉电:
还是一样的,客户端一直发数据,服务器不回应;超时重传还是不回应。客户端会发送一个**"复位报文段"(RST)(reset),如果FST发过去还没有回复,就会单方面释放连接**
ACK:应答报文
RST:复位报文段
SYN:同步报文段(建立连接时发送),建立好连接进入(ESTABLISH状态)
FIN:结束报文段(主动发起FIN的进入TIME_WAIT状态)
2.网线断开(和掉电十分相似,不过这俩一个发复位报文段,一个发心跳包,但是就是连接不上哈哈)。
TCP的心跳机制:再分布式系统中十分常见。一般检测一个机器是否挂了都是通过"心跳机制"来检测(不过后续使用的心跳机制都是在应用程序中自己实现的(一般都是毫秒级别了),而不是TCP的心跳(时间太长))
3.UDP和TCP之间的对比
第一点对比:
TCP:可靠传输,有重组机制(能传输很大的数据)。
在接收方,TCP协议会根据每个TCP段的序列号来重组这些数据。100kb的数据可以分成好几个段,接收方的TCP协议会按照序列号顺序将这些TCP段重新组装成完整的100 KB数据。(适合绝大部分的场景)
UDP:效率更高,无重组机制(只能传输很小的数据)。
UDP协议本身不提供数据分段和重组的功能。每个UDP数据报(datagram)(更适合于"可靠性不是很敏感,性能敏感,数据不多"的场景:譬如同一个局域网主机之间的通信
而且在以太网中UDP和TCP数据报最大不会超过1500字节(受MTU(最大传输单元)的影响)(后面我们会讲解到)
第二:
UDP天然适合"广播传输"(给这个局域网内所有的设备都发送信息),而TCP不支持(TCP只能遍历这个局域网设备里面的所有IP实在是太麻烦)
这是因为UDP的socket能给"广播地址"建立联系(譬如192.168.255),但是TCP无法和这个地址建立联系
第三:若UDP想要实现TCP的可靠传输,可以参考TCP的思路
确认应答(引入序号和确认序号,这样也可以组包啦)
超时重传(包丢了)
提高效率再滑动窗口等等我们TCP可靠传输的特性............
结束啦~~~
最后一个解答
大家可能会疑惑,像TCP和UDP这么NB的协议都是谁整出来的?
当然是一群神秘的大佬,把这些协议都整理出一个标准的文档---RFC标准文档
上述就是【TCP】三次挥手,四次挥手详解--UDP和TCP协议详解的全部内容啦~~~能看到这里相信您一定对小编的文章有了一定的认可。
有什么问题欢迎各位大佬指出
欢迎各位大佬评论区留言修正~~
****
**********您的支持就是我最大的动力!!!**