文章目录
- UDP
- TCP
- TCP为什么可靠
UDP
传输层的作用是负责能够从发送端到传输端。
我们的主机上有多个程序,那么怎么分辨哪个信息是发给哪个程序的呢?—端口号。其是一个16位的无符号整型,端口号分为知名端口号(0-1023)和OS动态分配的端口号(1024-65535)。一个进程可以绑定多个端口号,但一个端口号只能被一个进程绑定。
和应用层一样,传输层也有协议,我们先来看UDP协议。
其中16位UDP长度表示整个数据报的长度(首部+数据)。
如果校验和出错, 就会直接丢弃。
UDP的特点:
无连接: 知道对端的IP和端口号就直接进行传输, 不需要建立连接;
不可靠: 没有确认机制, 没有重传机制;如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层 返回任何错误信息;
面向数据报: 不能够灵活的控制读写数据的次数和数量
那么什么是面向数据报呢?
就是应用层交给你多长的数据,UDP原样发送。也就是说:发送端发多少次,接收端就得接收多少次。
UDP是没有发送缓冲区的,调用的sendto会直接将数据给内核,但是有接收缓冲区,不保证接收的UDP报文顺序和发送时一样。
并且当缓冲区满后,再发过来的报文则是会被直接丢弃。
TCP
TCP全称为 “传输控制协议(Transmission Control Protocol”). 人如其名, 要对数据的传输进行一个详细的控制;因此TCP的实现也更复杂一些,其报文首部也要更复杂。
TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。
什么是面向连接呢?
面向连接,是指通信双方在进行通信之前,要事先在双方之间建立起一个完整的可以彼此沟通的通道,这个通道也就是连接。在通信过程中,整个连接的情况一直可以被实时地监控和管理。而无连接的通信,就不需要预先建立起一个联络两个通信节点的连接来,需要通信的时候,发送节点就可以往“网络”上送出信息,让信息自主地在网络上去传,一般在传输的过程中不再加以监控,让该信息的传递在通信网络中尽力而为地往目的地节点传送。
TCP面向连接的,那么其连接过程是怎样的呢?
我们先来看TCP头部字段中的几个标志位。
6位标志位:
URG: 紧急指针是否有效
ACK: 确认号是否有效
PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走
RST:对方要求重新建立连接; 我们把携带RST标识的称为复位报文段
SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段 FIN:通知对方, 本端要关闭了
TCP是通过三次握手建立的连接。
一开始我们的客户端和服务端都是处于close的状态,然后服务端处于监听状态,等待客户端来连接。
第一次握手:客户端发送SYN包给服务器,然后将自己的状态改为SYN_SENT,并等待服务器确认。
第二次握手:服务端收到SYN包后,向服务器发送ACK+SYN的响应报文,将自己状态改为SYN_RCVD。
第三次握手:客户端收到服务器的响应报文后发送SYN给服务器,表明自己收到了消息,并将自己的状态改为ESTABLISHED
,服务器在收到ACK应答后,将自己状态改为ESTABLISHED,表明建立连接成功。
因此第三次握手是可以携带数据的,前两次握手是不可以携带数据的,第三次握手时,客户端的状态已经改为了连接状态,服务器在收到应答后,状态也将变为连接状态,所以可以将应答和数据一起发送过去。
- SYN_SENT状态是客户端发送连接请求后的等待状态,表示已发送请求但还未收到服务器的确认。
- SYN_RCVD状态是服务器接收到客户端连接请求后的等待状态,表示已接收到请求但还未收到客户端的确认。
- ESTABLISHED状态表示连接已经建立,双方可以进行数据传输
那为什么是三次握手呢?
三次握手可以确认双方的接收和发送能力是否正常,可以嫁接同等成本给客户端。
第一条本质就是在验证全双工,那么怎么理解第二条呢?
如何理解连接呢?
在我们的服务器中,未来会有大量的客户端与我们进行连接,所以在server中会有大量连接存在,那么要不要将这些连接管理起来,怎么管理,先描述,在组织。所谓连接本质就是一种数据结构,建立连接成功时,创建对应的连接对象,再将这些对象组织起来,所以维护连接是有成本的。
那么就会有不法分子攻击我们的服务器,此时我们就可以嫁接同等的成本给不法分子。杀敌一千自损八百。这叫做SYN洪水。
同步序列号。
那么一次握手行不行呢?
答案是不行的—一是无法验证全双工,二是一次握手,客户端可以一次发送多次连接请求,然后关闭自己的主机,而我们的客户端一次就得建立这么多连接的内核对象。
那两次呢?
对于服务器来说,两次握手,服务器只知道客户端能够发送,并不清楚其能接收。并且两次握手,无法嫁接同等成本给客户端。
那四次呢?
四次也是无法嫁接同等成本给客户端,并且,连接的过程也是消耗资源的,连接次数过多并不好。
TCP连接断开时的四次挥手
第一次挥手:客户端发送FIN请求给服务器,并将自己的状态改为FIN——WAIT1。
第二次挥手:服务器收到FIN请求后,形成应答报文,发送给客户端,这个响应还可能带有未发完的数据,服务器的状态改为CLOSE_WAIT,客户端收到应答后,将状态改为IN_WAIT2。
第三次挥手:服务器完成数据传输后,向客户端发送FIN请求,表示,服务器也没有数据要发了,然后将状态改为LAST_ACK。
第四次挥手:客户端收到FIN请求后,发送应答,并将自己的状态改为TIME_WAIT,服务器收到应答后将状态改为CLOSE,至此服务器才算关闭了连接。
客户端在经过一段时间,确认服务端没有要求重传时,将状态改为CLOSE,关闭连接。
下面我们再来解释两种状态:
CLOSE_WAIT:
通常来说,如果被动端意识到应用程序没有正常关闭套接字,它可能会发送一条警告消息,以通知应用程序关闭套接字。
如果被动关闭连接的一方(服务器)未能正确关闭套接字,CLOSE_WAIT 状态可能会一直持续下去,导致资源泄漏。
TIME_WAIT:
在 TIME_WAIT 状态期间,该连接处于等待状态,以确保网络中的任何未完成或延迟的分节都能到达目的地(确保对方(通常是服务器)已经成功收到并处理了FIN包。在这段时间内,该连接会进入TIME_WAIT状态)。TIME_WAIT 状态的持续时间通常为 2 倍的最大报文存活时间
在TIME_WAIT状态期间,这个套接字不会被分配给其他连接使用,但会占用一定的系统资源
TCP为什么可靠
TCP 是通过序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现可靠性传输的。
我们先来谈一谈重传机制
超时重传所依赖的就是确认应答和按序到达,有了它两我们的超时重传才可以实现。
那么什么是确认应答呢?
我们在远程通信中,怎么保证对方是收到我们所发出的消息呢?—对方对你发送的消息进行回复。
那么什么是按序到达呢?
在我们的TCP报头中,有序列号和确认应答号。
假设有一个TCP连接,发送方要传输一段长度为100字节的数据。发送方将每个报文段的序号设置为字节流中对应字节的序号。假设发送方将前50个字节发送到接收方,此时发送方的序号为0~49 ,接收方的确认序号为50。接收方成功接收到这些字节后,会设置确认序号为50,表示接收方期待接收的下一个字节为50。发送方继续发送剩余的50个字节,此时发送方的序号为50~99,接收方的确认序号为100。接收方接收完成后,将确认序号设置为100。
可是这样看来只需要一个确认序列就好了,为什么要有两个呢?-----接收方在进行应答时也可能携带数据。
所谓的重传机制就是发送发在发送完数据后,若接收方在一定时间内没有接收到应答,则会将前面发送的数据再次进行发送。
为什么没有收到应答呢?
接收端压根没收到数据
接收方发送的应答丢包了
网络延迟
这样一发一回应效率是不是太低了,因此实际情况中,发送方一次性会发送多个报文,那接收方该怎么回应呢?我们的序号就排上了用场了。
当我们发送的最后一个报文的序号是100,那么要是接收方将报文全部收到只需要恢复101就行,若是有中间的报文丢失,则从短缺序号开始之后的所有报文都进行重传。
流量控制
TCP是有接收发送缓冲区的。
发送缓冲区: 发送缓冲区位于发送方主机上,用于临时保存待发送的数据。当应用程序调用发送操作发送数据时,数据首先被复制到发送缓冲区。发送缓冲区通常由操作系统内核管理,并根据网络条件适时发送数据。
TCP发送缓冲区的主要作用是:
提供一个临时存储区,用于将应用程序产生的大量数据暂时保存,以便分段发送。
将数据进行分段并添加TCP头部等信息,使其符合TCP协议的要求。
通过流量控制和拥塞控制机制,动态地调整发送速率,以适应网络状况
接收缓冲区: 接收缓冲区位于接收方主机上,用于暂时存放接收到的数据。当TCP在网络上接收到数据时,数据被复制到接收缓冲区。接收缓冲区通常由操作系统内核管理,并提供给应用程序读取。
TCP接收缓冲区的主要作用是:
接收和存储网络传输过来的数据,以便应用程序从中读取。 对接收的数据进行重组,将分散的数据片段按序拼接成完整的消息或文件。
根据TCP协议规定,对接收到的数据进行校验和错误检测,以确保数据的完整性和正确性
TCP维护的缓冲区主要分为发送缓冲区和接收缓冲区,那么发送方和接收方不仅仅只限于发送和接受了!这样再次说明了TCP是可以支持全双工的。
接收端处理数据的速度是有限的。如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包问题,继而引起丢包重传等等一系列连锁反应。那么发送端既不能发送太快,也不能发送太慢,发送端发送数据的速度有什么来决定呢?目前我们就可以理解为是由接收端的窗口大小决定的,而接收端窗口大小不就标示的接收端的接收缓冲区所剩余空间的大小吗!!!
接收端如何把窗口大小告诉发送端呢? 回忆我们的TCP首部中, 有一个16位窗口字段, 就是存放了窗口大小信息。那么问题来了, 16位数字最大表示65535,那么TCP窗口最大就是65535字节么?实际上,TCP首部40字节选项中还包含了一个窗口扩大因子M,实际窗口大小是窗口字段的值左移 M 位。
TCP支持根据接收端的处理能力,来决定发送端的发送速度。 这个机制就叫做流量控制(Flow Control)。我们再来看流量控制中的细节:
接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 “窗口大小” 字段, 通过ACK端通知发送端;
窗口大小字段越大,说明网络的吞吐量越高;
接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端;发送端接受到这个窗口之后,就会减慢自己的发送速度;
如果接收端缓冲区满了,就会将窗口置为0。这时发送方不再发送数据,,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端。
在进行流量控制的时候,第一次我们加如何得知时方的接受能力呢?如何在第一次传输数据时就得知对方的接受能力呢?
注意,第一次交换数据不等同于第一次交换报文!不要忘记了,在传输数据之前,一定是要先把连接建立好的。也就是有三次握手!在前两次握手时,就可以互相告诉对放自己的窗口大小,也就是自己的接受能力!
通过流量控制维护发送缓冲区和接收缓冲区,TCP可以实现可靠的数据传输。发送方将数据经过分段、封装和流量控制等处理后发送到网络中,接收方则负责接收并按序重组数据。这种缓冲区的机制不仅确保了数据的可靠性和有序性,还能适应不同的网络状况,并根据具体情况进行动态调整。
滑动窗口
对每一个发送的数据段,都要给一个ACK确认应答。收到ACK后再发送下一个数据段。这样做有一个比较大的缺点,就是性能较差。尤其是数据往返的时间较长的时候。
那么能不能一次发送多条数据呢,也就是一次发送多个报文呢?最明显的优势:在传统的单个报文传输中,每次传输都需要等待一个ACK响应的时间来获取确认,而在一次发送多个报文的情况下,ACK确认可以一次性返回,减少了等待时间,提高了数据传输的效率(其实是将多个段的等待时间重叠在一起了)
答案是可以的。但是怎么控制一次到底能够发多少个报文过去?发过去的报文都有哪几个得到了响应? 滑动窗口就可以帮助我们很好的解决这些问题!
TCP中的滑动窗口是一种动态调整的机制,用于控制发送方将数据发送到接收方的速率,以确保网络上的稳定和可靠的数据传输。滑动窗口的基本思想是,接收方告诉发送方它有多少可用的缓冲区空间,发送方可以根据这个信息来确定发送多少数据。发现这与流量控制很相似!流量控制中引入了滑动窗口机制,也可以理解为流量控制就是基于滑动窗口来实现的。但是我们需要区分的是流量控制体现更多的是数据传输的可靠性,滑动窗口主要是传输数据速率的提升
TCP中滑动窗口理解的一些关键要点:
接收方窗口大小(Receiver WindowSize):接收方在TCP报文中通过窗口大小字段告诉发送方它有多少可用的缓冲区空间。这个窗口大小是动态调整的,可以根据接收方的缓冲区情况和网络拥塞情况而变化。
发送方窗口大小(Sender WindowSize):发送方也会维护一个窗口大小,表示它可以发送多少数据而不需要等待确认。发送方的窗口大小通常受到接收方窗口大小和网络拥塞的影响。
滑动窗口的操作:发送方发送数据,并等待接收方的确认。一旦接收方成功接收并确认数据,发送方的窗口会向前滑动,允许发送更多数据。这就是为什么它被称为"滑动"窗口。
流量控制和可靠性:滑动窗口机制有助于实现流量控制,防止发送方过多地发送数据,从而导致网络拥塞。它还增强了可靠性,因为接收方可以告知发送方哪些数据已经成功接收,哪些需要重新传输。