javaEE-网络原理-5.进阶 传输层UDP.TCP
上篇的网络编程,写的代码,实现回显服务器,都是在应用层 ,为了完成某项业务。现在谈谈传输层中的UDP,TCP协议。
目录
传输层:负责数据能够从发送端传输接收端.
一.端口号
二.UDP协议
UDP协议段格式:
源端口,目的端口
UDP长度:
校验和:
CRC校验过程
md5算法特点:
三、TCP协议
1.TCP协议段格式:
源端口,目的端口:
首部长度:
保留位:
6位标志位:
16位校验和:
32位序号,32位确认序号:
2.“确认应答”
“后发先至”现象:
3.“超时重传”
为啥会出现丢包现象:
接收方又如何判定接收到数据为“重复数据”:
4.连接管理:建立连接:“三次握手”
建立连接流程:
“三次握手"
三次握手流程图:
进行三次握手的意义:
5.连接管理:断开连接:“四次挥手”
四次挥手流程图:
三次握手和四次挥手的相似之处和不同:
6.连接管理:TCP的状态转换
客户端与服务端进行三次握手时的状态转换:
7.滑动窗口
当出现丢包时:
编辑确认应答机制,滑动窗口机制的转化:
可靠性的判定:
8.流量控制
TCP报头中的16个窗口大小报文段:
“窗口探测包”
9.拥塞控制
拥塞窗口的大小又是怎样确定的呢?
拥塞控制 有几个步骤:
拥塞控制的流程图:
10、延时应答
11.捎带应答
12.面向字节流
处理粘包问题,有两种方法:
13.异常情况
1.其中有一方出现进程崩溃。
2.一方出现关机(正常流程关机)
3.一方出现异常关机(断电/直接拔电源)
4.网络断开
传输层:负责数据能够从发送端传输接收端.
一.端口号
用于标识一个主机上进行通信的不同的应用程序。
端口号范围:0-65535;
范围划分:
0-1024:用于“知名端口号”,HTTP,FTP,SSH等这些⼴为使⽤的应⽤层协议,他们的端⼝号都是固定的。
1024-65535:为操作系统动态分配的端口号;客户端程序的端口号,就是应用程序从这个范围分配的。
二.UDP协议
特点:无连接,不可靠传输,面向数据报,全双工
UDP协议段格式:
UDP报头共有4个字段,每个字段2字节,共占8字节,
源端口,目的端口
就是上篇博客中的服务端口号/客户端口号。
UDP长度:
一个端口号的范围最大为65536,也就是64KB.
UDP协议⾸部中有⼀个16位的最⼤⻓度.也就是说⼀个UDP能传输的数据最⼤⻓度是 64K(包含UDP⾸部). 然⽽64K在当今的互联⽹环境下,是⼀个⾮常⼩的数字.如果我们需要传输的数据超过64K,就需要在应⽤层⼿动的分包,多次发送,并在接收端⼿动拼装;这个过程很容易出现问题,TCP的出现就能解决这个问题。
校验和:
验证数据在传输过程中是否正确传送。
网络数据传输。本质上是光信号/电信号/电磁波,这些在传输的过程中,都很可能收到影响,出现0->1,1->0,这种现象称为“比特翻转”。
校验和的作用就是时别当前数据是否出现比特翻转,是否是正确的数据。
检验和是拿着原始数据的一部分信息进行计算,有可能出现内容错误,但校验和结果还是原来正确的结果(概率较小,实践中可忽略)
CRC校验过程
校验和是使用CRC(循环冗余)算法完成检验:通过遍历取出数据报中的每个字节的数据,进行相加.得到校验和,不管是否产生数据溢出。
校验过程:UDP数据报在发送数据报之前,先进行CRC计算,得到value1值,放到数据报中,然后通过网络传输,将数据报发送;接收方收到数据包后,再对数据进行CRC计算,得到value2,将value1与value2进行比较,若结果不同,说明数据报在传输过程中发生了比特饭翻转,结果相同,则传输无误。
除了CRC算法校验,还有更精确的校验算法:md5校验,
md5算法特点:
1.定长:无论原始数据多长,算出来的md5数据都是固定长度的。
常见的me5的长度有16位版本(2字节)/32位版本(4字节)/64位版本(8字节)。
2,分散:计算md5时,只要原始数据发生了一点点变化,计算出来的md5值的变化都是非常大的。(若数据传输过程中发生了比特翻转,一定能发现)
3、不可逆的:计算出的md5值,是无法通过计算得到原始数据的。
md5常应用于一些加密场所,网站中保存用户的密码,一般存放的都不是明文密码,都是通过md5加密过的,就算通过非法手段获取到了密码,也无法破解。
三、TCP协议
特点:有连接,可靠传输,面向字节流,全双工
1.TCP协议段格式:
源端口,目的端口:
这个和UDP一样,是服务端或客户端的端口号.
首部长度:
TCP报头的前20字节为首部长度,其中包含了选项,这个选项需要时有,不需要时可以不存在。
保留位:
6位标志位:
这是TCP非常核心的部分。
16位校验和:
类似于UDP校验和,将报头和载荷放在一起进行计算。
32位序号,32位确认序号:
这两个是TCP中非常重要的,用来确保TCP的可靠传输。
用来确保可靠连接,最核心的机制就是“确认应答”和“超时重传”。
2.“确认应答”
设发送方为A,接收方为B
“确认应答”:发送方A发送了个消息,接收方B会返回一个消息,表示接收方B收到发送方A发送的消息了。
但在实际过程中,很可能会出现“后发先至”的现象。
“后发先至”现象:
为啥会出现后发先至的现象呢?
这是,就很有可能产生逻辑错误。
不仅接收方会出现这样的问题,发送方也可能会出现先发后至的问题。
为了解决后发先至的问题,就引入了序号和确认序号:对数据进行编号,应答报文就告诉发送方,这次应答的是哪个数据。
TCP将每个字节的数据都进行了编号,
应答报文中的确认序号是按照发送报文的最后一个字节+1来进行设定的。
主机A发送数据1-1000,主机B收到数据后,就发送收到数据的最后一个数据的下一个数据1001给A,此时A就知道发送的数据已被B正确接收,就能发送下一条数据了。
1001就是应答报文的确认序号。
应答报文(1001)表示的含义:
1.<1001的数据都已经接收到;
2.发送方要发送的下一条数据要从1001开始发送。
应答报文确认正确收到数据了,报头的一个标志位:ACK 也会从0 -> 1.
3.“超时重传”
当A发送的消息,B没有收到;或者B的确认消息,没有正确发送到A,此时A都认为消息没有发送成功,这属于“丢包”现象。TCP的可靠性最关键的就是解决丢包问题。
这个时候就需要“超时重传”了:
发送方 发送消息后,会等待接收方的确认消息,由于发送消息是需要时间的;因此,等待时会有一个等待时间的阈值(上线),超过阈值(超时)发送方 还未收到确认消息(ACK),就会认为是“丢包”,此时,发送方 会再次发送该消息(重传)。
为啥会出现丢包现象:
在网络中,路由器/交换机不仅仅是处理一条数据,要支持大量的主机之间的通信,可能存在在某个时刻,某个路由器/交换机突然负载量很高,短时间内大量的数据包要经过这个设备转发;但是一台设备能够处理的数据量是有上线的,当某瞬间高负载,设备接收的数据量超过能转发的上限时,多出来的数据就无法被处理,就出现了“丢包”。
丢包现象有两种情况:
A发送的数据,没有被B收到:此时A 要重新发送。
B收到了数据,但B的确认应答消息没有发送成功:此时A还是认为B没有收到数据,会重新发送数据。
第二种情况下,B就会收到两份相同的数据,又要怎么处理?
TCP Socket会有一个接收缓冲区(一块内存),发送方发送的消息会先放到缓冲区中,应用程序通过调用read/Scanner.next从缓冲区中读取数据。
当发送方的数据到达接收方的数据缓冲区的时候,会先判断该数据是否已经存在,若已经存在(说明这个数据已经收到过了,这个数据是重复的数据),会直接把新发送的数据丢弃;若不存在,则放入缓冲区中。
接收方又如何判定接收到数据为“重复数据”:
判定最主要的依据就是数据的序号。
1.当该数据还未被read/Scanner.next读取,还在缓冲区中,当再次收到该数据时,就会发现已存在该数据的序号了,则认为该数据就是重复数据。
2.若该数据已经被读走了,发送方又发送了该数据,该数据的序号在缓冲区中是不存在的。但是,对于收到的数据,是按序号从小到大排序的,也是按照序号的先后顺序读取,小序号数据被读走,才会读取大序号的数据(类似于带有优先级的阻塞队列)。若发来的数据序号是小于已存在的最大序号,也认为该数据是“重复数据”,直接丢弃就行。
超时重传也不是无限重传的,重传过程中有一定的策略:
1,重传次数:重传次数有上限值,当重传达到一定次数时,还没有收到ACK确认消息,就进行重新连接;若连接也失败,就放弃连接。
2.重传时间:每次重传的超时阈值不是固定的,随着重传次数的增加,超时阈值会增大(重传频率降低)。
随着重传次数的增加,数据包成功到达对方的概率也会增大。
“确认应答”,和"超时重传“这两个机制是保证TCP可靠传输最核心的机制了。
4.连接管理:建立连接:“三次握手”
TCP是有连接的,在客户端真正向服务端传输第一条数据之前,要先建立连接;
客户端执行new Socket(serverIp,serverPort);这个操作就是在建立连接,这只是调用了操作系统Socket的api,真正的建立连接是在操作系统内核中完成的。
建立连接流程:
连接建立完成之后,就可以进行数据传送了!
“三次握手"
内核又是怎样建立连接的呢?
内核通过“三次握手”来建立连接。这里的连接属于虚拟的,目的是为了让双方保存到对方的相关信息。
三次握手流程图:
这里的syn是synchronize单词,意思是 同步的。
syn是一种特殊的TCP报文段:只有TCP报头,没有载荷,不携带应用层数据,且TCP报头中的六个标志位的第五位 SYN 此时为1。
图上显示的是四次信息交流,但实际上,服务端发送的两次信息ACK和SYN可以合二为一,一次发送,就形成了“三次握手”。
服务端向客户端发送的报文中,TCP报头中六个标志位的第二位ACK,和第五位SYN都置为1.这个数据起到两个作用:1,表示接收到上个请求;2,发送SYN。
将两个数据报合二为一的好处:
数据包在发送过程中,每经过一个路由器/交换机,数据报都要进行封装和分用的过程,将两个包合在一起发送,有助于提高执行效率,降低成本。(就类似与同一个卖家同时买了两份衣服,用一个包裹来发送快递一样)
注意:三系握手的第一次一定是客户端先发送的,一定是客户端主动的。
所谓的建立连接,就是通信双方互相向对方发送一个SYN,再互相回复对方一个ACK报文段。当信息发送完成之后,双方就成功保存了对方的信息。
这里的SYN虽然没有载荷,但TCP报头,IP报头,以太网数据帧等信息都是有的,这里包含了当前的服务器的信息,以便告知对方。
客户端向服务端发送SYN,对方是否一定会同意连接呢?
在三次握手中,确认应答和超时重传也都是存在的,一方发送信息后,在一定的时间范围内没有收到对方返回ACK,还是会进行确认应答和超时重传操作的。
“同步”:这个词在多线程中也存在,实际上这两次的出现是不同的含义:
多线程时加锁的synchronized,同步是协调多个线程执行的先后顺序;
TCP里的同步,指的时服务端和客户端进入连接状态,双方就要相互配合完成一系列的工作。
这是同一个术语在不同的情境下有不同的含义。
三次握手的详细图解:
进行三次握手的意义:
1.“投石问路”:三次握手,针对通信路径先进行“投石问路”,初步确认通信链路是否通畅。
2.获取对端信息:三次握手也可以验证通信双方的发送能力和接收能力是否正常。
3.协商一些必要的参数:通信时双方的事情,要配合,一些内容要保持一致。
TCP中很多参数的协商都是在TCP报头的选项部分里体现的
选项范围是0字节到40字节,这里面就包含发送数据的序号,和序号的起始值的确定。
TCP一次通信过程中,发送数据的序号不是从0或1开始的,而是选择一个很大的数,从这个数开始计算,即使是同一个服务端和客户端,当再次建立连接时,起始序号也是不同的。
这样设计的目的时为了防止“前朝的剑,斩本朝的的官”,当上次发送的信息没有发送成功,再次发送,当信息到达对方时;此时当前连接已经结束了,并且又进行了新的连接,当上次连接的信息到达时,通过数据报的序号就可以判定这条数据非正常数据,要进行丢弃。
这种情况称为“先发后至”,当出现这种情况时,由于这个数据包的序号和目前正在使用的序号差别很大,一下就能时别出来,就能将其丢弃了。
这里的序号的使用,并不是随机的,背后也是有一系列分配策略的,这里就不详细说了。(有关TCP的详细资料,可以查看RCF标准文档,这个文档是最权威,最靠谱的)
5.连接管理:断开连接:“四次挥手”
建立连接是保存对方的信息,那么,断开连接就是为了删除对方信息,把对方的信息从数据结构中删除。
四次挥手流程图:
断开连接,四次挥手实际上就是通信双方互相给对方发送FIN结束报文段,再互相发送ACK确认应答信息。这里的流程都是在操作系统的内核中完成的,应用程序不用参与,包括上面的三次握手也是。
这里的FIN时finish,意思是:结束的。也是TCP报头的六个标志位的最后一位,当发送结束报文段时,FIN标志位置为1.
注意:四次挥手第一次发送FIN的,不一定是客户端先发送,服务端也可以先发送,谁先发送都可以,要看代码怎样写。
代码中。调用socket.close(),就会触发向对方发送FIN,当进程直接结束时,也会触发FIN。就是关闭scoket文件,就会触发FIN。
三次握手中间两次的ACK,SYN能同时发送,那么四次挥手的中间的ACK,FIN能不能也合并呢?
这个问题,有时可以合并,有时不可以合并。主要时要看代码的执行顺序,三次握手中的ACK和SYN的触发时机一样,都是服务端收到SYN后,就要发送,和代码无关,但在四次挥手中,若是ACK和FIN中间还有很多代码要执行,就不能合并,要分两次传输;要是两者是相继执行的,则可以合并。
四次挥手详细图解:
三次握手和四次挥手的相似之处和不同:
相似:都是通信双方给对方发送一个SYN/FIN,对方再返回一个ACK;
发送顺序相同:SYN/ACK/SYN/ACK FIN/ACK/FIN/ACK 且中间两次是由同一个主机发送。
不同:三次握手中间两次一定能合并,四次挥手不一定能合并;
三次握手一定是客户端主动第一次发送的,四次挥手,客户端服务器都可以。
6.连接管理:TCP的状态转换
这里谈到的状态和多线程中的状态是同一个概念.
状态转换图:
TCP的客户端和服务端都有一个数据结构保存当前连接的信息,这个数据结构中就有一个属性是"状态",操作系统内核根据状态的不同,决定了下一步要进行的操作.
上图中有好多种状态,现在详细了解其中的几种状态:
客户端与服务端进行三次握手时的状态转换:
LISTEN状态: 表示服务器这边已经准备好了,创建好了serverSocket,并且绑定端口号完成,可以进行信息交流了.
ESTABLISHED:确定的,表示服务端和客户端信息已经建立完毕,可以进行信息交流了.
将应用程序创建好后,并启动服务端,让后打开windows窗口,输入netstat -ano fidstr 端口号,就可以查看到当前服务端的状态了:
此时客户端还没有启动,因此目的IP就是0:0:0:0,状态为LISTENING状态,表示已创建好serverSocket,和已经绑定好端口号,可以接收客户端的连接了.
现在再启动客户端,再次查看状态:
这里由于服务端和客户端是在同一个主机上的,因此,双方的状态都可以查出来.
看到此时服务端的目的IP就是客户端的原IP,客户端的目的IP就是服务端的原IP,现在两者的状态都是ESTABLESHED状态,双方已经建立好连接(完成了三次握手),可以互相发送消息了.
当再开启一个客户端时,就能看到两对服务端和客户端的状态信息:
客户端与服务端进行四次挥手时的状态转换:
CLOSE_WAIT状态:表示接下来就要调用close(),关闭应用程序了;当收到对方的FIN报文段时,就会进入到这个状态.(注意,谁被动断开连接,谁会进入这个状态)
正常情况下,CLOSE_WAITING状态不容易观察到,关闭socket之后,会快速的从CLOSE_WAITING状态进入到LAST_ACK状态。若看到大量的CLOSE_WAITING状态,则有可能服务器代码出现了bug,如socket忘记close()了。
TIME_WAIT状态:表示本端给对端发送FIN,也收到对端返回的FIN报文段,此时进进入TIME_WAIT状态,给最后一个ACK重传留点时间。(谁主动断开连接,谁就进入这个状态)
TIME_WAIT状态出现的意义:若最后一个ACK在发送时,出现丢包,由于对方一直没有收到ACK,就会再次发送FIN结束报文,此时就要重传ACK,WAIT_TIME时间就是给ACK进行重传留一点时间;若是没有这个状态,主动断开连接端的TCP直接被释放掉,那么被动结束的一端就会一直收不到确认应答报文段,就无法释放对端信息。
TIME_WAIT 也不是无限等待的,最多等2MSL(MSL是一个系统内核的配置项,表示客户端到服务端消耗的最长时间,是一个很长的时间),主动断开连接端等了很长时间,对端都没有重传,则认为就不会再重传了。
7.滑动窗口
上面谈到的确认应答,超时重传和连接管理都是为了提高TCP传输的可靠性。可靠性的增强,就意味着单位时间内传输的数据量变少了,传输速度就受限了。因此,滑动窗口就是用来提高传输速度的机制。
在确认应答机制下,发送方每次收到一个ACK,才会进行下次消息的发送,这就导致大量的时间都浪费在等待ACK上了。
滑动窗口就是解决这个问题的。
引入批量传送,通过批量发送数据,再统一等待ACK,减少等待时间(这个策略是带有一定损耗的,并不是提高发送速度)
之前是发一个数据,等一个ACK,然后再发下一条数据;引入批量发送后,发一条数据,不等ACK的到达,接着发第二条数据,继续往下发,连续发了一连串数据后,再统一等待一波ACK的到达。把多次等待ACK时间,用同一份时间来等,减少了总的等待时间。
滑动窗口的由来:
这个白色区域称为滑动窗口的"窗口大小",连续发送这四分数据,在这之间会收到ACK的应答报文,收到一个就再发一个数据,(而不是收到四个数据的ACK后,才再发送下一个数据),由于数据的发送时非常快的,直观上感觉 这四个窗口就像在滑动一样,这个过程就叫滑动窗口。
滑动窗口提高了数据发送的速度,但当数据出现丢包时,又怎样保证数据发送的可靠性呢?
当出现丢包时:
1.ACK丢了:
当滑动窗口的大小为6000,在发送数据的过程中,返回ACK中间丢了几个,这其实时没有影响的,当最后一个ACK到达发送方时,就证明了前面发送的数据都已经被接收方收到了(前提是ACK丢了,并不是数据包丢了),即使前面的ACK出现了丢包,对结果也没有影响,无需做任何处理。
2.数据报丢了:
在发送的过程中,某个数据报丢了,此时,接收方就会一直发送丢了的数据包序号的ACK(多次向发送方索要1001这个序号的数据报),多次提醒发送方该数据并未成功发送,要再次发送该数据包。此处的关键要点就是 返回的应答报文的序号。
快速重传:
在上述重传过程中,整体效率是非常高的,重传是具有“针对性”的,哪个数据包丢了,就重传哪个,已经收到的数据是无需再重传的,对别的数据没有影响,当丢失的数据包重传成功之后,就获取到了全部的数据包了。整体的效率没有额外的损失,把这种重传就成为“快速重传”。
确认应答机制与滑动窗口机制的转化:
滑动窗口也有确认应答,只不过等待的时机不一致,转成批量的了,批量的前提是在短时间内要发送大量的数据,当要发送的数据很少时,滑动窗口就“滑”不起来,此时就退化为 确认应答了。
可靠性的判定:
如果当前是按照 滑动窗口发 送数据的,保证可靠性是快速重传,判定丢包的标准是收到连续多条ACK索要同一份数据。
若是确认应答方式发送数据,则保证可靠性是 超时重传,判定丢报的标准是达到 超时时间没有ACK到达.
8.流量控制
流量控制相当于是对滑动窗口的一个补充.
滑动窗口可以提升效率,那么滑动窗口的大小越大,效率就会越高,但窗口大小能一直增大吗?
明显是不能的,因为任何提升效率的行为都不能影响到可靠性。
当滑动窗口的大小越大时,发送的数据就会越快,此时就有可能丢包,(接收方的接收缓冲区满了,当继续给他发送数据时,就会丢包/接收方的应用程序处理不过来这么多的数据(通过read、Scanner从缓冲区中读走数据))。这时,当接收方的缓冲区满了的时候,就算发送方进行多次重传,都是没用的,都会出现丢包,并且还浪费资源。
与其接收方满了,发送方要等待接收方处理,不如发送方提前感知,降低发送速度。让发送发的发送速率和接收方的接收速率,步调一直。
流量控制就是让 接收方的接收速率 反过来影响 发送方的发送速率。
TCP报头中的16个窗口大小报文段:
他就是要完成这个功能:通过这个字段来反馈给发送方的发送速率。
这个字段在普通的TCP报文中不起作用,在ACK报文中才会起到作用。
16位窗口大小,(TCP中是以4字节为单位的,16位就是64字节)并不是每个滑动窗口都是64位的。
TCP报头的选项中有一个参数叫“窗口扩展因子”,真正的窗口大小是:
16位窗口大小*2^窗口扩展因子.
接收方就会根据自己接收缓冲区的大小,来动态调整返回的ACK中16位窗口大小的值,之后发送方就会根据收到的ACK动态调整滑动窗口的大小来控制发送速率,起到流量控制的效果。
当接收方的接收数据的能力为0时,发送方就暂时不再发送数据了,但是发送方什么时候才能再发送呢?
“窗口探测包”
停止发送数据后,发送方会周期性向接收方发送“窗口探测包”,这个包不带有载荷,只是为了让接收方发送ACK,判断是否有可用缓冲区了,发送方能否发送数据。
9.拥塞控制
拥塞控制也是用来限制发送方的发送数据速率的。
流量控制是站在接收方的角度来制约发送方的发送速率,拥塞控制是站在发送方的简爱读来限制发送方的发送数据速率。
当接收方处理数据很快,发送方发送数据也很快,若中间传输路径中出现了数据包堵塞,就算发送的数据再快,也没有用,这又要怎样解决呢?
拥塞控制就是解决这个问题的。
发送方按照一定的窗口大小发送数据包,当出现丢包了,就视为中间路径出现了堵塞,就减小滑动窗口的大小,当不再出现丢包时,就再扩大滑动窗口的大小,这样就能解决网络通信过程中路径上的“随机”堵塞问题,很好的适应当前网络环境的复杂性。
对于流量控制和拥塞控制是同时存在的,谁产生的滑动窗口更小,就按谁的分配。
拥塞控制又是如何控制窗口大小呢?
通过拥塞控制策略产生窗口大小成为“拥塞窗口”。
拥塞控制 通过这几个步骤完成:
1.慢启动:刚开始传输数据时,速率比较小,采用的拥塞窗口也非常小。(此时的网络状况未知,若路径比较堵塞,一上来就设很大的话,就很容易造成堵塞)
2.快增长:按照1的传输,没有出现丢包,就说明网络是通畅的,就可以增加窗口的大小了,会按照指数级增长(*2)。
3.窗口大小不会一直按照指数增长,会有一个“阈值”,当增长值达到阈值时,就按照线性增长(增长速率降低),线性增长能让当前窗口保持在一个较高的值,也不会出现丢包。(若按照指数增长,可能一下就造成网络堵塞了)
4.线性增长到一定时间后,由于传输速率太快,还是可能会造成丢包;一旦发现丢包,就要把拥塞窗口设置成一个较小的值,重新设置阈值,再次循环上面的步骤。
拥塞控制的流程图:
上面分析的第四步骤是按照上图中的TCP Tahoe版本分析的,
而TCP Reno版本是当出现丢包时,将拥塞窗口大小设置为(上次的丢包窗口-阈值窗口),然后进行线性增长.(老版本是早时TCP刚出现的时候,网络还是很不稳定的,经常会出现大规模的网络波动,一旦出现堵塞,此时网络带宽很可能就承受不了,这种小速率的增长更适合当时的环境,但现在的网络环境已经是非常稳定的了,不需要从很小的窗口值增加了,可以加快传输速率了)。新版本的指数增长只出现在刚开始的阶段,之后都是线性增长了。
拥塞控制通过动态改变拥塞窗口的大小实现了“动态平衡”。
10、延时应答
延时应答是基于滑动窗口,想要进一步提高数据传送效率。
延时应答指的是延时发送ACK,当接收方收到消息后,不是立即返回ACK,而是先等一下再发送.
目的是在允许的时间范围内,让滑动窗口尽可能的大。给接收方一点时间,用来处理收到的数据,等一下再发送,就能有更多空余空间可以接收数据了。
正常每个数据都有ACK,此时就可以每隔几个数据再发送ACK,这样既能起到延时应答的功能,也会减少ACK的传送次数,降低开销。
这里的每隔几个数据,不仅仅和数据量有关系,也和时间有关,当延时到一定时间,即时数据个数没够,也会返回ACK,而不失一直等着.
11.捎带应答
捎带应答是基于 延时应答引入的机制,也是用来提高数据传输效率的。
正常数据发送都是“一问一答”:
但每次返回ACK ,一般情况下都还要再发送request/response,那么能不能将每次的ACK和response/request合成一个数据包呢,这样既能提升发送数据的效率,又能节省开销。
这就是捎带应答。
ACK报文就是把标志位ACK置为1,正常的报文也能完成这样的功能,且对原信息没有影响,是完全可以合并
在捎带应答的加持下,每次的请求应答,都有可能触发捎带应答,将两次消息合二为一。
12.面向字节流
TCP的传送是面向字节流的,在面向字节流中,有一个很大的问题,就是每次发送信息后,并不是立即取出,而是之后再处理的,先放在缓冲区中,当读取到时再取出。但当读取数据的时候,又怎么知道哪段数据是一个完整的应用层数据包呢?
这就是“粘包问题”。
处理粘包问题的两种方法:
1.设置特定的分隔符:通过特殊符号作为分隔符,当读到分隔符时,就认为一个数据包读取结束了。
2.指定出包的长度:通过在每个数据包的前面加上一个特殊空间,表示整个数据包的长度.
面向字节流问题,不仅仅是TCP才存在的问题,只要是以字节流为单位的,都存在这样的问题.
这是需要程序员在设计应用层的时候考虑的问题,写应用层逻辑代码的时候,要自行处理.
对于UDP就没有这样的问题,UDP传输是以 UDP数据报 为单位的,在UDP这一层就已经分开了,每次读取一个数据包就可以,不需要使用额外的手段进行分开.
13.异常情况
丢包会影响数据传输的可靠性,但是若网络出现故障,这就是更严重的问题了,又要怎样处理呢?
异常分为一下几种情况:
1.其中有一方出现进程崩溃。
进程无论是正常结束,还是异常崩溃,都会触发回收文件资源,起到文件关闭的效果(是系统自动完成的)。TCP连接的生命周期会比进程长一点,虽然进程已经结束,但TCP还在,还能完成结束连接(完成四次挥手)。
2.一方出现关机(正常流程关机)
当一方主动按了关机键,就会强行结束进程(强杀进程),终止进程就会触发四次挥手,但四次挥手不一定能执行完,系统就马上关闭了。
四次挥手,若执行的快,执行完了,那么就能正常删除对方的信息;若没有执行完,关机方第一次发送的FIN能发出去,对端收到FIN后,就要进入释放连接的流程了,就会返回ACK和FIN,但此时接收方不一定能收到(已经关机了),也就无法返回ACK,对方一直没有收到ACK,就会进行重传,重传几次,还是没有回复,就会单方面 自动结束进程,释放信息资源了。
3.一方出现异常关机(断电/直接拔电源)
1>.断电的是发送方:
当发送方断电,就无法再发送信息了,此时接收方正处于阻塞状态,等待对方发送的信息,接收方一直没有收到信息,就要判断对方是丢包了,还是进程挂了。接收方就会发送“心跳包”,来监测对方的状态。
心跳包是一种特殊的数据包,不携带应用层数据,是周期性发送的,若心跳包没有心跳了,就视为对方挂了。
若检测到对端没有心跳了,就尝试重新连接,重连之后还是失败,就放弃连接,自动释放对方的信息了。
2>.断电是接收方:
当接收方断电后,就无法返回给对方ACK了,对方一直收不到ACK,就会进行重传,重传几次还收不到对方的ACK,就会尝试"复位"连接,清除原来TCP中的各种临时数据,重新开始,这里要用到一个6个标志位中的一个 "复位标志位"RST
若对方已经断电,再次发送RST,也是收不到对方的回复,就会自动结束连接,释放保存的对方信息了。
6个标志位中的四个ACK,SYN,FIN,RST,已经在前面提到过了,还有URG,PSH
URG和TCP的数据外带有关系,TCP中有些特殊的数据报,携带了特殊功能的数据。
PSH是push,推的意思,催促对方快点发送消息。
4.网络断开
通信双都有可能断开网络,既可能是发送发,也可能是接收方,这种情况本质上就是3.一方出现关机 中1和2的结合.就不再说了.