• 网络原理TCP/UDP


    TCP

    TCP全称为"传输控制协议(Transmission Control Protocol)。人如其名,要对数据的传输进行一个详细的控制。

    TCP协议段格式

    在这里插入图片描述

    • ack:应答
    • syn:申请建立连接
    • FIN:申请断开连接
    • “校验和”:验证数据的完整性

    传输的数据控制(关注可靠与效率),可靠与效率成反比,越可靠效率越低。
    TCP是综合考虑了两者,取的一个均衡:不是保证绝对意义的可靠,也不是绝对意义的效率最高

    可靠机制

    确认应答机制

    在这里插入图片描述

    • 数据会进行编号,并使用32位序号保存。
    • 确认应答,ack标志位置位1,会使用32位确认序号保存。
    • 下一个是多少? ——下一个是1001
      接收到的数据报,连续序号的最大值+1
      告诉对方下一次从哪个位置发。——数据(1001~2000)
    超时重传机制

    主机A发送数据给B之后,可能因为网络拥堵等原因,数据无法到达主机B;
    如果主机A在一个特定时间间隔内没有收到B发来的确认应答,就会进行重发;

    在这里插入图片描述
    没有接收到ack应答包,可能是由于:(1)发送数据丢包。(2)ack应答丢包
    解决办法:发送数据包重新发送。

    超时时间如何确定,隔多长时间进行重传呢?

    跟网络环境有关,是动态变化的。

    • Linux中(BSD Unix和Windows也是如此),超时以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍.
    • 如果重发—次之后,仍然得不到应答,等待2*500ms后再进行重传.
    • 如果仍然得不到应答,等待4*500ms进行重传.依次类推,以指数形式递增.累计到一定的重传次数, TCP认为网络或者对端主机出现异常,强制关闭连接.
    连接管理机制
    建立连接——三次握手

    在这里插入图片描述
    建立连接流程如下:

    1. 客户端发送syn=>申请建立客户端到服务器的连接。
    2. 服务端返回syn+ack => 申请建立服务端到客户端的连接。ack是对第一个数据报的应答,syn和ack可以一起发送(一个数据报,两个标志位置为1),也可以分开发送。
    3. 客户端返回ack => 对第二个数据报syn的应答。
    断开连接——四次挥手

    在这里插入图片描述
    断开连接流程如下:

    1. 客户端发送FIN到服务器:申请关闭客户端到服务器的连接。
    2. 服务端返回ACK :服务器状态置为 close_wait
    3. 服务器发送FIN到客户端:申请关闭服务器到客户端的连接 => 客户端收到申请关闭连接的请求,状态置为TIME_WAIT
    4. 客户端返回ACK => 服务端状态置为closed(已关闭)
    常见问题

    1. 双方连接状态为什么最后才是closed(已关闭)?

    :双方都要保证可靠的关闭连接。

    2. 第2,3个数据报为什么没有合并?

    :第2个数据报,是系统内核返回的(不用程序写代码来发送)
    第3个数据报,是程序调用close方法发送的服务端在关闭连接前,可能需要做一些其他工作。

    是否可以合并:先放在缓冲区(可能是立即发,也可能不是)=>对应的,第三个数据报也是发送到缓冲区。
    此时,如果第二个数据报还在缓冲区,就可能合并发送。

    3. 服务端出现大量的close_wait,原因?

    :服务端没有执行 close方法,因为执行close方法才会发送第3个数据报。

    4.客户端接收第3个数据报,状态为什么置为TIME_WAIT,而不是CLOSED?

    :客户端接收到第3个数据报,不能马上置为CLOSED。第4个数据报,可能出现丢包(服务端无法断开连接)服务端就会根据超时重传机制,重发第3个数据报此时客户端如果是CLOSED,就没法接收了

    5.客户端接收第3个数据报,状态时TIME_WAIT,需要等多久?
    :2MSL。1MSL是单个报文传输的最大时间。

    效率机制

    滑动窗口

    作用:以并行的方式发送数据报,减少等待时间,提高效率。
    在这里插入图片描述
    窗口大小如何确定?

    窗口大小:无需等待确认应答,而可以继续发送的数据报最大值。
    滑动窗口大小滑动窗口大小= min(流量窗口大小,拥塞窗口大小)

    窗口滑动是如何滑动的?
    在这里插入图片描述
    发生丢包的情况:

    1. ack丢包:没影响,后续的ack也能表示序号前的全部接收
    2. 发送的数据报丢包:接收的ack下一个序号,是接收端,接收到的连续序号最大值+1﹐(如果中间有部分没接收到,就相当于不连续)
    3. 快重传:连续3次接收到下一个是x,就表示从x开始的数据报丢包,需要重传
    流量控制机制

    背景:发送端发送速度如果快于接收端,程度读取速度,就可能导致接收缓冲区被打满,进而引起一系统丢包,重传再次丢包的问题。

    • 接收端接收能力有限,主动的告诉发送端,自己的接收能力。
    • 接收端:接收缓冲区,剩余空间大小=>返回的ack应答包,还会使用“窗口大小”字段来设置这个值。
    拥塞控制

    背景:网络状态不明的情况下,贸然发送大量的数据报,就可能产生网络拥塞。类似发快递,不清楚交通状态,就贸然发送大批量的货物,可能导致交通阻塞。

    • TCP引入慢启动机制,先发少量的数据,探探路(根据“拥塞窗口”探路),摸清当前的网络拥堵状态,再决定按照多大的速度传输数据;
    • 拥塞窗口
    • 发送开始的时候,定义拥塞窗口大小为1;
    • 每次收到一个ACK应答,拥塞窗口加1;
    • 每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小做比较,取较小的值作为实际发送的窗
    • 拥塞窗口如何变化
      在这里插入图片描述
    • 当TCP开始启动的时候,慢启动阈值等于窗口最大值;
    • 在每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置回1;
    延迟应答

    背景:接收端返回流量窗口代表接收缓冲区可用空间大小,如果立即返回,就不划算(立即返回的流量窗口大小就会比较小)
    接收端返回的流量窗口(ack应答包有窗口大小字段),不是立即返回,而是等待一定时间,这样返回的流量窗口大小就可能更大

    • 流量窗口大小,是滑动窗口大小的决定因素之一。
    • 滑动窗口大小是网络吞吐量的决定因素之一。

    所以延迟应答是效率机制——延迟一定时间应答,效率就更高。

    捎带应答

    背景:不管是客户端还是服务端,每一端,即可以是发送端,也可以是接收端。不管客户端还是服务端,接收到数据后,返回的ack应答包(作为接收端),可以和发送的数据报(作为发送端)合并在一起(捎带的方式)发送给对方

    粘包问题——应用层的数据包

    应用层需要约定统一的协议:明确包和包之间的边界。没有明确边界,就会出现粘包问题。

    站在传输层角度,TCP是一个一个报文过来的,按序号排好在缓冲区。而应用层角度,看到的只是一串连续字节数据。

    传输层:如果基于tcp协议(面向字节流,没有关闭流,可以一直收发数据)
    解决方案

    1. 固定大小的包:读、写都按照固定大小来发送/接收。
    2. 可变长度的包:发送时包含长度的信息,接收到就按照这个信息读取相应大小的数据。也可以自定义,如使用分隔符来分割包。

    UDP协议:因为是面向数据报的,不存在粘包问题。

    TCP与UDP区别

    UDPTCP
    无连接有连接(三次握手、四次挥手)
    不可靠可靠
    面向数据报面向字节流
    缓冲区:只有接收缓冲区有接收缓冲区和发送缓冲区
    大小受限大小不限
    适用场景:实时性要求高,允许少量丢包。(早期QQ)适用场景:可靠性比较高的地方(文件传输)
    数据报 vs字节流

    面向数据报:

    应用层交给UDP多长的报文,UDP原样发送,既不会拆分,也不会合并;
    用UDP传输100个字节的数据:

    如果发送端调用一次sendto,发送100个字节,那么接收端也必须调用对应的一次recvfrom,接收100个字节;而不能循环调用10次recvfrom,每次接收10个字节。
    面向字节流:

    创建一个TCP的socket,同时在内核中创建一个发送缓冲区和一个接收缓冲区;

    • 调用write时,数据会先写入发送缓冲区中;
    • 如果发送的字节数太长,会被拆分成多个TCP的数据包发出;
    • 如果发送的字节数太短,就会先在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适的时机发送出去;
    • 接收数据的时候,数据也是从网卡驱动程序到达内核的接收缓冲区;然后应用程序可以调用read从接收缓冲区拿数据;
    • 另一方面, TCP的一个连接,既有发送缓冲区,也有接收缓冲区,那么对于这一个连接,既可以读数据,也可以写数据.这个概念叫做全双工

    由于缓冲区的存在,TCP程序的读和写不需要一一匹配,例如:

    • 写100个字节数据时,可以调用一次write写100个字节,也可以调用100次write,每次写一个字节
    • 读100个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以一次read 100个字节,也可以一次
      read一个字节,重复100次;
    基于UDP,设计一个可靠传输?

    在应用层,自己引入序列号,确认应答机制、超时重传机制等类似TCP的机制,实现可靠传输。

    基于UDP,设计大小不限数据传输?

    在应用层,自己写代码实现 引入序列号,确认应答机制、超时重传机制等类似TCP的机制,实现可靠传输。

  • 相关阅读:
    幼儿安全消防知识教案
    JUC并发编程 wait notify详解
    数据结构:图
    五年之后超过10000订阅者
    Unity VR 开发教程: Oculus 一体机开发 (一) 环境配置(基于 Oculus Integration v46)
    [Linux入门]---yum软件安装及vim编辑器配置
    用Python写一个去文档水印的算法
    Redis 的底层数据结构和IO模型
    [CC2642r1] ble5 stacks 蓝牙协议栈 介绍和理解
    Golang Redis连接池封装
  • 原文地址:https://blog.csdn.net/xy199931/article/details/127700890