网络协议-传输层(TCP&UDP)

aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X2pwZy8yQTh0WGljQ0c4eWxQcXVSQ3ZVcFd6azVmMXpaSlpVcVJPcURqb3k4dzVSR1VrdTBMS3A0cWU3TUkyQ2xIZ2wyeXlON1JsUnljM3QwdE9LNUVFaWMyaWJrQS82NDA.png
传输层最重要的两个协议就是TCP协议UDP协议,面试中经常会一起问道

  • TCP协议和UDP协议的区别

  • UDP

    • UDP数据格式

      首先如果一个完整的UDP包到达目标机器的时候首先会验证MAC地址如果和自己的一直则拨开MAC层去除IP层封装的数据,然后在对比IP地址和自己的是否一致,发现一致后然后再拿到UDP包(目标机器通过IP层封装的协议类型来得知用的是哪个协议),现在数据是拿到了,但是数据要给谁呢,目标机器装了很多应用具体给那个应用呢,这个时候就需要端口号, 每个应用会监听一个端口号,所以按理说TCP和UDP的包头中都有端口号,所以UDP数据结构如下(相较于TCP还是简单很多的) Image”.png 其中源端口本机端口号,应为数据发过去之后肯定是要回的所以需要将自己的端口号也要放到里面,目标端口号就是目标机器的端口号。

      这里UDP长度值得是首部的长度加上数据的长度,UDP首部的大小是8字节(TCP会占用20字节)

      • UDP检验和

        作用主要是差错检测,但是如果检验出错则直接丢包没有回复重传的功能。如果是分片上传但凡一片检验出错整个包都会被丢弃,所以一般使用UDP传输建议UDP数据报大小小于1500(也就是尽量避免分片上传)

        检验和的计算内容是:伪首部+首部+数据
        伪首部:固定的虚拟首部只用作计算检验和不会放到UDP数据报中,大小是12字节,其中含有源IP地址、目标IP地址、协议类型、UDP长度、还有一个是保留位固定是0。(计算检验和需要加伪首部是TCP/IP协议规定的目的是增强检验功能)
        如下图: image.png

      • 端口

        占用2字节,也就是端口号的取值范围是0~65535
        上文有大概说到过目标端口,其实就是用来辨别接收到的信息到底是传递给谁的,就比如我们自己的服务器,可以开很多个端口,然后我们项目不止一个想用一个服务器,这个时候就可以开多个端口,然后每个端口上运行一个服务器软件(tomcat),然后将我们的后端代码部署到相应的服务软件就行了,这样不同的端口号就可以返回正确的数据了。

        但是同时还有一个源端口号,也就是我们的客户端端口号,客户端的端口都是临时随机开启的端口,不想服务器端口号它需要一直开启一直监听。客户端数据通信完毕之后这个临时端口号就会关闭。

        下面是常见协议的默认协议端口(当然也是可以改的,修改tomcat配置文件) image.png

    • UDP特点
      1. 需要资源少,UDP的头部只占用的8字节,应为UDP的思维就很简单,你让我发数据我就发送,我不会管交通是否拥挤,我直接出发,同时UDP也不会管丢包的问题,丢了就丢了,不会再重传。少了那么多的限制条件,自然需要的资源也就不多
      2. 不需要建立链接,发送消息之前他不需要知道目标服务器的情况,也就是目标服务器及时关闭了无法通信, 他也不管只要接到发送的消息就直接发送出去,找不到目标服务器就丢包
      3. 处理速度快、时延低、可以容忍少数丢包。即便是网络拥塞的时候也是一如既往好不退缩
  • TCP

    • TCP数据结构

      Image From 07_传输层.png 从这个图中就可以看出TCP的头部要比UDP的复杂的多,UDP的首部只有8字节,而TCP的首部至少20字节

      • 源端口号和目标端口号

        和UDP一样,这里就不多说

      • 序号

        TCP是面向字节流传输的,所以传输数据每个自己都会对应一个标号,而这里传输的序号就是传给对方的数据的第一个字节的编号,需要注意的是这个序号不一定就是这个数据的第一个字节的编号,数据是要根据对方窗口(窗口后文会讲到)大小传输的,有可能出现数据很大窗口很小,所以此时的数据是需要分段传输的,所以这个需要就是每一段的第一个字节的编号

      • 确认号

        在建立连接后,确认号代表:期望对方下一次传过来的TCP数据部分的第一个字节的编号,比如上文举的例子,让窗口大小小于数据大小的时候数据就要分段传输,如果第一段接收完毕,此时接收方释放缓存然后再会给发送方说接收到了,可以接受下一段数据,下一段数据的编号告诉发送方,如下图所示: 未命名文件.png TCP04_序号确认号01_相对.png 注意:这里说明一下正常的话应该不会是每次都会有的应答而是好几次之后有一次应答,图二主要是为了便于学习

      • 数据偏移

        该值乘以4就是首部长度,或许这里会有人有疑问就是会发现TCP首部中没有存储TCP数据包的总长度,UDP首部中是有的,其实这个总长度是完全可以通过网络层的数据和TCP首部算出来的,拿到网络层的数据总长度减去网络层的首部长度其实就是TCP数据包的总长度了,然后再减去首部长度不就是TCP数据包的数据长度吗,所以这里可以认为其实UDP首部中的总长度字节有点冗余,目的其实就是想字节对齐(猜测)

      • 标志位(Flags)
        • URG
          当URG=1时,紧急指针字段才有效。表明当前报文段中有紧急数据,应优先尽快传送
        • ACK 当ACK=1时,确认号字段才有效
        • PSH
          与URG相对,此作用于接收端,令接收端首先接受此数据
        • RST 当TCP连接出现严重错误时,RST=1,说明需要释放连接
        • SYN
          只有建立请求和确认请求的两个数据包中,SYN=1、ACK=0,即TCP三次握手的前两次握手,若对方同意建立连接,则回复SYN=1、ACK=1,这也比较好理解应为只有ACK=1确认号才有用此时才能冲确定号中得知需要发送的序号是多少才会发送数据,其他情况确认号是失效的所以就不会发送数据
        • FIN
          当FIN=1时,表明数据已经发送完毕,要求释放连接
      • 窗口

        这个字段有流量控制功能,用以告知对方下一次允许发送的数据大小(字节为单位),机器读取了数据对应的缓存再被释放然后继续读取数据

      • 检验和

        这个也是和UDP是一样的包含计算方法也是一样的

      • 紧急指针

        标记当前数据需要优先处理,当然也是要根据标志位来确定紧急指针是否有效

    • 可靠传输-停止等待ARQ协议

      就是会有个超时时间,如果发送的消息在超时时间内一直等待不到接收端返回的消息则视为这个消息丢了。则此时需要重新传刚才传递的消息,这里会出现三种情况如下图: Image From 07_传输层.png Image From 07_传输层.png 正常情况好理解此时就是确认收到了才能传递下一条数据,异常数据如上图所示,但是这里还有个问题是这个信道每次就只能传递一条数据信道利用率太低,于是就出现了连续ARQ协议+滑动窗口协议

    • 可靠传输-连续ARQ协议+滑动窗口协议

      停止等到协议每次只能发送一条消息,而该协议每次能根据窗口大小发送好几条消息。发送方和接收方各自维持着发送窗口和接受窗口,发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置。接收方一般采用累计确认方式,即接收方不必对收到的分组逐个发送确认,而是可以在收到几个分组后,对按序到达的最后一个分组发送确认,这样就表示:到这个分组位置的所有分组都已经正确收到了。如下图所示: 停止等待协议
      这样就解决了信道利用率的问题如下图所示: Image From 07_传输层.png 但是在TCP通信过程中,如果发送序列中间某个数据包丢失(比如1、2、3、4、5中的3丢失了) 则最后确认的是2所以3、4、5都会重传,这也降低了TCP的性能所以又发展出了SACK

    • 可靠传输-SACK(选择性确认)

      SACK的信息是存放在TCP首部的选项部分,

      • Kind:占1字节。值为5代表这是SACK选项
      • Length:占1字节。表明SACK选项一共占用多少字节
      • Left Edge:占4字节,左边界
      • Right Edge:占4字节,右边界

      如下图所示 Image From 07_传输层.png 这样就能确认是哪一段出现问题

      一对边界信息需要占用8字节,由于TCP首部的选项部分最多40字节,所以SACK选项最多携带4组边界信息

    • 流量控制

      如果对方的缓冲区已经满了,此时还不停的发送数据的话,接收方会直接把数据丢掉,此时就要用到流量控制,具体做法是在TCP链接的阶段就告诉发送方我的缓冲区有多大,也就是我的窗口有多大,约束发送方只能发送不超过窗口大小的数据,每一次接收方接收到了数据之后放到缓冲区中就会将当前窗口的大小回给发送方,当窗口大小是0的时候说明此时接收方已经处理不了数据了这个时候发送方暂时不在像发送方发送数据(这里当接收方可以处理数据了就会给发送方发送一个窗口非0的报文,此时发送方就可以继续发送数据了)

      上述解决方案看似比较完美但是也有一些小问题。
      例如如果窗口非0的报文丢失掉了,发送方如何知道继续发送消息呢?
      解决方案是这样的,在发送方收到了一个窗口为0的报文的时候就会开启一个定时器,到时间就会发送一个报文询问窗口大小的报文,如果还是接收到一个窗口为0的报文则刷新定时器一次类推

    • 拥塞控制

      Image From 07_传输层.png 如上图所示每个链路都是有他的吞吐量的也就是带宽,一旦超过了改链路的带宽就会造成拥塞此时就会丢包,所以很多人以为达到了吞吐量最大值就会一直按照最大值传输,这是理想状态,如上图还没达到最大值的时候其实网络已经开始拥塞了,一旦拥塞丢包就多了,然后一些包就一直在重传最后死锁了。所以需要拥塞控制。但是相较于上文说的流量控制(点到点端到端的约束),拥塞控制则是针对整个网络的控制,就好比是交通法则,每个司机都需要遵守。主要通过下面几种方法来时先拥塞控制:

      • 名词解释
        • MSS(Maximum Segment Size):每个段最大的数据部分大小
          在建立连接时确定,比如TCP数据包就是1500-20(网络首部)-20(TCP首部)=1460
        • cwnd(congestion window):拥塞窗口
        • rwnd(receive window):接收窗口
        • swnd(send window):发送窗口
          swnd = min(cwnd, rwnd)
      • 慢开始(slow start,慢启动)
        慢开始比较好理解其实就是控制cwnd的大小起初控制只能发一个包,一个包发过去发现网络没有堵塞然后此时正价cwnd的大小(成倍增长)如下图所示: Image From 07_传输层.png
      • 拥塞避免(congestion avoidance)
        慢开始到一定的阈值ssthresh(slow start threshold)之后cwnd开始缓慢增长,以防止过早的出现拥塞,然后出现拥塞之后篱笆将阈值ssthresh减半,然后重新开始慢启动如下图 Image From 07_传输层.png
      • 快速重传(fast retransmit)
        快重传算法要求首先接收方收到一个失序的报文段后就立刻发出重复确认,而不要等待自己发送数据时才进行捎带确认。接收方成功的接受了发送方发送来的M1、M2并且分别给发送了ACK,现在接收方没有收到M3,而接收到了M4,显然接收方不能确认M4,因为M4是失序的报文段。如果根据可靠性传输原理接收方什么都不做,但是按照快速重传算法,在收到M4、M5等报文段的时候,不断重复的向发送方发送M2的ACK,如果接收方一连收到三个重复的ACK,那么发送方不必等待重传计时器到期,尽早重传未被确认的报文段。 Image From 07_传输层.png
      • 快速恢复(fast recovery)
        当收到三个重复确认信息则说明此时出现了网络堵塞,然后此时的阈值就减半,与上文上的不同的是这里不在开始慢启动也就是直接将cwnd设置成阈值然后再加法增大
      • 快重传+快回复
        Image From 07_传输层.png
    • TCP链接管理
      • 建立链接-三次握手

        TCP如果想进行数据传输首先就需要建立链接,链接建立不成功是不会进行数据传输的,大致的步骤如下:

        1. A发送SYN=1 seq=x len=0, 也就是发送一个数据包为0的消息
        2. B应答SYN=1,ACK=1 seq=y,ack=x+1 len=0, 同样的发送一个数据包为0的应答
        3. A再应答ACK=1 seq=x+1,ack=y+1

        这三个回合也比较好理解,第一步A要发起建立连接的请求,同时需要告诉B我这里的序号起始号子是多少,当B收到建立连接的请求之后开始回复A可以建立连接同时也会告诉A我这边的序号的起始号子是多少,此时B需要应答A的请求所以ACK=1,同时B也想和A建立链接所以SYN=1,上次发送的需要是x但是传过来的数据是0所以此时B就希望A传输x+1的开始的数据所以ack=x+1,当然应为是应答建立连接的消息所以一样的没有数据包的传输,最后就到了第三步,当A收到了B的应答之后再给B一个应答,但是此时不再是建立链接的请求所以只有ACK,应为上一次B发送过来的信息需要是y然后len=0所以A就希望下一次的数据需要是冲y+1开始所以ack=y+1,然后上一次的B希望我发送x+1开始的数据那么我的seq=x+1,至此三次握手也就解读完了

        这里也存在着几个问题,为什么是三次握手,假如是两次握手也就是A收到了B的应答之后就开始建立链接然后开始发送数据,但是有一种情况是如果A的建立链接请求长时间未达到B,此时过来超时时间而然后开始重传建立连接的请求这一次就很顺利然后传完数据之后开始释放链接,但此时A发送的第一次请求链接到达了B,B又不知道这个请求链接是否是有效的所以就又发送给A同意建立链接,所以此时又建立起了链接,然后A有不发消息所以不会应答B也不会给B发送数据,也就造成了A并不想建立连接而B以为A想建立链接单相思,所以此时就浪费了资源,当然作为B的程序设计人对于这种长时间不发包的客户端可以主动关闭释放资源,但是这样一样会占用一段时间,当然还有人问为啥不是4次握手呢,其实可以4次握手甚至可以百次握手都行,但是都不能保证可靠传输,所以一般只要双方的消息有来有回就可以了。

        三次握手除了建立链接还会告诉双方自己的窗口大小、MSS、以及序号起始位这个是比较重要的,假如没有需要起始位,例如A连上B之后,发送了1、2、3三个包,但是发送3的时候,中间丢了或者绕路了,此时会重新发送后来A掉线了,然后再重新连上B此时只想发送1、2,但是上次3绕路回来了此时B就会认为是A发送过来的也就出现了数据错乱的问题。因此没接链接都需要一个不同的序号,这个需要是随着时间变化的。

        双方建立了信任链接之后就会维护一个状态机,状态变化时序图如下: Image From 第11讲 TCP协议(上):因性恶而复杂,先恶后善反轻松.png
        一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN状态。然后客户端主动发起连接 SYN,之后处于 SYN-SENT 状态。服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态。客户端收到服务端发送的SYN 和 ACK 之后,发送 ACK 的 ACK, 之后处于 ESTABLISHED 状态,因为它一发一收成功了。服务端收到 ACK 的 ACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。

      • 释放链接-四次挥手

        释放链接是通信双方都可以先发起的,但是都需要通过四次挥手之后才能断开链接,四次挥手和三次握手差不多都是在维护一个状态如下图: Image From 第11讲 TCP协议(上):因性恶而复杂,先恶后善反轻松.png 没有发送断开链接的请求之前肯定都是在链接状态下的。

        • 第一次挥手:当第一次发送断开链接的请求(FIN=1的报文)的时候发送方进入到FIN-WAIT-1的状态,此时发送方就等待接收方的回复

        • 第二次挥手:当接收方收到断开链接的请求的时候自己会回应一个ACK报文给对方,此时则进入到CLOSE-WAIT状态。在此状态下,需要考虑自己是否还有数据要发送给对方,如果没有,发送FIN报文给对方(需要注意的是如果此时接收方还有消息需要传给发送方则可以继续发送消息不发送FIN报文给发送方)发送方收到ACK报文之后就会进入到FIN-WAIT-2状态,(如果此时接收到了FIN、ACK则不需要第三次挥手直接进入到第四次)

        • 第三次挥手:如果接收方收到了FIN报文并回复给了发送方,然后此时刚好自己也没有其他数据需要发送给发送方,那么此时的接后方就发送一个FIN=1,ACK=1的报文,然后进入到LAST-ACK状态,也就是最后等待发送方在给一个ACK报文然后就可以关闭了

        • 第四次挥手:发送方收到FIN=1,ACK=1的报文之后立马回复给接收方,然后自己进入到 TIME-WAIT状态等待一定的时间(2MSL)后关闭(可以防止本次连接中产生的数据包误传到下一次连接中(因为本次连接中的数据包都会在2MSL时间内消失了))

          MSL是Maximum Segment Lifetime, 报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为TCP 报文基于是 IP 协议的,而 IP 头中有一个 TTL域,是IP 数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减1,当此值为0,则数据报将被丢弃,同时发送 ICMP 报文通知源主机。协议规定 MSL为2分钟,实际应用中常用的是 30秒,1分钟和2分钟等。

          主要问题

          • 为什么是四次挥手
            首先链接建立之后双方都可以发送消息的,之后双方都没有数据需要发送给对方的时候才能断开链接,那么为什么是四次呢,简单的说接收方在收到发起方的FIN请求后不能立刻终止连接,它需要确认自己是否还有需要回复的内容,待到回复完毕后,才能够将连接终止,而为了不让发起方重复发送请求,ACK是需要立刻回复的。
          • 为什么需要等待2MSL
            如果发送方发送ACK后马上释放了,然后又因为网络原因,接收方没有收到发送方的ACK,接收方就会重发FIN,发送方没有任何响应,服务器那边会干等,甚至多次重发FIN,浪费资源,还一种情况是发送方有个新的应用程序刚好分配了同一个端口号,新的应用程序收到FIN后马上开始执行断开连接的操作,本来 它可能是想跟接受方建立连接的
          • 超过了 2MSL 的时间,依然没有收到接收方发的FIN的ACK怎么办
            按照TCP 的原理,B 当然还会重发 FIN, 这个时候 A 再收到这个包之后,A 就表示,我己经在这里等了这么长时间了,己经仁至义尽了,之后的我就都不认了,于是是就直接发送 RST, B就知道 A 早就跑了。
      • TCP状态机

        Image From 第11讲 TCP协议(上):因性恶而复杂,先恶后善反轻松.png

    • 问题回顾
      • 在连续ARQ协议+滑动窗口协议中有提到过会根据接收方的窗口大小传递数据,比如现在窗口大小可以接受4个包,那么就会等到4个包都接收完毕之后会给发送方一个确认,但是问题来了,如果此时发送方此时只发送两个包,那么什么时候接收方发送确认信息呢?
        答案是接收方会有个等到时间(TCP默认会设置这个时间),等到这个等到时间到了还没有收到确认信息的时候,那么就会给发送方发送确认信息
      • 上文讲到可靠传输和流量控制的时候说到了数据如果过大在传输层就会拆分成很多段,而不是等到网络层拆分,这是为什么?
        答案是应为网络层没有可靠传输的机制,比如一个6000字节的数据包被拆分成了4个包分片上传,那么一旦有一个包出现问题丢失了,那么整个数据都需要重新分片上传,假如吧分割数据的操作放在传输层,同样的被拆分成了4个段1、2、3、4,假如3丢失了那么接收方会告诉发送方3没了再把3重新发送一下,这样就提高了重传的性能
    • TCP & UDP 区别对比

      image.png

Guess you like

Origin juejin.im/post/7049659222987898911