文章目录
- UDP协议
- UDP协议的段格式
- UDP的传输过程
- 校验和
- 无连接
- TCP协议
- TCP报文的格式段
- 有连接
- 可靠性
- 确认应答
- 超时重传
- 如果ACK丢了呢?
- 序号和确认序号
- 连接的构建和断开
- 连接的构建(三次握手)
- 三次握手的作用
- 为什么握手是三次,而不是四次或者两次呢?
- 连接的断开(四次挥手)
- 四次挥手的流程
- 什么是time_waite状态
- 什么是SYN FIN ACK
- TCP协议传输效率的优化机制和安全机制
- 滑动窗口
- 第一种:ACK丢了
- 如果数据包丢了呢?
- 流量控制(安全机制)
- 拥塞控制
- ACK的延迟发送
- 捎带应答
UDP协议
UDP协议的段格式
16位UDP长度表示整个数据报的长度
如果校验和出错则会直接放弃
UDP的传输过程
UDP的传输过程相当于是写信传输,一个udp报文其实我们就可以理解为一封信那么在信件的传输过程中势必会造成信件的丢失,此外我们想一下UDP的传输中,UDP的报文最大只有64kb,可是假如说我们要传输的数据超过64kb怎么办呢?那当然是用多个UDP报文去传输了,可是这时候就涉及到一个问题,那就是我们使用多个UDP报文去传输的话我们怎么知道自己的报文有没有丢失呢?就比如说我们寄多封信件,那么在信件的传输过程中我们无法保证这个信件是否能够全部到达接收方,也不能保证他们的到达顺序,那么接收方该如何知道我们发送的数据有没有丢失,那么这就需要有一个东西可以告诉接收方,我发了多少信息并且这些信息有什么特征。这就用到了校验和
校验和
在UDP的传输过程中为了应对数据的丢失,做了一个处理那就是校验和当一个数据过大需要用到多个数据报去传输的时候那么这时候就会用到校验和这样的机制这个机制背后的原理是什么样的呢?请看下图
在udp传输过程中我们的数据会采用一种算法,将我们的内容取出一部分特征将其转换为一个整数这个整数就是校验和,然后当我们的数据传输到接收端的时候我们的校验和其实也传输了过去,当我们的接收端接收到数据之后会对接受到的数据进行一个相同的算法然后得到一个新的校验和,之后只需要拿着新的校验和与数据报中的校验和进行比对就可以发现这次的数据有没有丢失,或者出错,此外我们要知道一点那就是我们的UDP其内部其实并没有实现可靠传输的这样一个机制,因此如果你想要丢失重传的话那就需要自己代码层面上进行实现。
无连接
UDP传输的无连接性,其实我们是可以从代码上看出来的,我们的UDP在传输前是先打一个包,也就是Datagrampacket,我们的要传递的数据,源IP地址,目的IP地址都存在这个包里面,UDP本身是不会保存这些的,这也是他和TCP的主要区别之一。
TCP协议
TCP报文的格式段
这里面各种格式代表什么意思我们一点一点的来进行讲解。首先第一个
有连接
我们经常说TCP是有连接的UDP是无连接的,那么这个有无连接究竟是怎么体现的呢?我们来对比一下UDP和TCP的代码
UDP
DatagramPacket requesgramPacket=new DatagramPacket(reques.getBytes(),reques.getBytes().length,InetAddress.getByName(IP),Port);
TCP
我们从这里其实是可以看出一些差异的,UDP想要传输的话那么每一次都必须构建一个DatagramPacket对象把所有的信息保存进去,而TCP想要发送的话就简单了需要只需要构建好连接之后通过这个连接将自己的数据流直接写就行了。由此我们可以感受到有无连接的区别
可靠性
确认应答
确认应答机制是TCP可靠性的关键机制之一,这个机制是一个什么意思呢?什么是确认应答呢?确认应答我们可以理解为,当一方发出一个信息的时候那么另一方就必须针对这个消息做出应答。我们可以看一下下面这个图
在下面这个图中,当A发送了一个请求之后呢,B就会先发送一个ACK之后再发送一个回应,那么这里面的这个ACK其实不带有任何数据,这个ACK的目的是为了告诉发送方你发送给我的数据我收到了,那么这时候,A就知道了自己的数据已经被收到了就不需要采取更多的操作只需要慢慢的等回应就行了
举个例子,假如说你和你的好友互相发送短信,那么当短信成功发送到对方的手机上之后往往手机上会显示一个已送达,那么这时候作为发送方的你就知道这个消息已经成功发送到他的手机上了没有因为网络或者别的问题导致短信丢失,这时候你就可以耐心等待对方回复即可。
超时重传
而有了这样一个机制就能保证TCP的可靠性了吗当然不是,除了这样的一个机制之外我们还需要针对这种情况做出处理才对,那么应该做出什么处理呢?当然是重传了,此时就引出了我们要继续说的超时重传机制。重传机制又是什么样的呢?我们采用生活中的例子来说明一下,假如说我要给我的女神乐乐,发送一个短信,但是这时候网络信号比较差,这个短信一直转呀转,那么在转的这个过程中呢,其实这个信息已经被传输了很多次了。在这个转的过程中我们从我们底层来看其实就是信息在发送的过程中不断的重传,也就是说如果这个消息发送失败了其实在底层来看他发送了十几次都是失败告终。那么判断是否失败就是看对方是否发来了ACK报文,如果对方成功发来了ACK报文就说明这个消息是被重新接受的。
如果ACK丢了呢?
那么这时候就有人会有疑惑那如果我传输的数据其实没丢,但是他给我发送的ACK报文丢了呢?那么如果对方发送的ACK报文丢了的话我们也会进行重传。
序号和确认序号
那么上面两个解决了我们发送的数据丢失的问题,那么如何解决我们发送的数据乱序的情况呢?也就是当我们发送了多条数据之后可能会由于网络问题导致出现了后发先到的情况那么这种情况该用什么措施呢?那么也就是需要用到我们的序号和确认序号了。
我们假设我们要发送的数据包所占据的字节数都是1000,那么第一个数据包,占据的这片内存的范围是1到1000(这里的数字就理解为数组下标-个下标对应的那部分值的内存大小是一个字节那么1000字节所占据的这个数组内的下标肯定是1到1000了)那么此时这个数据包的序号就是1000他的确认序号就是1001,那么与他紧邻的数据包表示的范围就应该是1001到2000那么它的确认序号也就是2001,那么我们知道先发送的消息肯定是先进入这个内存中的消息,那么既然先进入,那么把这个内存想象成一个数组的话先进入,肯定就先占据靠前的下标了,这也就是为什么我们的序号和确认序号可以保证顺序的原因,因为确认序号越小肯定就说明这个消息时越早被发送的了。
连接的构建和断开
连接的构建(三次握手)
TCP是有连接性的那么连接的构建是通过什么来完成的呢?我们简称为三次握手那么我们下面通过下面的图来解释一下什么是三次握手,
首先由发起连接的一方发送SYN报文来请求B建立连接那么在底层来看这里的底层如何进行的呢?
也就是说在内核里面我们建立连接其实是会先构造出一个文件描述符,然后调用connection方法建立连接,那么我们也很好理解毕竟连接其实就是一个抽象的概念,其实建立连接的意思是指通讯的双方保存对方的信息,也就是保存对方的IP地址端口号之类的,那么当我们保存好之后未来我们要进行通信的时候只需要用这个保存好的信息,直接发送就可以了,因此我们既然需要保存好对方的地址的话我们当然就需要,创建一个文件,用这个文件来接收对方发送的各种信息。并且通过这个文件来标记一下我们保存的都是那些主机的IP地址,和端口号。这是客户端在发送syn所经历的事情
服务端在接收到请求之后的历程
对于一个服务器端来说请求连接服务器的主机可能不止一个因此我们可以将需要连接的主机全部放入一个队列里面当一个主机与服务器的交互结束之后就会断开连接,然后断开连接之后再从等待队列中取出队头的等待主机从而进行新的连接,那么这时候就需要有一个监视器这个监视器就是listenfd。
那么以上就是再建立连接的时候客户端与服务器端,彼此交互的过程。
三次握手的作用
通过三次握手可以使得两台主机建立连接,我们也说了建立连接其实本质上是保存对方的信息,那么这个保存对方的信息,除了对方的IP地址和端口号之外就没了吗?当然不止这些,在我们建立连接的过程中我们还需要两台主机进行协商,可以理解为签合同,在三次握手的过程中两台主机会对一些信息进行协商,比如说双方的接收缓冲区是多少啊等等之类的。通过三次握手我们可以使得通信的双方建立成功连接
为什么握手是三次,而不是四次或者两次呢?
首先其实我们的握手为什么是叫做三次握手呢?我们看一下图,我们发现在这里其实是有四条交互箭头的,那么为什么不叫四次握手呢?因此这里面服务器端发送的ACK和syn是进行了捎带应答的(这个机制在下面会有讲述),大家简单的理解为两条数据合并成了一条进行发送即可,因此就变成了下图
那为什么是三次不是两次呢?这里我们来举一个例子来说一下比如说此时A要对B进行表白,那么此时就有了下面的对话。
当A向B进行表白的时候那么此时A(我)会先发我们可以在一起吗?那么此时女主(Bcl)因为她也喜欢我所以就很乐意和我在一起,可是他会有疑虑会担心我不是因为喜欢,而是想骗钱,所以会询问我,是否是真的喜欢,此时我回答是真的喜欢,于是连接建立完成,由此大家可以看出我们这时候在进行连接的时候是需要三次的,两次是无法完成上述的任务的。
连接的断开(四次挥手)
四次挥手的流程
连接的断开该如何断开呢?断开涉及到了四次挥手的话题四次挥手是怎么进行的呢?如下图
我们来讲述一下这个图解是怎么回事,首先,请求断开连接的一方会先发送一个FIN请求,也就是请求断开连接,然后接收到请求的B端就会对这个请求进行ACK应答应答了之后会也发送一个FIN请求,然后A端会进行一个ACK应答然后开始断开,那么针对这里面的一些细节我们进行一个详细的阐述
什么是time_waite状态
有的人可能会疑惑这个time_waite是什么东西,这个其实就是等待状态,也就是当A端回复了最后一个ACK之后会进入等待,等待一段时间后再断开,那么为什么要这样呢?我们可以想一下如果没有这个状态会发生什么?首先假如说A端发送的最后一个ACK,然后没有time_waite这个状态直接进行close,那么假如说这个ACK丢失了呢?如果它丢失了就会导致B端接收不到ACK应答从而重新发送FIN然后又因为,此时的A端已经close了,她无法收到B端重新传输的FIN报文,并且也不可能再发送ACK报文了,这时候会导致B端不断的触发超时重传机制从而导致B端不断的去重传,FIN报文然后,直到多次重传过后,B端断开连接。这就造成了B端重传FIN报文造成了资源的浪费,也就是一句俗语叫做占着茅坑不拉s,因此我们设置了一个time_waite状态当我们发送了ACK之后会等待一段时间,如果在等待的时间内,再次收到了FIN就可以重新发送一个ACK了。
什么是SYN FIN ACK
这些是报文的格式其实我们TCp结构图中是有的如下图
也就是我在上面写着会在这里进行讲解的东西,那么当我们发送ACK报文的时候会将ACK这个位置置为1同理可得其他类型的区分也是如此。
URG:紧急指针是否有效
ACK:确认号是否有效
PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文段
SYN:请求建立连接;我们把携带SYN标识的称为同步报文段
FIN:通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段
TCP协议传输效率的优化机制和安全机制
我们知道TCP为了保证可靠传输因此引入了超时重传啊,确认应答啊之类的但是这些机制对于计算机来说都是效率上的消耗会导致计算机在效率上被拖慢因此TCP也引入了一些机制,做亡羊补牢那种让计算机可以在效率上快一些弥补一些与UDP的差距。
滑动窗口
滑动窗口:我们刚刚谈论了TCP的确认应答机制,我们知道,当我们发送了一个报文之后对方就必须给我们回复一个应答报文,但是这种机制的效率会非常的差。
因此该怎么办呢?这时候就有人想到既然一次发送一条数据的效率太慢那就一次发送多条数据来提高我们的效率
由此便提出了滑动窗口概念也就是说发送消息的时候将窗口内的消息全都发送出去。然后接收到应答的话就将接收到应答的数据排出滑动窗口,并且重新添加一个消息,并发送,这样子就有了如下概念图
窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。上图的窗口大小就是4000
个字节(四个段)。
发送前四个段的时候,不需要等待任何ACK,直接发送;
收到第一个ACK后,滑动窗口向后移动,继续发送第五个段的数据;依次类推;
操作系统内核为了维护这个滑动窗口,需要开辟 发送缓冲区 来记录当前还有哪些数据没有
应答;只有确认应答过的数据,才能从缓冲区删掉;
窗口越大,则网络的吞吐率就越高;
这时候就会遇到一个新的问题,那就是如果我们窗口中的数据发生了丢失该怎么去重传呢?这里就需要分情况进行讨论了。
第一种:ACK丢了
那么就是下面的这种情况
假如说我们的第一个ACK1001丢失了,那么这时候ACK确认应答的机制的好处就显现出来了如果某个ACK丢失那么由于ACK的确认应答有个特点那就是如果某个ACK报文发送过去了就说明这个ACK之前的所有消息都已经被接受到了,比如说B发送了ACK的确认序号是2001那就是说明2000和1000这两个报文也全都接受了。
如果数据包丢了呢?
比如说如果是1000这个数据包丢了那么就会引发重复的确认应答,这时候就可以提醒发送方这个消息丢失是的其进行重新发送
当某一段报文段丢失之后,发送端会一直收到 1001 这样的ACK,就像是在提醒发送端 “我想
要的是 1001” 一样;
如果发送端主机连续三次收到了同样一个 “1001” 这样的应答,就会将对应的数据 1001 -
2000 重新发送;
这个时候接收端收到了 1001 之后,再次返回的ACK就是7001了(因为2001 - 7000)接收端
其实之前就已经收到了,被放到了接收端操作系统内核的接收缓冲区中;
流量控制(安全机制)
由于接收端处理数据的速度是有限的,如果说发送端发送速度过快就会导致接收端的存储缓冲区被挤满了,如果还是继续发送就会导致这些数据溢出,也就是丢失。
因此我们的TCP通讯要根据此时接收端的接收能力来保证自己的发送速率。那么这一点该如何做到呢?
其实也是我们的TCP报文段中我们可以看到,TCP的报文段中有这样的一个结构
那就是这个16位窗口大小,这16位窗口大小其中填写的就是自己缓冲区的大小当自己处理数据的能力较低,窗口大小减小,那么发送端在得到消息的时候就会降低自己发送数据的速度,当窗口大小字段越大,就说明此时的数据吞吐量越大如果接收缓冲区满了的话那么就会将这里填写为0,那么此时发送端就会停止发送数据但是会间隔一段时间后发送一个询问,来查询此时接收端的窗口大小。
拥塞控制
什么是拥塞控制呢?其实我们的数据从发送端传输到接收端,其实这个过程不一定只有两个机器而是很多机器在相互传输,就像我们在高中传递小纸条一样,我们会让别人帮忙传递。
那么在数据的传输过程中,由于涉及到很多机器,这就导致我们的信息传递速度如果一直按照一个很快的速度就会导致我们上面说的哪个问题,也就是某个机器可能会,处理数据能力较低,导致接收缓冲区满了的情况,并且因为,这里面涉及到很多的主机,导致如果我们采取上面的那种策略会很麻烦。那么该怎么办呢?
TCP协议传输就想了个办法,在刚开始的时候让传输速率慢一些,然后逐渐增长,当增长到某个值发生了,丢包的时候我们再让这个速度慢下来然后,再继续增长,使得我们的传输速度维持在一个动态的区间内。也就是慢启动机制传输,TCP引入 慢启动 机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传
此处引入一个概念程为拥塞窗口
发送开始的时候,定义拥塞窗口大小为1;
每次收到一个ACK应答,拥塞窗口加1;
每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小做比较,取较小的值作为实际发送的窗口;
通过上面的这张图我们可以看到,在我们数据刚开始传输的时候虽然速率较慢但是其增长速度很快。
当TCP开始启动的时候,慢启动阈值等于窗口最大值;
在每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置回1;
并且这里网络拥塞部分,只有发生大量丢包的时候才会判定为网络拥塞,而少量的丢包则是会进行超时冲床机制,拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的
折中方案。
ACK的延迟发送
ACK的延迟发送,我们上面讲到窗口大小的时候说明了如果接收端在收到消息的瞬间直接发送ACK的话就会造成效率的底下,因此我们引入了滑动窗口一次性的发送多个数据包,并且这个滑动窗口的大小是根据当前接收端的缓冲区大小(可以理解为能够承受消息的能力大小)那么此时我们可以联想一下假如说接收缓冲区的大小是1mb但是此时这里面的数据有500kb如果我们直接进行返回的话那么此时发送端接收到的接收端缓冲区大小就是500kb,但是如果我们延迟发送呢?这时候由于接收端内部还在处理着数据因此当我们延迟发送的这部分时间接收端在处理着自己缓冲区内的数据,当相应ACK应答的时候就会返回一个处理一部分缓冲区内部数据后的大小也就可能是600kb,那么此时接收方收到后就会将自己的窗口大小调整的更大一些,然后因为窗口更大那么数据的吞吐量一定也是更大的由此我们可以得出,传送的效率也就增高了。
那么所有的包都可以延迟应答么?肯定也不是;
数量限制:每隔N个包就应答一次;
时间限制:超过最大延迟时间就应答一次
捎带应答
有了上面的延迟发送,那么我们的就可以拥有另一种机制也就是捎带应答,其实这个很简单,比如说我们的ACK由于延迟应答,因此此时当我们要发送ACK的时候恰好接收端有个数据也要发送,那么这时候我们就可以进行捎带应带了。那么我们来看看如果没有捎带应答机制是怎样一个聊天场景呢?如下图。
上图所示就是没有延迟应答,由于此时发送端发送了请求然后接收端,接收到请求之后直接发送ACK报文然后处理这个请求处理完成之后返回答复。那么此时这个聊天是不是就感觉很怪异啊。
那么如果因为延迟应答导致我们在发送ACK的时候我们的程序恰好可以返回回答,那么此时就可以将这个ACK报文与我们的回复数据一同发送,这就是捎带应答如下图
吹灭读书灯,一身都是月,希望此时准备休息的你也能开开心心。