精华内容
下载资源
问答
  • 一条tcp连接的数据传输方向
    千次阅读
    2022-01-16 16:11:24

    TCP

    TCP协议的特点

    • 主要解决传输的可靠、有效、无丢失和不重复问题;
    • 主要特点:
      1. TCP是面向连接的传输层协议;
      2. 每条TCP连接只能有两个端点,每条TCP连接只能是点对点的;
      3. TCP提供可靠的交付服务,保证传送的数据无差错、不丢失、不重复且有序;
      4. TCP提供全双工通信,允许通信双方的应用进程在任何时候都可以发送数据,为此TCP连接的两端都设有发送缓存和接收缓存,用来临时存放双方通信的数据;
        • 发送缓存用来暂时存放:
          1. 发送应用程序传送给发送方TCP准备发送的数据;
          2. TCP已发送但尚未收到确认的数据;
        • 接收缓存用来暂时存放:
          1. 按序到达但尚未被接收应用程序读取的数据;
          2. 不按序到达的数据;
      5. TCP是面向字节流的,虽然应用程序和TCP的交互是一次一个数据块,但TCP把应用程序交下来的数据仅视为一连串的无结构的字节流;

    TCP报文段

    • TCP报文段既可以用来运载数据,又可以用来建立连接、释放连接和应答;

    TCP报文段

    • 各字段意义:
      1. 源端口和目的端口:各占2B,端口是传输层与应用层的服务接口,传输层的复用和分用功能都要通过端口实现;
      2. 序号:占4B,范围0~232-1,共232个序号,序号字段的值是指本报文段所发送的数据的第一个字节的序号;
      3. 确认号:占4B,是期望收到对方下一个报文段的第一个数据字节的序号。若确认号为N,则表明到序号N-1为止的所有数据都已正确收到;
      4. 数据偏移:即首部长度,占4位,表示首部长度,指出TCP报文段的数据起始处距离TCP报文段的起始处有多远;
      5. 保留:占6位,保留为今后使用,目前应置为0;
      6. 紧急位URG:URG = 1表明紧急指针字段有效,告诉系统有紧急数据应尽快传送;
      7. 确认位ACK:ACK = 1时确认号字段有效,在连接建立后所有传送的报文段都必须把ACK置1;
      8. 推送位PSH:接收方TCP收到PSH = 1的报文段,尽快交付给接收应用程序,而不再等到整个缓存都填满后再向上交付;
      9. 复位位RST:RST = 1时,表明TCP连接中出现严重差错,必须释放连接,然后重新建立传输连接;
      10. 同步位SYN:同步SYN = 1表示这是一个连接请求或连接接受报文;
      11. 终止位FIN:用来释放一个连接,FIN = 1时,表明此报文段的发送方的数据已发送完毕,并要求释放传输连接;
      12. 窗口:占2B,范围0~216-1,指出现在允许对方发送的数据量,接收方的数据缓存空间是有限的,因此用窗口值作为接收方让发送方设置其发送窗口的依据;
      13. 校验和:占2B;
      14. 紧急指针:占2B,紧急指针仅在URG = 1时才有意义,指出本报文段中紧急数据共有多少字节,紧急数据在报文段数据的最前面;
      15. 选项:长度可变;
      16. 填充:为了使整个首部长度时4B的整数倍;

    TCP连接管理

    • TCP连接的三个阶段:连接建立、数据传送、连接释放;
    • 需解决问题:
      1. 钥匙每一方能够确知对方的存在;
      2. 要允许双方协商一些参数,如最大窗口值,、是否使用窗口扩大选项、时间戳选项及服务质量等;
      3. 能够对运输实体资源,如缓存大小、连接表中的项目进行分配;
    • 连接的建立采用客户/服务器方式,主动发起连接建立的应用进程称为客户,而被动等待连接建立的应用进程称为服务器;
    TCP连接的建立

    TCP三次握手

    • 连接建立前,服务器进程处于LISTEN(收听)状态,等待客户的连接请求;
    • 连接建立步骤:
      • 第一步:
        • 客户机的TCP首先向服务器的TCP发送连接请求报文;
        • SYN = 1, seq = x;
      • 第二步:
        • 服务器的TCP收到连接请求报文段后,如同意建立连接,则向客户机发回确认,并为该TCP连接分配缓存和变量;
        • SYN = 1, ACK = 1, seq = y, ack = x+1;
      • 第三步:
        • 当客户机收到确认报文段后,还要向服务器给出确认,并为该TCP连接分配缓存和变量;
        • ACK = 1, seq = x+1, ack = y+1;
    • 提供全双工通信,因此通信双方的应用进程在任何时候都能发送数据;

    注意

    • 服务端的资源是在完成第二次握手时分配的,客户端资源是在完成第三次握手时分配的;
    • 使得服务器易于收到SYN洪范攻击;
    TCP连接的释放

    释放TCP连接

    • 连接释放步骤:
      • 第一步:
        • 客户机打算关闭连接时,向其TCP发送连接释放报文段,并停止发送数据,主动关闭TCP连接;
        • TCP是全双工的,可以想象为一条TCP连接上有两条数据通路,发送FIN的一端不能在发送数据,即关闭了其中一条数据通路,但对方还可以发送数据;
        • FIN = 1, seq = u;
      • 第二步:
        • 服务器收到连接释放报文段后即发出确认;
        • 此时,从客户机到服务器这个方向的连接就释放了,TCP连接处于半关闭状态,但服务器若发送数据,客户机仍要接收,即从服务器到客户机这个方向的连接并未关闭;
        • ACK = 1, seq = v, ack = u+1;
      • 第三步:
        • 若服务器已经没有要向客户机发送的数据,就通知TCP释放连接;
        • FIN = 1, ACK = 1, seq = w, ack = u+1;
      • 第四步:
        • 客户机收到连接释放报文段后,必须发出确认;
        • ACK = 1, seq = u+1, ack = w+1;

    TCP可靠传输

    • TCP的任务是在IP层不可靠的、尽力而为服务的基础上建立一种可靠数据传输服务;
    • TCP提供的可靠数据传输服务保证接收方进程从缓存区读出的字节流与发送方发出的字节流完全一样;
    • TCP使用了校验、序号、确认和重传等机制达到可靠传输目的;
    序号
    • TCP首部的序号字段用来保证数据能有效提交给应用层,TCP把数据视为一个无结构但有序的字节流;
    • 序号建立在传送的字节流之上,而不建立在报文段之上;
    • TCP连接传送的数据流中的每个字节都编上一个序号,序号字段的值是指本报文段所发送的数据的第一个字节的序号;
    确认
    • TCP首部的确认号是期望收到对方的下一个报文段的数据的第一个字节的序号;
    • 发送方缓存区会继续存储那些已发送但未收到确认的报文段,以便在需要时重传;
    • TCP默认使用累计确认,即TCP只确认数据流中至第一个丢失字节为止的字节;
    重传
    • 有两种事件会导致TCP对报文段进行重传:超时和冗余ACK;
    超时
    • TCP每发送一个报文段,就对这个报文段设置一次计时器,计时器设置的重传时间到期但还未收到确认时,就要重传这一报文段;
    • 为计算超时计时器的重传时间,TCP采用一种自适应算法,它记录一个报文段发出的时间,以及收到相应确认的时间,这两时间之差称为报文段的往返时间RTT,TCP保留了RTT的一个加权平均往返时间RTTs,他会随新测量RTT样本值的变化而变化;
    冗余ACK(冗余确认)
    • 超时触发重传存在的一个问题时超时周期往往太长;
    • 发送方通常可在超时事件发生之前通过注意所谓的冗余ACK来较好地检测丢包情况;
    • 冗余ACK就是再次确认某个报文段地ACK,而发送方先前已收到过该报文段地确认;

    TCP流量控制

    • TCP提供流量控制服务来消除发送方发送速率太快使接收方缓存区溢出地可能性,因此可以说流量控制是一个速度匹配服务,匹配发送方的发送速率与接收方的读取速率;
    • TCP提供一种基于滑动窗口协议的流量控制机制;
      • 在通信过程中,接收方根据自己接收缓存的大小,动态地调整发送方地发送窗口大小,这称为接收窗口rwnd;
      • 发送方根据其对当前网络拥塞程度的估计而确定的窗口值,这称为拥塞窗口cwnd,其大小与网络带宽和时延密切相关;
    • 一个例子

    利用可变窗口进行流量控制

    • 传输层和数据链路层的流量控制的区别:
      • 传输层定义端到端用户之间的流量控制;
      • 数据链路层定理两个中间的相邻结点的流量控制;
      • 传输层的窗口协议的窗口大小可以动态变化;
      • 数据链路层的窗口协议的窗口大小不能动态变化;

    TCP拥塞控制

    • 拥塞控制是指防止过多的数据注入网络,保证网络中的路由器或链路不致过载;
    • 拥塞控制与流量控制的区别:
      • 拥塞控制是让网络能够承受现有的网络负荷,是一个全局性的过程,涉及所有的主机、所有的路由器,以及与降低网络传输性能有关的所有因素;
      • 流量控制是指点对点的通信量控制,是个端到端的问题,他所要做的是一直发送端发送数据的速率,以便使接收端来得及接收;
    • 拥塞控制与流量控制的相似:
      • 都通过控制发送方发送数据的速率来达到控制效果;
    • 进行拥塞控制的4种算法:
      • 慢开始;
      • 拥塞避免;
      • 快重传;
      • 快恢复;
    • TCP协议要求发送方维护以下两个窗口:
      1. 接收窗口rwnd:
        • 接收方根据目前接收缓存大小所许诺的最新窗口值,反映接收方的容量;
        • 由接收方根据其放在TCP报文的首部的窗口字段通知发送方;
      2. 拥塞窗口cwnd:
        • 发送方根据自己估算的网络拥塞成都而设置的窗口值,反映网络的当前容量;
        • 只要网络未出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去;
        • 只要网络出现拥塞,拥塞窗口就减小一些,以减少注入网络的分组数;
      • 发送方上限值应取接收窗口rwnd和拥塞窗口cwnd中较小的一个,即:
        • 发送窗口上限值 = min[rwnd, cwnd];
    慢开始和拥塞避免
    慢开始算法
    • 在TCP刚连接好并开始发送TCP报文段时,先令拥塞窗口cwnd = 1,即一个最大报文段长度MSS;
    • 每收到一个对新报文段的确认后,将cwnd加1,即增大一个MSS;
    • 使用慢开始算法后,每经过一个传输轮次RTT,cwnd就会加倍,即cwnd的大小指数式增长;
    • 直到cwnd增大到一个规定的慢开始门限ssthresh阈值,然后改用拥塞避免算法;
    拥塞避免算法
    • 每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,使拥塞窗口cwnd按线性规律缓慢增长;
    • 归纳如下:
      1. swnd < ssthresh,使用慢开始算法;
      2. swnd > ssthresh,停止使用慢开始算法而改用拥塞避免算法;
      3. swnd = ssthresh,即可使用慢开始算法,又可使用拥塞避免算法;
    慢开始和拥塞避免算法的实现过程

    慢开始和拥塞避免的实现过程

    快重传和快恢复
    快重传
    • 当发送方连续收到三个重复的ACK报文时,直接重传对方尚未收到的报文段,而不必等待那个报文段设置的重传计时器超时;
    快恢复
    • 原理:当发送方连续收到三个冗余ACK时,执行“乘法减小”算法,把慢开始门限ssthresh设置为此时发送方swnd的一半;
    快恢复算法的实现过程

    快恢复算法的实现过程

    更多相关内容
  • TCP协议-TCP连接管理

    千次阅读 2021-07-13 16:07:28
    这就是说,通信双方在使用TCP协议进行通信之前,必须先建立TCP连接。在通信结束后,必须释放已经建立的TCP连接。这就好比打电话,通话前要先拨号建立连接,通话结束后要挂机释放连接。 (2)TCP 是点对点(

    一、TCP概述

    TCP协议是 TCP/IP 协议族中一个非常重要的协议。它是一种面向连接、提供可靠服务、面向字节流的传输层通信协议。

    TCP(Transmission Control Protocol,传输控制协议)。

    1.1 TCP协议的特点

    (1)TCP 是面向连接的传输层协议。这就是说,通信双方在使用TCP协议进行通信之前,必须先建立TCP连接。在通信结束后,必须释放已经建立的TCP连接。这就好比打电话,通话前要先拨号建立连接,通话结束后要挂机释放连接。

    (2)TCP 是点对点(一对一)的连接。每一条TCP连接只能有两个通信端点(endpoint)。所以基于广播和多播(通信目标是多个主机地址)的应用程序不能使用TCP连接。

    (3)TCP 提供可靠交付的通信服务。通过TCP连接传递的数据,无差错、不丢失、不重复,并且按序到达。

    (4)TCP 提供全双工通信。TCP允许通信双方在任何时候都能发送和接收数据。TCP连接的两端都设有发送缓存和接收缓存,用来临时存放双向通信的数据。在发送时,应用程序在把数据发送给TCP的发送缓存后,就可以做自己的事,而TCP在合适的时候把数据通过网卡发送出去。在接收时,TCP把收到的数据先放入接收缓存,应用层的应用进程在合适的时候再读取缓存中的数据。

    (5)TCP 是面向字节流的。TCP中的“流”(stream)指的是流入到进程或从进程流出的字节序列。“面向字节流”的含义是:虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把上层应用程序交下来的数据仅仅看成是一连串无结构的字节序列(就像水流一样)。TCP并不知道所传送的字节流的含义。TCP不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系(例如,发送方应用程序交给发送方的TCP共10个数据块,但接收方的TCP可能只用了4个数据块就把收到的字节流交付上层的应用程序)。但是,接收方应用程序收到的字节流必须和发送方应用程序发出的字节流完全相同。因此,接收方的应用程序必须有能力识别接收到的字节流,把它还原成有意义的应用层数据。下图的示意图解释了TCP面向字节流的含义:

    TCP面向字节流的概念

    TCP和UDP在发送报文时所采用的方式完全不同。TCP并不关心上层的应用程序一次把多长的报文发送到TCP的缓存中,而是根据对方给出的窗口值和当前网络拥塞的程度来决定一个报文段应该包含多少个字节(UDP发送的报文段是上层的应用程序给出的)。如果应用程序传送到TCP缓存的数据块太大,TCP就可以把它划分短一些再传送。如果应用进程一次只发来一个字节,TCP也可以等待积累有足够多的字节后再构成报文段发送出去。关于TCP报文段的长度问题,会在下面内容中进行讨论。

    1.2 TCP的连接概念

    每一条TCP连接都有两个端点。TCP连接的端点叫做套接字(socket)。根据RFC 793的定义:端口号拼接到(concatenated with)IP地址即构成了套接字。因此,套接字的表示方法为:

    套接字 socket = (IP地址:端口号)

    例如,若IP地址是 192.168.1.112,而端口号是 80,那么得到的套接字就是(192.168.1.112: 80)。

    每一条TCP连接唯一地被通信两端的两个端点(即两个套接字)所确定。即:

    TCP连接 ::= {socket1, socket2} = {(IP1: port1), (IP2: port2)}

     IP1 和 IP2 分别是两个端点主机的IP地址,而port1 和 port2 分别是两个端点主机中的端口号。TCP连接的两个套接字就是socket1 和 socket2。

    二、TCP报文段的首部(头部)结构

    TCP虽然是面向字节流的,但是TCP传送的数据单元却是报文段。一个TCP报文段分为首部和数据两部分,而TCP的全部功能都体现在它首部中各字段的作用。因此,只有弄清楚TCP首部各字段的作用才能掌握TCP的工作原理。

    这里再讲一下“面向字节流”的含义,是指应用层上的应用程序将数据(这些数据可能是有结构层次的)传递给传输层的TCP时,TCP只把这些数据看做是一连串的字节流,而不会去关心这些数据是什么结构的。

    TCP报文段首部的前20个字节是固定的,后面有4n字节是根据需要而增加的选项(n是整数)。因此TCP的首部的最小长度是20字节。

    下图显示了TCP首部的数据格式。如果不计选项字段,TCP首部的大小通常为20字节。

    每个TCP首部都包含源端和目的端的端口号,大小均为16bit,用于寻找发送端和接收端应用进程。这两个值+IP首部中的源端IP地址和目的端IP地址,就可以唯一确定一条TCP连接。

    TCP报文段的首部格式

     2.1 TCP首部固定部分各字段的含义

    (1)源端口和目的端口:各占2字节,分别写入源端口号和目的端口号。和UDP的首部类似。

    (2)序号:占4字节。序号范围是 [0, 2^32-1],共2^32(即4 294 967 296)个序号。序号增加到(2^32-1)后,下一个序号就又回到0。也就是说,序号使用 mod 2^32 运算。TCP是面向字节流的,在一个TCP连接中传送的字节流中的每一个数据字节都按顺序编号。整个要传送的字节流的起始序号必须在TCP连接建立时设置。首部中序号字段值指的是本报文段所发送的数据的第一个字节的序号

    例如,某个TCP报文段的首部序号字段值是301,而这个报文段携带的数据共有100字节,那么最后一个字节的序号是400。显然,下一个报文段(如果还有的话)的数据序号应当从401开始,即下一个报文段的序号字段值是401。这个字段的名称就叫做“报文段序号”。

    (3)确认号:占4字节。是期望收到对方下一个报文段的第一个数据字节的序号值

    例如,B正确收到了A发送过来的一个报文段,其序号字段值是501,而数据长度是200字节(序号501~700)。这表明B正确收到了A发送的到需要700为止的数据。因此,B期望收到A的下一个数据字节序号是701,于是B在发送给A的确认报文段中把确认号设置为701。请注意,现在的确认号不是501,也不是700,而是701。总之,应当记住:

    若确认号 = N,则表明:到序号 N-1 为止的所有数据都已正确收到。

    由于序号字段有32位长,可对4GB的数据进行编号。在一般情况下,可保证当序号重复使用,旧序号的数据早已通过网络到达终点了。

    (4)数据偏移:占 4 bit。这个字段用于记录TCP报文段的首部长度。由于TCP报文段的首部结构中还有长度不确定的选项字段,因此数据偏移字段是必要的。当应注意的是,“数据偏移”的单位是32位字(即为4字节的字长为计算单位)。由于4位二进制能够表示的最大十进制数是15,因此数据偏移的最大值是60字节,这也是TCP报文段首部的最大长度(即选项字段长度不能超过40字节)。一般情况下,TCP首部的长度为20字节。

    (5)保留:占6位。保留为今后使用,但目前应置为0。

    (6)6个控制位,用来说明本报文段的性质,它们各自的含义如下:

    • 紧急URG:(Urgent Pointer)紧急指针字段。当URG=1时,有效。它告诉系统此报文段有紧急数据,应尽快传送(相对于高级优先的数据),而不要按原来的排队顺序来传送。当 URG置1时,发送应用进程就告诉发送方的TCP有紧急数据要传送。于是发送方TCP就把紧急数据插入到本报文段数据部分的最前面,而在紧急数据后面的数据仍是普通数据。这时要与首部中紧急指针(Urgent Pointer)字段配合使用。

    例如,已经发送了很长的一个程序要在远地的主机上运行。但后来发现了一些问题,需要取消该程序的运行。因此用户从键盘发出中断命令(ctrl + c),如果不使用紧急数据,那么这两个字符将存储在接收TCP的缓存末尾。只有在所有的数据都被处理完毕后这两个字符才被交付给接收方的应用进程,这样做就浪费了许多时间。

    • 确认ACK:(ACKnowledgment) 当 ACK=1时,确认号字段才有效。当ACK=0时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1。
    • 推送PSH:(Push) 当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能够收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。这时,发送方TCP把 PSH 置 1,并立即创建一个报文段发送出去。接收方TCP收到 PSH=1 的报文段后,就尽快地交付给接收方的应用进程,而不再等到整个缓存都填满后再向上交付。

    虽然应用程序可以选择推送操作,但推送操作很少使用。

    • 复位RST:(ReSeT) 当RST=1时,表明TCP连接出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立TCP连接。RST置1,还可用来拒绝一个非法的报文段或拒绝打开一个TCP连接。RST也可称为重建位或重置位。我们将携带RST标志的TCP报文段称为复位报文段
    • 同步SYN:(SYNchronization)  在TCP连接建立时用来同步序号。当 SYN=1,ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应报文段中使用 SYN=1,ACK=1。因此,SYN置为1,就表示这是一个连接请求或连接接受报文段。我们将携带SYN标志的TCP报文段称为同步报文段。关于TCP连接的建立和释放,会在下面的部分进行详细的讨论。
    • 终止FIN:(FINish)  原来关闭一个TCP连接。当 FIN=1 时,表明此报文段的发送方的数据已经发送完毕,通知对方本端要关闭连接了。我们将携带FIN标志的TCP报文段称为结束报文段

    (7)窗口:占2字节。窗口值是 [0, 2^16-1]之间的整数值。窗口字段指的是发送本报文段的一方的接收窗口(Receiver Window, RWND)(而不是自己的发送窗口)。窗口值告诉对方:本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值是接收方目前允许发送方发送的数据量(以字节为单位),作为发送方设置其发送窗口的依据。

    例如,发送了一个TCP报文段,其确认号是701,窗口字段值是1000。这就是告诉对方:从701号算起,我方(即发送此报文段的一方)的接收缓存空间还可接收1000个字节的数据(字节序号是701~1700),你在给我发送数据时,必须考虑到这一点。

    总之,记住:窗口字段明确指出了 现在允许对方发送的数据量。窗口值经常在动态变化着。

    (8)校验和:占2个字节。由发送方填充该字段,校验和字段检验的范围包括首部和数据这两部分。接收端对TCP报文段执行CRC算法以检验TCP报文段在传输过程是否有损坏,校验和检测也是TCP提供可靠传输的一个重要保障。和UDP数据报一样,在计算校验和时,要在TCP数据报的前面加上12字节的伪首部。TCP伪首部的格式和UDP用户数据报的伪首部格式一样,但应把伪首部第4个字段中的17改为6,(TCP的协议号是6),把第5个字段的UDP长度改成TCP长度。接收方收到此报文段后,仍要加上这个伪首部来计算校验和。若使用IPV6,则相应的伪首部也要改变。

    UDP用户数据报的首部和伪首部

    (9)紧急指针:占2个字节。紧急指针字段仅在 URG=1 时才有意义。它指出本报文段中紧急数据的字节数(紧急数据都是放在TCP报文段数据部分的前面,紧急数据结束后才是普通数据)。该字段值和序号字段相加表示最后一个紧急数据的下一个字节的序号值。当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作。值得注意的是,即使窗口值为0时也可发送紧急数据。

    (10)选项:TCP头部最后一个选项(options)字段是可变长的可选信息,最长可达40字节,因此TCP首部最长是60字节。当没有使用“选项”字段时,TCP首部长度是20字节。

    2.2 TCP首部选项字段结构

    TCP首部结构的最后一个选项(Options)字段是一个可变长的可选信息。

    上面我们已经知道,在TCP报文段的首部有一个“数据偏移”字段,占 4 bit位,最大能表示的十进制数为 15,单位为32位字(也就是4字节),因此数据偏移字段的最大值是60字节。该字段的含义是TCP报文段的首部长度。

    因此,TCP首部选项字段的最大长度 = 60字节 - 首部固定大小的20字节 = 40字节

    典型的TCP首部选型字段的结构示意图如下所示:

    TCP首部选项字段的一般结构
    •  kind:占1个字节。该字段是说明选项的类型。有的TCP选项没有后面两个字段,仅包含1字节的kind字段。
    • length:占1个字节。指定该选项的总长度。该长度包括kind字段和length字段占据的2个字节。
    • info:是选项的具体信息。常见的TCP选项有7种,如下图所示:
    7种TCP选项

    (1)kind=0:是选项表结束选项。

    (2)kind=1:是空操作(nop)选项,没有特殊含义,一般用于将TCP选项的总长度填充为4字节的整数倍。

    (3)kind=2:是最大报文段长度(Maximum Segment Size,MSS)选型。MSS是每一个TCP报文段中的数据部分的最大长度。数据部分加上TCP首部才等于整个的TCP报文段,所以MSS并不是整个TCP报文段的最大长度,而是TCP报文段长度减去TCP首部长度。TCP模块通常将MSS设置为(MTU-40)字节(减掉的这40字节包括20字节的TCP首部长度和20字节的IP首部长度)。这样携带TCP报文段的IP数据报的长度就不会超过MTU(假设TCP报文段首部和IP数据报首部都不包含选项字段,当然这是一般情况),从而本机发生IP分片。对于以太网而言,MSS的值是1460(1500-40)字节。

    为什么要规定一个最大报文段长度MSS呢?这并不是考虑接收方的接收缓存可能放不下TCP报文段中的数据,实际上,MSS与接收窗口值没有关系。主要原因是为了提高网络的利用率。因为TCP在传送数据时,是以报文段为单位发送数据的。而待传送的数据是以 MSS 的大小为单位进行分割的,然后加上TCP首部,就组装成一个完整的TCP报文段。进行重发时,也是以 MSS 为单位。

    如果MSS设置太低了,网络的利用率就会降低;如果MSS设置太高了,又会增加网络处理IP分片的开销。因此,设置一个合理的MSS值是很有必要的,MSS的值应尽可能大些,只要在IP层传输时不需要再分片就行,最理想的情况是,最大TCP报文段长度正好是IP数据报中不会被分片处理的最大数据长度。

    在建立TCP连接建立的过程中,双方都把自己能够支持的MSS填入这一字段,以后就按照这个数值传送数据,两个传送方向可以有不同的MSS值。若主机未填写这一项,则MSS的默认值是536字节长度。因此,所有在互联网上的主机都应能接受报文段长度是 536+20(TCP报文段固定首部长度)=556字节。

    <备注1> MTU(Maximum Transmission Unit,最大传输单元) 它属于数据链路层上的概念,表示的是数据链层帧的数据部分的最大长度。

    <备注2> 有一种流行的说法:在TCP连接建立的阶段“双方协商MSS值”,但这是错误的,因为这里并不存在任何的协商,而只是一方把MSS值设定好以后通知另一方而已。

    (4)kind=3:窗口扩大因子选项。该选项是为了扩大窗口。我们知道,TCP首部的窗口字段长度是16位,因此最大的窗口大小为 64K(65 535) 字节。由上图可知,窗口扩大选项占3个字节,其中有一个字节表示移位值M。假设TCP首部中的接收窗口字段值为 N,窗口扩大选项中的移位值为 M,那么TCP报文段的实际接收窗口大小为:N * 2^M,或者说N左移M位。注意,M的取值范围是[0, 14]的整数值。我们可以通过修改 Linux系统中的 /proc/sys/ipv4/tcp_window_scaling 内核变量来启动或关闭窗口扩大因子选项。

    和MSS选项一样,窗口扩大因子选项只能出现在同步报文段中,否则将被忽略。但同步报文段本身不执行窗口扩大操作,即同步报文段首部的接收窗口字段值就是该TCP报文段的实际接收窗口大小。当TCP连接建立好之后,每个数据传输方向的窗口扩大因子就确定下来了。如果连接一方实现了窗口扩大,当它不再需要扩大其窗口时,可发送M = 0的选型,使窗口大小回到16。

    (5)kind=4:选择确认(Selective Acknowledgment, SACK)选项。TCP通信时,如果某个TCP报文段丢失,则TCP模块会重传最后被接收方确认的TCP报文段后续的所有报文段,这样原先已经正确传输的TCP报文段也可能重复发送,从而降低了TCP性能。SACK选项正是为了改善这种情况而产生的,它使TCP模块只重新发送丢失的TCP报文段,而不用发送所有未被确认的TCP报文段。选择确认选项用在TCP连接建立过程中,表示是否支持SACK选项。我么可以通过修改Linux系统的 /proc/sys/net/ipv4/tcp_sack 内核变量来启用或关闭选择确认选项。

    (6)kind=5:是SACK实际工作的选项。该选项的参数告诉发送方,本端已经接收到的不连续的数据块,从而让发送端据此检查并重发丢失的数据块。每个块边沿(end of block)参数包含一个4字节的序号。其中,块左边沿表示不连续块的第一个数据字节的序号,而块右边沿则表示不连续块的最后一个数据字节的下一个字节序号。这样一对参数(块左边沿和块右边沿)之间的数据就是没有收到的。因为一个块信息占8字节,所有TCP首部选型中实际上最多可以包含4个这样的不连续数据块(考虑到选项类型的长度占用的2个字节)。

    (7)kind=8:是时间戳选项,占10个字节。时间戳选项有以下两个功能:

    第一,用来计算通信双方之间的回路时间(Round Trip Time,RTT)。发送方在发送TCP报文段时把当前时间值放入时间戳字段,接收方在确认该报文段时把时间戳字段值复制到时间戳回显应答字段。因此,发送方在收到确认报文后,可以准确地计算出RTT来。

    第二,用于处理TCP序号超过2^32(4,294,967,296)的情况,这又称为防止序号绕回PAWS(Protect Against Wrapped Sequence numbers)。我们知道,TCP报文段的序号只有32位,而每增加2^32个序号后就会重新从0开始编号。当使用高速网络时,在一次TCP连接的数据传送中序号很可能会被重复使用。例如,当使用 1.5Mbit/s的速率发送TCP报文段时,序号重复要6小时以上。但若使用2.5Gbit/s的速率发送数据报时,则不到14秒钟序号就会重复。为了使接收方能够把新的报文段和迟到很久的报文段(序号相同的情况下)区分开,可以在报文段中加上时间戳选项。我们可以通过修改Linux系统的 /proc/sys/net/ipv4/tcp_timestamps 内核变量来启用和关闭时间戳选项。

    示例1,我们使用Wireshark软件查看一个TCP连接的SYN同步报文段信息。

    SYN同步报文段信息

     由上图可以看到,TCP报文段首部各个字段的信息,该报文段的首部长度是40字节,其中包括固定的首部长度20字节+选项字段20字节长度。标志位Flags中,只有 SYN=1,其他标志位均为0。初始序号值为0,确认号为1,接收窗口值为5840,检验和字段为0x5574,紧急指针字段为0。

     我们展开其Options选项的内容,如下图所示:

    SYN同步报文段Options选项信息

     由上图可知,在Options选项中,设置了如下的几种TCP选项:

    • kind=2,最大报文段MSS选项,MSS的值为1460。
    • kind=4,选择确认SACK选项
    • kind=8,时间戳timestamps选项
    • kind=1,无操作NOP选项
    • kind=3,窗口扩大(Window scale)选项,移位值为7,可以扩大2^7=128倍。

    三、TCP的连接管理

    TCP是面向连接的传输层协议。TCP连接的建立和释放是每一次面向连接的通信中必不可少的过程。因此TCP通信过程有3个阶段,即:连接建立、数据传输和连接释放。这里我们主要将TCP连接的建立和释放的过程。

    3.1 TCP连接的建立

    TCP连接的建立采用客户-服务器方式。主动发起连接建立的应用进程叫做客户端(client),而被动等待连接建立的应用进程叫做服务器(server)。

    TCP建立连接的过程叫做握手,握手的过程需要在客户端和服务器之间交换3个TCP报文段,因此我们形象地将TCP连接的建立过程称为“三次握手”。

    下图是TCP三次握手建立连接的过程,假定主机A运行的是TCP客户程序,主机B运行的是TCP服务器程序。最初两端的TCP进程都处于关闭(Closed)状态。注意:A客户端主动打开连接,B服务器被动打开连接。

     描述整个过程

    1. 服务器初始化状态

    主机B的TCP服务器进程先创建传输控制块(TCB),这时socket(),bind() 函数已经执行完毕,服务器进程准备接受客户进程的连接请求。然后服务器进程调用listen()函数,此时服务器进程处于监听(listen)状态,紧接着调用accept()函数,等待客户的连接请求到来。如有,即作出响应。

    服务端进程调用函数顺序:socket—>bind—>listen—>accept。当执行到accept()函数时,服务器进程会一直处于阻塞状态,直到有客户连接请求到达才返回。

    2. 客户端发起连接请求,发送SYN同步报文段,第一次握手

    主机A的客户进程也是首先创建传输控制块(TCB),然后向主机B发出连接请求报文段,这时请求报文段的首部的同步位SYN=1,同时选择一个初始序号seq=x,这个初始序号x就是随机产生的整数ISN。TCP规定,SYN报文段(即SYN=1的TCP报文段)不能携带数据,但要消耗一个序号。这时,TCP客户进程进入 SYN-SENT(同步已发送) 状态

    客户进程调用函数顺序:socket——>connect。当客户进程调用connect()函数时,客户进程就会向服务器进程发出连接请求的SYN同步报文段。

    前面我们已经讲过,携带SYN标志的TCP报文段称为同步报文段。此时,同步标志位 SYN = 1。

    ISN(Initial Sequence Number) 初始序列号

    3. 服务器同意建立连接,回复确认信息,第二次握手

    主机B的服务器进程收到连接请求报文段后,如同意建立连接,则向主机A的客户进程发送确认报文段。在确认报文段的首部中,SYN=1,ACK=1,确认号=x+1(没有数据,所以长度为0,直接seq+1即可),同时也为自己选择一个初始序号seq = y。请注意,这个确认报文段也不能携带数据,但同样要消耗一个序号。这时,TCP服务器进程进入 SYN-RCVD(同步收到) 状态

    4. 客户端确认连接,发送确认连接信息,第三次握手

    主机A的客户进程收到主机B的服务器进程的确认报文段后,还要向主机B的服务器进程的SYN报文段给出确认。在确认报文段的首部中,ACK=1,确认号ack=y+1,而自己的序号seq=x+1。TCP的标准规定,ACK报文段可以携带数据。但如果不携带数据,则不消耗序号。在这种情况下,客户进程的下一个数据报文段的序号仍是seq=x+1。这是,TCP连接已经建立,主机A的客户进程也进入 ESTABLISHED(已建立连接)状态当主机B的服务器进程接收到主机A的客户进程发来的确认报文段后,也进入ESTABLISHED(已建立连接)状态

    上面给出的TCP连接建立过程叫做“三次握手”。请注意,在上图中,B发送给A的报文段,也可以拆成两个报文段分两次发送。即先发送对A的连接请求同步报文段的确认报文段(ACK=1, ack=x=1),接着再发送B自己的连接请求的同步报文段(SYN=1, seq=y)给A。A收到B的同步报文段后,再给B回复一个确认报文段。那么,这样的过程就变成了“四次握手”,但效果是一样的。

     3.1.1 TCP连接建立相关问题

    问题1:TCP连接的建立为什么需要3次握手,而不是两次握手?

    在谢希仁编写的《计算机网络》一书中,给出的理由是:防止已失效的连接请求报文段突然又传送到了服务器端,因而产生错误。但是这个解释是不够准确的,不是最根本原因。首先,我们要清楚一点,TCP是全双工通信的三次握手的一个重要作用是客户端和服务端交换彼此的ISN,以便让对方知道接下来接收数据的时候如何按序列号重新组装TCP报文段。如果只有两次握手,至多只有连接发起方的起始序列号被确认,而另一方的起始序列号却得不到对方的确认。因为只有TCP通信双方的SYN同步报文段的首部中有本端的起始序列号,所以每一方都要对对方的SYN同步报文段回复一个ACK确认报文段,用来表明我方已经确认收到你方发送的同步报文段了。

    结论:TCP连接的建立过程中的三次握手,握的就是通信双方的起始序列号。三次握手实质上是三报文握手。

    下面,我们来讨论一下谢希仁先生给出的理由的这种情况的分析。

    (1)“已失效的连接请求报文”是如何产生的。现假定出现一种异常情况,即 客户端A发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间滞留了,以致延误到连接释放以后的某个时间才到达B。本来这是一个早已失效的连接请求报文段,但服务端B收到此失效的连接请求报文段后,就误以为是客户端A又发出一次新的连接请求。于是就向A发出确认报文段,同意建立连接。如果只有两次握手,那么只要B发出确认报文段,B就认为新的连接建立了,B进入ESTABLISHED(已建立连接)状态。

    (2)由于现在A并没有发出建立连接的请求,因此不会理睬B的确认,也不会向B发送数据。但B却以为新的连接是已经建立了的,并一直在等待A发来数据。也就是说,B此时一直处于忙等状态,其结果是浪费了B的资源开销。

    结论:两次握手,可能导致服务器需要维护许多不成功的TCP连接,造成服务器资源的浪费。

    需要注意的是,在TCP连接建立的过程中,只要服务器进程收到了连接请求的SYN同步报文段,服务器就会为这个连接请求创建传输控制块TCB数据结构,这就需要开辟内存空间来维护这个数据结构。

    问题2:在TCP连接建立过程中,如果服务器一直收不到客户端的ACK确认报文段,会发生什么?

    操作系统会给每个处于 SYN-RCVD状态的服务器进程都设定一个定时器,如果超时时间还没有收到客户端第三次握手的ACK确认报文段,将会重新发送第二次握手的报文段,直到重发达到一定次数时才会放弃。

    问题3:初始序列号ISN为什么要随机初始化?

    seq序号表示的是发送的TCP报文段数据部分的起始字节位置,服务器/客户端可以通过序号正确读取数据。如果不是随机分配起始序列号,那么黑客就会很容易获取到客户端与服务器之间TCP通信的初始序列号,然后通过伪造序列号让通信主机读取到携带病毒的TCP报文段,发起网络攻击。

    问题4:在TCP连接建立的过程中,可能会出现什么攻击?如何解决?

    SYN flood 攻击:又称为SYN泛洪攻击。下面介绍一下泛洪攻击是如何发生的。

    (1)攻击者在短时间内伪造大量不存在的IP地址,向服务器不断地发送连接请求的SYN同步报文段,当服务端收到这些连接请求的报文段后,就会为该连接请求创建传输控制块来保存客户端的信息。当有大量的连接请求时,服务器会消耗掉大量的内存资源,直至内存资源被耗尽。

    (2)服务器同时需要为每条连接请求回复ACK确认报文段,并等待客户端的确认。但是客户端的IP地址是虚假的,也就不会向服务器回复确认报文段,那么服务器需要不断地重发第2次握手的报文段直至超时。同时,这些伪造的连接请求SYN同步报文段还将长时间占用未连接队列(Linux默认的限制一般是256个),导致正常客户端的连接请求SYN同步报文段被丢弃,目标系统运行缓慢,严重者引起网络阻塞甚至服务器系统瘫痪。

    我们可以通过下面这个图来直观了解一下SYN泛洪攻击的过程:

    SYN泛洪攻击

     解决的办法:

    方法1:缩短SYN Timeout 时间。由于 SYN Flood 攻击的效果取决于服务器上保持的半连接数,这个值=SYN攻击频度 x SYN Timeout,所以通过缩短从接收到SYN报文段到确定这个报文段无效并丢弃该连接的时间。例如,设置为20秒以下(过低的SYN Timeout 设置可能会影响客户的正常访问),可以成倍地降低服务器的载荷。

    方法2:设置 SYN Cookie。就是给每一个连接请求的IP地址分配一个Cookie,如果短时间内连续收到某个IP地址的大量重复SYN报文段,就认定是收到了攻击。以后从这个IP地址来的报文段会被丢弃。

    方法3:使用防火墙。SYN Flood攻击很容易就能被防火墙拦截。

    3.1.2 连接超时问题

    前面介绍的是TCP连接正常建立的过程。如果客户端访问一个距离它很远的服务器或者由于网络繁忙,导致服务器对于客户端发出的同步报文段没有应答,此时客户端程序将产生什么样的行为呢?显然,对于提供可靠服务的TCP来说,它必然是先进行重连(可能执行多次),如果重连仍然无效,则通知应用程序连接超时,建立TCP连接失败。

    发起TCP连接请求的客户端,当发送了同步报文段后,会开启一个重传定时器,当超时时间到达后,如果没有收到确认报文段,会重发一次同步报文段,并将重连的超时时间增加一倍,直到达到规定的重连次数为止。TCP重连次数是由 /proc/sys/net/ipv4/tcp_syn_retries 内核变量定义的,默认值是6,它表示的含义是建立TCP连接时SYN同步报文段重发的次数。

    可以通过sysctl 命令来查看:(Linux系统:CentOS-8.3)

    # sysctl -a | grep tcp_syn_retries
    net.ipv4.tcp_syn_retries = 6

     在Linux系统中,连接超时典型为2分7秒,而对于一些Client来说,这是一个非常长的时间,所以在实际网络编程中,可以使用非阻塞模式来实现。例如:使用 select(2)、poll(2)、epoll(2)等系统调用来实现多路复用。

    下来来分析一下,这个2分7秒的时间间隔是怎样来的。

    2分7秒 即 127秒,刚好是 2 的 7 次幂减1,如果TCP连接建立的SYN报文段超时时间间隔是按照2的幂来递增的话,那么:

    第 1 次发送 SYN 报文后等待 1s(2 的 0 次幂),如果超时,则重发
    第 2 次发送后等待 2s(2 的 1 次幂),如果超时,则重发
    第 3 次发送后等待 4s(2 的 2 次幂),如果超时,则重发
    第 4 次发送后等待 8s(2 的 3 次幂),如果超时,则重发
    第 5 次发送后等待 16s(2 的 4 次幂),如果超时,则重发
    第 6 次发送后等待 32s(2 的 5 次幂),如果超时,则重发
    第 7 次发送后等待 64s(2 的 6 次幂),如果超时,则超时失败
    
    综上,1+2+4+8+16+32+64=127

     上面总的超时时间刚好是127秒。也就是说,Linux内核在尝试建立TCP连接时,最多会尝试7次,重发6次。

    修改重连次数和超时时间

    可以通过以下命令修改该值,例如将其改为5。

    sysctl -w net.ipv4.tcp_syn_retries=5

    如果希望重启系统后仍生效,可以在 /etc/sysctl.conf 文件中添加如下内容:

    net.ipv4.tcp_syn_retries=5

    在应用程序中,我们可以通过设置socket选项中的 SO_SNDTIMEO 选项,然后调用connect()函数。具体代码如下:

    //设置connect超时时间
    int timeout_connect(const char *ip, int port, int timeout)
    {
        int ret = 0;
        struct sockaddr_in cli_addr;
        
        bzero(&cli_addr, sizeof(cli_addr));
        cli_addr.sin_family = AF_INET;
        inet_pton(AF_INET, ip, &cli_addr.sin_addr);
        cli_addr.sin_port = htons(port);
        
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        assert(sockfd >= 0)
        //通过选项SO_SNDTIMEO所设置的超时时间类型是timeval,这和select()系统调用的超时参数类型相同
        struct timeval timeout;
        timeout.tv_sec = time;
        timeout.tv_usec = 0;
        socklen_t len = sizeof(timeout);
        ret = setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, len);
        assert(ret != 1);
        
        ret = connect(sockfd, (struct sockaddr*)&cli_addr, sizeof(cli_addr));
        if(ret == -1){
            //超时对应的错误号是EINPROGRESS。下面这个条件如果成立,我们就可以处理定时任务了
            if(errno == EINPROGRESS){
                printf("connection timeout, process timeout logic\n");
                return -1;
            }
            printf("error occur when connecting to server\n");
            return -1;
        }
        return sockfd;
    }

    3.2 TCP连接的释放

    TCP连接的释放可以用“四次挥手”的过程来描述。数据传输结束后,通信的双方都可释放连接。现在 客户端A和服务器B都处于 ESTABLISHED 状态。释放连接的过程如下图所示:

     描述整个过程

    1. A客户端主动断开连接,发送释放连接的FIN报文段,第一次挥手

    A客户端进程先向B服务器进程发送释放连接的FIN结束报文段,并停止再发送数据,主动关闭TCP连接。在结束报文段的首部中,终止控制位FIN=1,其序号字段seq=u,它等于前面已发送过的数据的最后一个字节的序号加1。此时,A客户端进程进入 FIN-WAIT-1(终止等待1)状态,等待B服务器进程的确认。请注意,TCP规定,FIN报文段即使不携带数据,它也要消耗掉一个序号。这点和SYN报文段是一样的。

    2. B服务器收到A客户端的结束报文段,发出确认报文段,第二次挥手

    B服务器进程收到A客户端进程发来的释放连接的FIN结束报文段后,立即发出确认报文段,确认号ack=u+1,而这个报文段自己的序号seq=v,等于B服务器前面已发送过的数据的最后一个字节的序号加1。然后B服务器进程进入 CLOSE-WAIT(关闭等待)状态。TCP服务器进程这时通知高层的应用进程,那么从 A 到 B 这个方向的连接就释放了,此时TCP连接处于半关闭(half-close)状态,即A客户端已经没有数据要发送了,但B服务器若发送数据,A客户端仍要接收。也就是说,从 B 到 A 这个方向的连接并未关闭,这个状态可能会持续一段时间。

    A客户端收到B服务器的确认报文段后,就进入 FIN-WAIT-2(终止等待2)状态,等待B服务器发出的FIN结束报文段。

    3. B服务器释放连接,发出连接释放的结束报文段,第三次挥手

    当B服务器已经没有要向A客户端发送的数据时,其应用进程就通知TCP释放连接,向A客户端发送释放连接的结束报文段。在这个结束报文段的首部中,终止控制位FIN置1,假定其序号字段为w(在半关闭状态中,B服务器可能又发送了一些数据),同时还必须重复上次已发送过的确认号ack=u+1。这时,B服务器进程就进入 LAST-ACK(最后确认)状态,等待A客户端的确认。

    4. A客户端收到B服务器释放连接的结束报文段,发出确认报文段,第四次挥手

    A客户端在收到B服务器的释放连接的结束报文段后,必须对此发出确认,即向B服务端发送一个确认报文段。在确认报文段的首部中,控制位ACK=1,确认号字段ack=w+1,而自己的序号字段seq=u+1(根据TCP标准,前面发送过的FIN报文段是要消耗一个序号的)。然后A客户端进入到TIME-WAIT(时间等待)状态

    请注意,此时TCP连接还没有释放掉,必须经过时间等待计时器(TIME-WAIT timer)设置的时间2MSL后,A才进入到 CLOSED 状态。时间MSL(Maximum Segment Lifetime,最长报文段寿命) 即一个TCP报文段存活的最长时间。RFC793建议设为2分钟,现在可以根据情况使用更小的MSL值。因此从A客户端进入到 TIME-WAIT 状态后,要经过4分钟才能进入到CLOSED状态,才可以建立下一个新的连接,当A客户端撤销相应的传输控制块TCB后,就结束了这次的TCP连接。

    B服务器只要收到了A客户端发出的确认报文段,就进入 CLOSED状态。同样,B服务器在撤销相应的传输控制块TCB后,就结束了此次的TCP连接。

    可以发现,B服务器结束TCP连接的时间要比A客户端早一些。

    上述内容就是TCP连接释放的过程,俗称“四次挥手”过程,其实质上是四报文挥手。

    3.2.1 TCP连接释放相关问题

    问题1:为什么建立连接是三次握手,而关闭连接却是四次挥手?

    在建立TCP连接时,当服务器收到客户端发来的连接请求的SYN同步报文段后,可以直接发送一个ACK+SYN的报文段给客户端,其中ACK控制位是用来确认的,SYN控制位是用来同步的。但是在关闭连接时,当服务器收到客户端发来的FIN结束报文段时,自己这边可能还有数据没有发送完,因此只能先回复一个ACK确认报文,告诉客户端,“你发来的FIN报文我收到了”。只有等到服务器所有的数据都发送完了,服务器才会向客户端发送一个FIN结束报文段,最后客户端回复一个确认报文,总共就是四次挥手过程。也就是说,在关闭连接的第二次挥手阶段,服务器不能将控制位ACK+FIN 同时放在一个报文段中回复给客户端。

    注意:发送了FIN结束报文段,只是表示本端不能再继续发送数据了,但是还可以接受数据。TCP通信它是全双工的,收到一个FIN报文段,只是关闭了一个方向上的连接,而另一个方向仍能发送数据,此时TCP处于半关闭状态。

    问题2:为什么客户端在 TIME-WAIT 状态时,必须等待2MSL的时间才能进入到 CLOSED状态呢?

    第一,为了保证客户端发送的最后一个ACK报文段能够到达对端,即保证可靠地终止TCP连接。因为如果出现网络拥塞,这个ACK报文段是有可能丢失的,因而使处于LAST-ACK状态的服务器收不到客户端对自己已发送过的FIN+ACK报文段的确认。那么,服务器会超时重传这个FIN+ACK报文段,而客户端就能在2MSL时间内收到这个重传的FIN+ACK报文段。接着客户端重传一次确认,重新启动2MSL计时器。最后,客户端和服务器都正常进入到CLOSED状态。如果客户端在 TIME-WAIT 状态时不等待一个2MSL时间,而是在发送完ACK确认报文段后立即释放连接,进入到CLOSED状态,那么就无法收到服务器重传的FIN+ACK报文段,因而也不会再发送一次确认报文段。这样,服务器就无法按照正常步骤进入到CLOSED状态。

    第二,防止已失效的连接请求报文段出现在本次TCP连接中。客户端在发送完最后一个ACK报文段后,再经过2MSL的时间后,就可以使本次TCP连接持续的时间内所产生的所有报文段都从网络上消失。这样就可以使下一个新的TCP连接中不会出现之前旧的连接请求报文段。

    问题3:为什么是2MSL,这个时间是如何得来的?

    我们知道,MSL是TCP报文段的最大生存时间,2MSL的时间是从客户端发出最后一个ACK报文段开始计时的,考虑到了重传的因素。

    客户端—[ACK报文段]—>服务器            1MSL

    服务器—[FIN+ACK报文段]—>客户端    1MSL (如果服务器在超时时间内没有收到客户端的ACK报文段,就重传FIN+ACK报文段)

    保证在TCP的两个传输方向上,那些尚未被接收或迟到的报文段都消失,理论上保证最后一个ACK报文段可靠到达。

    问题4:TIME-WAIT 状态何时出现?TIME-WAIT会带来哪些问题?

    TIME-WAIT状态是主动关闭连接的一方收到了对方发来的FIN结束报文段并且本端发送ACK确认报文段后的状态。

    TIME-WAIT的引入是为了让TCP报文段得以自然消失,同时为了让被动关闭的一方能够正常关闭连接。

    • 服务器主动关闭连接,短时间内关闭了大量的客户端连接,会造成服务器上出现大量的 TIME-WAIT状态的连接,占据大量的tuple(源IP地址、目的IP地址、协议号、源端口、目的端口),严重消耗着服务器的资源。
    • 客户端主动关闭连接,短时间内大量的短连接,会大量消耗客户端主机的端口号,毕竟端口号只有65535个,断开耗尽了,后续就无法启用新的TCP连接了。

    问题5:解决 TIME-WAIT 状态引起的bind()函数执行失败的问题?

    问题场景:我在编写一个基于TCP连接的socket服务器程序并反复调试的时候,发现了一个让人无比心烦的情况:每次kill掉该服务器进程并重新启动的时候,都会出现bind错误:error:98,Address already in use。然而再kill掉该进程,再次重新启动的时候,就bind成功了。

    问题原因:当我kill掉服务器进程的时候,系统并没有马上完全释放掉socket的TCP连接资源,此时socket处于TIME-WAIT状态,当我使用 netstat 命令查看该进程的端口号时,发现该进程处于TIME-WAIT状态,需要等待2MSL时间后,整个TCP连接才算真正结束。这就是我的服务器进程被杀死后,不能马上重新启动的原因(错误提示为:“Address already in use”)。Linux系统中,一个端口释放后需要等待两分钟才能再次被使用。

    问题解决:我们可以使用setsockopt()函数设置socket描述符的SO_REUSEADDR选项,该socket选项可以让端口被释放后立即就能被再次使用,表示允许创建端口号相同但是IP地址不同的多个socket描述符。

    代码描述如下:

    int opt = 1;
    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    问题6:TIME-WAIT 和 CLOSE-WAIT 的区别?

    • TIME-WAIT 状态是主动关闭TCP连接的一方在本端已经关闭的前提下,收到对端的关闭请求并将ACK确认报文段发送过去后所处的状态。

    这种状态表示:通信双方都已完成工作,只是为了保证本次TCP连接能够顺利正常的关闭,即可靠地终止TCP连接。

    • CLOSE-WAIT 状态是被动关闭TCP连接的一方在接收到对端的关闭请求(FIN结束报文段)并且将ACK确认报文段发送出去后所处的状态。

    这种状态表示:收到了对端关闭连接的请求,但是本端还没有完成工作,还未关闭本端的TCP连接。

    问题7:半连接、半打开、半关闭的区别?

    半连接:在TCP连接建立的三次握手过程中,主动发起连接请求的一方不发最后一次的ACK确认报文,使得服务器端阻塞在 SYN-RCVD(同步收到)状态。

    半打开:如果TCP通信一方异常关闭(如断网、断电、进程被kill掉),而通信对端并不知情,此时TCP连接处于半打开状态,如果双方不进行数据通信,是无法发现问题的。解决的办法是引入心跳机制,设置一个保活计时器(keepalive timer),以检测半打开状态,检测到了就发送RST复位报文段,重新建立连接。

    设想有这样的情况:客户端已主动与服务器建立了TCP连接。但是后来客户端的主机突然发生故障,客户端的TCP服务进程被kill掉了。显然,服务器以后就不能再收到客户端发来的数据。因此,应当有必要的措施使得服务器不要再白白地等待下去。解决的办法就是使用保活计时器。服务器每收到一次客户端的数据,就重新设置保活计时器,时间的设置通常是两小时。若两小时没有收到客户端发来的数据,服务器就发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文段后仍无客户的响应,服务器就认为客户端出了故障,接着就关闭这个TCP连接。

    半关闭:主动发起连接关闭请求的一方A发送了FIN结束报文段,对端B回复了ACK确认报文段后,B并没有立即发送本端的FIN结束报文段给A。此时A端处于FIN-WAIT-2(结束等待2)状态,A仍然可以接收B发送过来的数据,但是A已经不能再向B发送数据了。这时的TCP连接为半关闭状态。

    半打开和半关闭的区别:半打开是指TCP通信的一端由于异常关闭,通信双方已经无法进行正常的数据传输了;半关闭是指TCP通信的其中一个方向上的连接已经关闭了,而另一个方向的连接还是正常的,仍然可以进行数据传输。

    四、TCP状态转换

    为了更清晰地看出TCP连接的各种状态之间的关系,下图给出了TCP的状态转换示意图。

    说明:紫色框框是TCP状态,红色是服务器进程的正常状态转换,蓝色是客户端进程的正常状态转换,黑色是异常变迁,即出现问题时的状态转换。

    TCP的状态转换

     4.1 服务器正常状态转换

     服务器状态转换示意图如下所示

    1. TCP连接建立的三次握手阶段

    • CLOSED —> LISTEN:服务器进程调用listen()后进入LISTEN状态,被动等待客户端发起连接。
    • LISTEN   —> SYN-RCVD:服务器进程一旦收到客户端发来的连接请求SYN同步报文段,就会将该连接放入内核中的等待连接队列中,并向客户端发送ACK+SYN确认报文段,服务器进程进入 SYN-RCVD状态。
    • SYN-RCVD —> ESTABLISHED:服务器一旦接收到客户端发来的ACK确认报文段,就进入 ESTABLISHED 状态,此时TCP连接建立成功,可以进行数据传输了。

    2. TCP连接释放的四次挥手阶段

    • ESTABLISHED —> CLOSE-WAIT:当客户端主动发起连接关闭,服务器收到客户端发来的FIN结束报文段,服务器向客户端返回确认报文段,服务器就进入了 CLOSE-WAIT状态,此时TCP处于半关闭状态。
    • CLOSE-WAIT —> LAST-ACK:当服务器向客户端发送关闭连接的FIN结束报文段后,服务器就进入 LAST-ACK 状态。
    • LAST-ACK —> CLOSED:当服务器收到客户端对自己发出的FIN结束报文段的确认报文段后,服务器关闭TCP连接,进入 CLOSED 状态。

    4.2 客户端正常状态转换

     客户端状态转换示意图如下所示

    1. TCP连接建立的三次握手阶段

    • CLOSED —> SYN-SENT: 客户端调用connect()向服务器发送连接请求的同步SYN报文段,表示想与服务器建立TCP连接,自己进入 SYN-SENT 状态,等待服务器的响应。
    • SYN-SENT —> ESTABLISHED: connnect()调用成功,客户端收到服务器的ACK确认报文段,此时客户端进入 ESTABLISHED 状态。

    2. TCP连接释放的四次挥手阶段

    • ESTABLISHED —> FIN-WAIT-1: 客户端主动调用close(),向服务器发送断开连接的FIN结束报文段,自己进入 FIN-WAIT-1 阶段,等待服务器的响应。
    • FIN-WAIT-1 —> FIN-WAIT-2: 客户端收到服务器对自己发出的FIN结束报文段的确认报文段后,进入 FIN-WAIT-2 阶段,等待服务器的结束报文段。
    • FIN-WAIT-2 —> TIME-WAIT: 客户端收到服务器发来的FIN结束报文段,发送ACK确认报文段给服务器,自己则进入 TIME-WAIT 状态。
    • TIME-WAIT —> CLOSED: 客户端要等待2MSL,即2个报文最大生存时间,才进入CLOSED状态。这是为了保证客户端发送的最后一个ACK确认报文段能够到达服务器,即保证可靠地终止TCP连接,以及防止已失效的连接请求报文段出现在本连接中。

     五、基于TCP协议的socket通信流程

            Socket 又称 ”套接字”,是系统提供的用于网络通信的方法。它并不是一种协议,没有规定计算机应当怎么传递信息,只是给开发人员提供了一个发送和接收消息的接口。开发人员能使用这个接口提供的方法,发送与接收消息。Socket描述了一个 IP 和 端口号。它简化了开发员的操作,知道了对方 IP 以及 port,就可以给对方发送消息,再由服务器处理发送过来的这些消息,Socket包含了通信的双方,即客户端与服务器。

    TCP协议的socket通信流程,如图所示:

    5.1 TCP网络编程—服务器编程步骤

    1、socket():创建一个套接字,创建成功返回一个套接字文件描述符 listen_fd。

    1-1、setsockopt():可选,设置socket属性。

    2、bind():绑定本地的 IP 地址和端口号信息到socket上。

    3、listen():开启监听。

    4、accept():接收客户端的连接请求。连接成功,返回一个连接文件描述符 conn_fd。

    5、recv()、send():收发数据。或者使用 read()、write()。

    6、close(conn_fd):关闭网络连接。

    7、close(listen_fd):关闭监听。

    5.2 TCP网络编程—客户端编程步骤

    1、socket():创建一个套接字,创建成功返回一个套接字文件描述符 client_fd。

    1-1、setsockopt():可选,设置socket属性。

    1-2、bind():可选,绑定本地的 IP 地址和端口号信息到socket上。

    2、connect():请求连接到服务器端。

    3、recv()、send():收发数据。或者使用 read()、write()。

    4、close(client_fd):关闭网络连接。

    参考

    《计算机网络(第7版-谢希仁)》第5章

    《Linux高性能服务器编程》第3章

    《UNIX网络编程卷1:套接字联网API(第3版)》第1部分

    四、TCP三次握手、四次挥手详解

    TCP连接的状态详解以及故障排查

    TCP 为什么三次握手而不是两次握手(正解版)

    详解TCP三次握手/四次挥手

     近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题

    展开全文
  • 传输层 --- 面向连接传输TCP

    千次阅读 2022-04-04 15:11:19
    在一对通信的进程之间提供一条理想的字节流管道。 2.点到点通信: 仅涉及一对通信进程。 3.全双工: 可以同时双向传输数据。 4.可靠、有序的字节流: 不保留报文边界。 5.需要的机制 ①建立连接: 通信双方...

    一.TCP概述

    1.TCP服务模型:

    在一对通信的进程之间提供一条理想的字节流管道。

    2.点到点通信:

    仅涉及一对通信进程。

    3.全双工:

    可以同时双向传输数据。

    4.可靠、有序的字节流:

    不保留报文边界。

    5.需要的机制
    ①建立连接:

    通信双方为本次通信建立数据传输所需的状态(套接字、缓存、变量等)。

    ②可靠数据传输:

    流水线式发送,报文段检错,丢失重传。

    ③流量控制:

    发送方不会令接收方缓存溢出。

    在这里插入图片描述

    二.TCP报文段结构

    在这里插入图片描述
    1.TCP头包和数据构成:

    TCP和UDP都有一个服务器和客户端的模型,对客户端端口和服务器端口来讲,服务器的端口一定要是被指定的,而客户端的端口可以自己分配给它,也可以由计算机或其他设备随机生成相应的端口号。

    ①序列号:sequence number 32bits

    a.当SYN(flag)=1时,这时一个初始序列号。
    b.当SYN=0时,序列号代表的是这段数据中累加后的字节,也就是发送了多少个数据的字节。

    ②确认号:Acknowledgement number 32bits

    a.当ACK(flag)=1时,确认号针对的是接受者所期望获得的下一个序列号是多少。
    b.第一个ACK对初始的ISN是有确认的,但是对数据没有回应。

    ③首部长度(数据偏移):Data offset

    数据偏移主要代表的是TCP头包有多大的数量,一般的TCP头包的头数量是20bytes,有options和padding时,最大为60bytes。

    ④状态位:Flags 9bits

    URG:紧急指针字段;
    ACK:当ACK为1时,Acknowledgement number字段为1;
    PSH:当PSH出现时,它要求接收者需要将接收道德数据尽快地提供给应用;
    RST:当发送RST请求时,逻辑上的连接需要复位;
    SYN:代表的是同步数据请求,在建立连接时会出现;
    FIN:在连接终止时会出现。

    状态位还包括了ENC域(exploit congestion notification):包括了CWR和ECE。
    当一个IP包的ENC域被路由器设置为11时(CWR和ECE均为1),接收端而非发送端被通知路径上发生了拥塞。ECN使用TCP头部来告知发送端网络正在经历拥塞,并且告知接收端发送端已经收到了接收端发来的拥塞通告,已经降低了发送速率。

    ⑤接收窗口:window 16bits

    Window代表的是接收窗口提示,即可以接收的数据量是多少字节。

    ⑥校验和

    16位校验主要是为了在TCP中对头和数据都进行计算。

    ⑦紧急数据指针:Urgent 16bits

    当flag中的URG置位后,16位的字段就是有意义的,这只是相对于序列号的一个偏移。

    ⑧选项中的几种选择:Options 变长,最大40字节

    a.最大报文长度MSS(Maxium Segment Size),指的是一个TCP报文数据段的最大长度。要尽可能大一些但是又不需要IP拆分。
    b.窗口扩大选项。可用于控制传输窗口大小。
    c.时间戳。非常有用。可以用于计算往返时间RTT、区分重复报文。因为报文的序号只能是232-1个,所以很容易就重复了,加上时间戳可以进行区分。

    Q:为何TCP字段没有length字段,而UDP有length字段?
    A:TCP容纳不了一个长度字段,但接收端可以隐含计算出;UDP正好剩个位子容纳UDP长度字段,无须隐含计算。
    注:一端发送的协议报文,另外一端一定要有方法知道协议报文的长度,如果没有方法知道,那这个协议一定是一个假的协议!

    2.发送序号和确认序号的含义
    在这里插入图片描述
    ①这两个序号是实现可靠数据传输的重要组成部分。由于TCP提供的是字节流传输,每个字节都有一个序号。

    ②序号:这个序号并不是报文段的序号,而是报文段中第一个数据字节的序号。

    ③确认序号:前面在介绍可靠传输时,假设数据是单方向传输的,接收方的反馈放在一个单独的ACK分组中发送。但是TCP提供的是全双工通信,A向B发送数据的同时,A可能也在从B接收数据,这种情况下A可以将反馈捎带在它发送给B的报文段中。

    ④TCP采用累积确认,确认序号用来表示期望从对方接收的下一个字节的序号。

    ⑤举例说明
    在这里插入图片描述

    a.主机A向主机B发送仅包含一个字符‘C’的报文段:

    • 发送序号是42,
    • 确认序号是79(对前一次数据的确认)。

    b.主机B将字符‘C’回送给主机A:

    • 发送序号为79,
    • 确认序号为43(对收到‘C’的确认)。

    c.主机A向主机B发送确认报文段(不包含数据):

    • 确认序号为80(对收到‘C’的确认)。

    3.重要的TCP选项
    ①最大段长度MSS

    a.TCP段中可以携带的最大数据字节数;
    b.建立连接时,每个主机可声明自己能够接受的MSS,缺省为536字节。

    ②窗口比例因子window scale

    a.建立连接时,双方可以协商一个窗口比例因子;
    b.实际接收窗口大小=window size*2^window scale^。

    ③选择确认SACK

    a.最初的TCP协议只使用累积确认;
    b.改进的TCP协议引入选择确认,允许接收端指出缺失的数据字节。

    1.TCP有两个窗口,一个是滑动窗口,一个是拥塞窗口。WS与滑动窗口相关。通过增加一个因子来扩大默认滑动窗口的值。
    2.滑动窗口的定义:收到ACK之前可以连续发送的字节数。

    举例说明:某接收端的窗口值假设为10240字节,假设发送端发的包每次都是1024字节大小,那么发送端可以连续发送10个包,而不需要收到接收端的ACK确认。

    3.窗口大小占TCP头部16位,也就是窗口值最大能有2^16=65535bytes=64K。
    65535字节到底多大?在以前网速没有那么快的情况下是足够使用的,但随着网速的不断提升,65535字节就不够使用了。
    因此引入了WS,在TCP的option中增加16位作为滑动窗口因子window scaling。
    窗口值*窗口因子=最终的窗口值,但是并不是所有的客户端、服务端都支持窗口因子。

    三.TCP可靠数据传输

    1.概述
    ①TCP在不可靠的IP服务上建立可靠的数据传输。

    ②基本机制:

    a.发送端:流水线式发送数据、等待确认、超时重传;
    b.接收端:进行差错检测,采用积累确认机制。

    ③乱序段处理:协议没有明确规定

    a.接收端不缓存:可以正常工作,处理简单但效率低;
    b.接收端缓存:效率高,但处理复杂。

    2.一个高度简化的TCP协议
    ①仅考虑可靠传输机制,且数据仅在一个方向上传输。

    ②接收方:

    a.确认方式:采用积累确认,仅在正确、按序收到报文段后,更新确认序号;其余情况,重复前一次的确认序号(与GBN类似)。
    b.失序报文段处理:缓存失序的报文段(与SR类似)。

    ③发送方:

    a.发送策略:流水线式发送报文段。
    b.定时器的使用:仅对最早未确认的报文段使用一个重传定时器(与GBN类似)。
    c.重发策略:仅在超时后重发最早未确认的报文段(与SR类似,因为接收端缓存了失序的报文段)。

    3.TCP发送方要处理的事件
    ①收到应用数据:

    a.创建并发送TCP报文段;
    b.若当前没有定时器在运行(没有已发送、未确认的报文段),启动定时器;
    c.如果发送窗口不可用,refuse。

    ②超时:

    a.重传包含最小序号的、未确认的报文段;
    b.重启定时器。

    ③收到ACK:

    如果确认序号大于基序号(已发送未确认的最小序号):
    a.推进发送窗口(更新基序号);
    b.如果发送窗口中还有未确认的报文段,启动定时器,否则终止定时器。

    4.TCP发送方状态机
    在这里插入图片描述

    5.TCP可能的重传场景
    在这里插入图片描述
    ①情形一:ACK丢失

    超时、重传。

    ②情形二:超时设置得太小

    重发最早未确认的报文段(序号92),重启定时器。
    当ACK(100)到来时,更新基序号为100,对序号为100的段启动定时器。
    当ACK(120)到来时,更新基序号为120,终止定时器。对最后一个到来的ACK不做响应。

    ③情形三:累积ACK减少重传

    流水式发送的若干报文段中,若某个报文段的ACK丢失,而其后报文段的ACK在超时前到来,则发送方不会重发丢失了ACK的报文段。

    ④问题:
    a.第二种情形,如果TCP像SR一样,每个报文段使用一个定时器,会怎么样?

    只使用一个重传定时器,避免了超时值过小时大量报文段的重发。

    b.第三种情形,采用流水式发送和累积确认,可以避免重发哪些报文段?

    避免重发某些丢失了ACK的报文段。

    6.如何设置超时值
    ①为什么设置合理的超时值很重要

    a.若超时值太小,容易产生不必要的重传;
    b.若超时值太大,则丢包恢复的时间太长。

    ②直观上,超时值应大于RTT,但RTT是变化的。

    ③如何估计RTT:

    a.RTT是变化的,需要实时测量从发出某个报文段到收到其确认报文段之间经过的时间(称为SampleRTT)。
    b.由于SampleRTT波动很大,更有意义的是计算其平均值(称EstimatedRTT)。

    ④平均RTT的估算方法(指数加权移动平均):

    a.EstimatedRTT=(1-α)*EstimatedRTT + α* SampleRTT。
    b.典型的,α=0.125。

    ⑤瞬时RTT和平均RTT有很大的偏差:

    a.需要在EstimatedRTT上加一个“安全距离”,作为超时值。
    b.安全距离的大小与RTT的波动幅度有关。

    ⑥估算SampleRTT与EstimatedRTT的偏差(称DevRTT):

    a.DevRTT=(1-β)*DevRTT + β *|SampleRTT-EstimatedRTT|
    b.典型的,β=0.25。

    ⑦设置重传定时器的超时值:

    TimeoutInterval=EstimatedRTT + 4*DevRTT

    7.TCP确认的二义性
    在这里插入图片描述

    ①TCP确认的二义性问题:

    a.重传的TCP报文段使用与原报文段相同的序号;
    b.发送端收到确认后,无法得知是哪个报文段进行的确认。

    ②二义性确认带来的问题:

    对重传报文段测量的SampleRTT,可能不准确。

    ③解决方法:

    a.忽略有二义性的确认,只对一次发送成功的报文段测量SampleRTT,并更新EstimatedRTT;
    b.当TCP重传一个段时,停止测量SampleRTT。

    8.定时器补偿
    ①简单忽略重传报文段的问题:

    a.重传意味着超时值可能偏小了,需要增大;
    b.若简单忽略重传报文段(不更新EstimatedRTT),则超时值也不会更新,超时设置过小的问题没有解决。

    ②解决方法

    a.采用定时器补偿策略,发送方每重传一个报文段,就直接将超时值增大一倍(不依赖于RTT的更新);
    b.若联系发生超时事件,超时值呈指数增长(至一个设置的上限值)。

    9.Karn算法
    Karn算法结合使用RTT估计值和定时器补偿策略确定超时值:

    a.使用EstimatedRTT估计初始的超时值;
    b.若发生超时,每次重传时对定时器进行补偿,直到成功传输一个报文段为止;
    c.若收到上层应用数据、或某个报文段没有重传就被确认了,用最近的EstimatedRTT估计超时值。

    10.TCP的接收端
    ①理论上,接收端只需区分两种情况:

    a.收到期待的报文段:发送更新的确认序号;
    b.其他情况:重复当前的确认序号。

    ②为减小通信量,TCP允许接收端推迟确认:

    接收端可以在收到若干报文段后,发送一个累积确认的报文段。

    ③推迟确认带来的问题:

    a.若延迟太大,会导致不必要的重传;
    b.推迟确认造成RTT估计不准确。

    ④TCP协议规定:

    a.推迟确认的时间最多为500ms;
    b.接收方至少每隔一个报文段使用正常方式进行确认。

    11.TCP接收端的事件和处理
    在这里插入图片描述
    12.快速重传
    在这里插入图片描述

    ①仅靠超时重发丢失的报文段,恢复太慢。

    ②发送方可利用重复ACK检测报文段丢失:

    a.发送方通常连续发送许多报文段;
    b.若仅有个别报文段丢失,发送方将收到多个重复序号的ACK;
    c.多数情况下IP按序交付分组,重复ACK极有可能因丢包产生。

    ③TCP协议规定:

    当发送方收到对同一序号的3次重复确认时,立即重发包含该序号的报文段。

    ④快速重传:

    就是在定时器到期前重发丢失的报文段。

    event:ACK received,with ACK field value of y
    	if(y>SendBase){   //收到更新的确认号
    		SendBase=y
    		if(there are currently not-yet-acknowledged segments)
    	}
    	else{    //收到重复序号的ACK
    		increment count of dup ACKs received for y
    		if(count of dup ACKs received for y=3){
    			resend segment with sequence number y //快速重传
    		}
    	}
    

    13.小结
    ①TCP可靠传输的设计要点:

    a.流水式发送报文段;
    b.缓存失序的报文段;
    c.采用累积确认;
    d.只对最早未确认的报文段使用一个重传定时器;
    e.超时后只重传包含最小序号的、未确认的报文段。

    ②以上措施可大量减少因ACK丢失、定时器过早超时引起的重传。

    ③超时值的确定:

    基于RTT估计超时值+定时器补偿策略。

    ④测量RTT:

    a.不对重传的报文段测量RTT;
    b.不连续使用推迟确认。

    ⑤快速重传:

    收到3次重复确认,重发报文段。

    ⑥TCP使用GBN还是SR?

    Go-Back-N
    a.接收方:

    • 使用累积确认;
    • 不缓存失序的分组;
    • 对失序分组发送重复ACK。

    b.发送方:

    • 超时后重传从基序号开始的所有分组。

    TCP
    a.接收方:

    • 使用累积确认;
    • 缓存失序的报文段;
    • 对失序报文段发送重复ACK;
    • 增加了推迟确认。

    b.发送方:

    • 超时后仅重传最早未确认的报文段;
    • 增加了快速重传。

    SR:
    a.接收方:

    • 缓存失序的分组;
    • 单独确认每个正确收到的分组。

    b.发送方:

    • 每个分组使用一个定时器;
    • 仅重传未被确认的分组。

    修改的TCP:
    a.接收方:

    • 缓存失序的报文段;
    • 累计确认;
    • SACK选项头中给出非连续数据块的上下边界。

    b.发送方:

    • 只对最早未确认的报文段使用一个定时器;
    • 仅重传接收方缺失的数据;
    • 增加了快速重传。

    ⑦TCP结合了GBN和SR的优点

    a.TCP的差错恢复机制可以看成是GBN和SR的混合体:

    • 定时器的使用:与GBN类似,只对最早未确认的报文段使用一个定时器;
    • 超时重传:与SR类似,只重传缺失的数据。

    b.TCP在减小定时器开销和重传开销方面要优于GBN和SR。

    四.TCP流量控制

    1.TCP的接收端:接收缓存
    ①接收端TCP将收到的数据放入接收缓存。
    ②应用进程从接收缓存中读数据。
    ③进入接收缓存的数据不一定被立即取走、取完。
    ④如果接收缓存中的数据未及时取走,后续到达的数据可能会因缓存溢出而丢失。
    在这里插入图片描述

    2.流量控制

    发送端TCP通过调节发送速率,不使接收端缓存溢出。

    3.为什么GBN/SR不需要流量控制
    ①GBN和SR均假设:

    a.正确、按序到达的分组被立即交付给上层;
    b.其占用的缓冲区被立即释放。

    ②发送方根据确认序号即可知道:

    a.哪些分组已被移出接收窗口;
    b.接收窗口还可以接受多少分组。

    4.为什么UDP不需要流量控制
    ①UDP不保证交付:

    a.接收端UDP将收到的报文载荷放入接收缓存;
    b.应用进程每次从接收缓存中读取一个完整的报文载荷;
    c.当应用进程消费数据不够快时,接收缓存溢出,报文数据丢失,UDP不负责任。

    5.TCP如何进行流量控制
    ①接收缓存中的可用空间称为接收窗口:

    RcvWindow=RcvBuffer-[LastByteRcvd-LastByteRead]

    ②接收方将RcvWindow放在报头中,向发送方通告接收缓存的可用空间。

    ③发送方限制未确认的字节数不超过接收窗口的大小,即:

    LastByteSent-LastByteAcked ≤ RcvWindow

    ④特别是,当接收方通告接收窗口为0时,发送方必须停止发送。
    在这里插入图片描述
    6.非零窗口通告
    ①发送方/接收方对零窗口的处理:

    a.发送方:当接收窗口为0时,发送方必须停止发送;
    b.接收方:当接收窗口变为非0时,接收方应通告增大的接收窗口。

    ②在TCP协议中,触发一次TCP传输需要满足以下三个条件之一:

    a.应用程序调用;
    b.超时;
    c.收到数据/确认。

    ③对于单向传输中的接收方,只有第三个条件能触发传输。

    ④当发送方停止发送后,接收方不再收到数据,如何触发接收端发送“非零窗口通告”呢?

    TCP协议规定:
    a.发送方收到“零窗口通告”后,可以发送“零窗口探测”报文段;
    b.从而接收方可以发送包含接收窗口的响应报文段。

    7.零窗口探测的实现
    ①发送端收到零窗口通告时,启动一个坚持定时器;
    ②定时器超时后,发送端发送一个零窗口探测报文段(序号为上一个段中最后一个字节的序号);
    ③接收端在响应的报文段中通告当前接收窗口的大小;
    ④若发送端仍收到零窗口通告,重新启动坚持定时器。

    9.糊涂窗口综合症
    ①当数据的发送速度很快,而消费速度很慢时,零窗口探测的简单实现带来以下问题:

    a.接收方不断发送微笑窗口通告;
    b.发送方不断发送很小的数据分组;
    c.大量带宽被浪费。

    ②解决方案:

    a.接收方启发式策略;
    b.发送方启发式策略。

    在这里插入图片描述
    10.接收方启发式策略
    ①接收端避免糊涂窗口综合症的策略:

    a.通告零窗口之后,仅当窗口大小显著增加之后才发送更新的窗口通告;
    b.什么是显著增加:窗口大小达到缓存空间的一半或者一个MSS,取两者的较小值。

    ②TCP执行该策略的做法:

    a.当窗口大小不满足以上策略时,推迟发送确认(但最多推迟500ms,且至少每隔一个报文段使用正常方式进行确认),寄希望于推迟间隔内有更多数据被消费;
    b.仅当窗口大小满足以上策略时,才通告新的窗口大小。

    11.发送方启发式策略
    ①发送方避免糊涂窗口综合症的策略:

    发送方应集聚足够多的数据再发送,以防止发送太短的报文段。

    ②问题:发送方应等待多长时间?

    a.若等待时间不够,报文段会太短;
    b.若等待时间过久,应用程序的时延会太长;
    c.更重要的是,TCP不知道应用程序会不会在最近的将来生成更多的数据。

    12.小结
    ①TCP接收端

    a.使用显式的窗口通告,告知发送方可用的缓存空间大小;
    b.在接收窗口较小时,推迟发送确认;
    c.仅当接收窗口显著增加时,通告新的窗口大小。

    ②TCP发送端

    a.确定发送时机;
    b.使用接收窗口限制发送的数据量,已发送未确认的字节数不超过接收窗口的大小。

    五.TCP连接管理

    1.建立TCP连接
    建立一条TCP连接需要确定两件事:

    a.双方都同意建立连接(知晓另一方想建立连接);
    b.初始化连接参数(序号,MSS等)。

    2.两次握手建立连接
    ①在一个不可靠的网络中,总会有一些意外发生:

    a.包传输延迟变化很大;
    b.存在重传的报文段;
    c.存在报文重排序。

    ②两次握手失败的情况:
    在这里插入图片描述

    3.TCP三次握手建立连接
    在这里插入图片描述
    ①客户TCP发送SYN报文段(SYN=1,ACK=0)

    a.给出客户选择的起始序号x;
    b.不包含数据。

    ②服务器TCP发送SYNACK报文段(SYN=1,ACK=1)(服务器端分配缓存和变量)

    a.给出服务器选择的起始序号y;
    b.确认客户的起始序号x+1;
    c.不包含数据。

    ③客户发送ACK报文段(SYN=0,ACK=1)(客户端分配缓存和变量)

    a.确认服务器的起始序号y+1;
    b.可能包含数据。

    4.如何选择起始序号
    ①为什么起始序号不从0开始?

    若在不同的时间、在同一对套接字之间建立了连接,则新、旧连接上的序号有重叠,旧连接上重传的报文段会被误以为是新连接上的报文段。

    ②可以随机选取起始序号吗?

    若在不同的时间、在同一对套接字之间建立了连接,且新、旧连接上选择的起始序号x和y相差不大,那么新、旧连接上传输的序号仍然可能重叠。

    ③结论:必须避免新、旧连接上的序号产生重叠。

    5.TCP起始序号的选择
    ①基于始终的起始序号选取算法

    a.每个主机使用一个时钟,以二进制计数器的形式工作,每隔△T时间计数器加1;
    b.新建一个连接时,以本地计数器值的最低32位作为起始序号;
    c.该方法确保连接的起始序号随时间单调增长。

    ②△T取较小的值(4微秒)

    确保发送序号的增长速度,不会超过起始序号的增长速度。

    ③使用较长的字节序号(32位):

    确保序号回绕的时间远大于分组在网络中的最长寿命。

    6.关闭TCP连接
    在这里插入图片描述
    7.客户/服务器经历的TCP状态序列
    在这里插入图片描述
    8.SYN洪泛攻击
    ①TCP实现的问题

    a.服务器在收到SYN段后,发送SYNACK段,分配资源;
    b.若未收到ACK段,服务器超时后重发SYNACK段;
    c.服务器等待一段时间(称SYN超时)后丢弃未完成的连接,SYN超时的典型值为30~120秒。

    ②SYN洪泛攻击:

    a.攻击者采用伪造的源IP地址,向服务器发送大量的SYN段,却不发送ACK段;
    b.服务器为维护一个巨大的半连接表耗尽资源,导致无法处理正常客户的连接请求,表现为服务器停止服务DoS。

    9.TCP端口扫描
    ①TCP端口扫描的原理:

    a.扫描程序依次与目标机器的各个端口建立TCP连接;
    b.根据获得的响应来收集目标机器信息。

    ②在典型的TCP端口扫描过程中,发送端向目标端口发送SYN报文段:

    a.若收到SYNACK段,表明目标端口上有服务在运行;
    b.若收到RST段,表明目标端口上没有服务在运行;
    c.若什么也没收到,表明路径上有防火墙,有些防火墙会丢弃来自外网的SYN报文段。

    ③FIN扫描试图绕过防火墙,发送端向目标端口发送FIN报文段:

    a.若收到ACK=1、RST=1的TCP段,表明目标端口上没有服务在监听;
    b.若没有响应,表明有服务在监听(RFC973的规定);
    c.有些系统的实现不符合RFC973规定,如在Microsoft的TCP实现中,总是返回ACK=1、RST=1的TCP段。

    展开全文
  • TCP协议保证数据传输可靠性的方式

    千次阅读 2020-08-07 19:46:01
    TCP协议保证数据传输可靠性的方式主要有: 校验和 序列号 确认应答 超时重传 连接管理 流量控制 拥塞控制 ###1、检验和 TCP检验和的计算与UDP一样,在计算时要加上12byte的伪首部,检验范围包括TCP首部及数据部分,...

    微信公众号:二进制人生
    专注于嵌入式linux开发。
    更新:2020/05/20,内容整理自网络。

    TCP协议保证数据传输可靠性的方式主要有:

    校验和
    序列号
    确认应答
    超时重传
    连接管理
    流量控制
    拥塞控制

    ###1、检验和
    TCP检验和的计算与UDP一样,在计算时要加上12byte的伪首部,检验范围包括TCP首部及数据部分,但是UDP的检验和字段为可选的,而TCP中是必须有的。

    ###2、序列号
    TCP将每个字节的数据都进行了编号,这就是序列号。

    序列号的作用:
    数据传输过程中的确认应答处理、重发控制以及重复控制等功能都可以通过序列号来实现。

    • TCP在发送数据时,并不是按顺序发送的,发送出去的数据包也不能保证按序到达(网络的不确定性)。接收端接收到数据之后,按序号排序,如果中间某个数据报丢失了,之后的数据报还是会被接收,但是不会对发送端返回之后的确认,而是会重复发送对丢失出之前的数据确认,保证发送端会对丢失的数据段进行重发。保证数据的按序组装

    • TCP规定,在确认报文里,若确认号=N,意思是告诉发送者,到序号N-1为止的所有数据都已经正确的收到,下次你从N开始发送

    • 建立连接时,双方发送的SYN报文和ACK报文段都是不携带数据的,但是会消耗一个序号,这个序号通常是随机值

    • TCP规定,首部中序号字段的值是本报文段发送数据的第一个字节的序号。

    ###3、确认应答机制(ACK)
    TCP通过确认应答机制实现可靠的数据传输。在TCP的首部中有一个标志位——ACK,此标志位表示确认号是否有效。接收方对于按序到达的数据会进行确认,当标志位ACK=1时确认首部的确认字段有效。进行确认时,确认字段值表示这个值之前的数据都已经按序到达了。而发送方如果收到了已发送的数据的确认报文,则继续传输下一部分数据;而如果等待了一定时间还没有收到确认报文就会启动重传机制。

    正常情况下的应答机制:

    ###4、超时重传机制
    当报文发出后在一定的时间内未收到接收方的确认,发送方就会进行重传(通常是在发出报文段后设定一个定时器,到点了还没有收到应答则进行重传),其基本过程如下:

    当然,未收到确认不一定就是发送的数据包丢了,还可能是确认的ACK丢了:

    当接收方接收到重复的数据时就将其丢掉,重新发送ACK。而要识别出重复的数据,就要用到前面提到的序列号了,利用序列号很容易就可以做到去重的效果。

    重传时间的确定:报文段发出到收到应答中间有一个报文段的往返时间RTT,显然超时重传时间RTO会略大于这个RTT,TCP会根据网络情况动态的计算RTT,即RTO是不断变化的。在Linux中,超时以500ms为单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。其规律为:如果重发一次仍得不到应答,就等待2500ms后再进行重传,如果仍然得不到应答就等待4500ms后重传,依次类推,以指数形式递增,重传次数累计到一定次数后,TCP认为网络或对端主机出现异常,就会强行关闭连接。

    #####超时重传的过程:

    1. 放置片段到重传队列中,启动计时器:TCP在发送包含数据的片段后,片段都会被复制一份并放在重传队列中,然后启动计时器。

    2. 确认处理:如果在计时器超时之前收到确认信息,就把该片段从重传队列中移除

    3. 超时重传:如果在计时器超时之前没有收到确认信息,则相应片段被重新发送给对方,即重传机制,但是TCP也不能保证重传报文的可靠性,所以该报文依然会处于重传队列中,并重新计时,如果还是超时,则重复这一动作,而且超时时间会设置的较之前长,但是TCP只会重传一定数量的次数,因此当超过这个次数时,TCP会检查故障并断开连接

    4. 这个等待的时间被称为RTO,RTO也是根据RTT(传输往返时间)来确定的,也和当时网络的状态有关系,需要通过具体算法实现,不是确定值

    如果超时时间设置的太长,会影响整体的重传效率
    如果超时时间设置的太短,会频繁发送很多重复的包

    1. 去重:当主机B的确认报文丢失时,主机A没有收到相应的确认报文,就会重传,主机B会收到重复的报文,TCP会根据报文中的序列号来移除重复收到的报文。

    ###5、连接管理机制
    连接管理机制即TCP建立连接时的三次握手和断开连接时的四次挥手。
    ####首先三次握手:


    建立过程为:
    (1)B首先建立传输控制块TCB,进入LISTEN(收听)状态,等待用户的连接请求。如有,则建立连接。(这个过程在套接字编程中为服务器端调用socket函数、bind函数和listen函数的过程)

    (2)A建立传输控制块TCB,然后向B发送连接请求报文段,报文段中首部的同步位SYN=1,同时选择一个序列号seq=x(实际测试是0),TCP规定SYN报文段不携带数据,但要消耗一个序列号。然后A进入SYN-SENT(同步已发送)状态。(这个过程在套接字编程中为客户端调用socket函数和connect函数的过程)

    (3)B收到请求后,如同意建立连接,就向A发送确认报文段。此时SYN=1、ACK=1,确认号ack=x+1,同时选择一个序列号seq=y,这个报文也不携带数据,但要消耗一个序列号。然后B进入SYN-RCVD状态(同步收到)。

    (4)A收到B的确认后,还要向B发送确认。确认报文段的ACK=1,确认号ack=y+1,seq=x+1。TCP规定,ACK报文段可以携带数据,而如果不携带数据则不消耗序列号,此时下一个报文段的序列号仍为seq=x+1。这时,连接就建立成功了,A进入ESTABLISHED状态(已建立连接状态)。

    (5)当B收到A的确认后,也进入ESTABLISHED状态,此时就可以进行数据传输了。

    当然,在进行三次握手时不是仅进行连接,可能还会进行一些后续操作所需要的信息交流。

    ######为何是三次握手而非两次?
    三次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。

    现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,假如S发给C的应答丢失了,C将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。

    C由于长时间收不到S的回应,再次发起连接请求,S收到了请求,给C发送应答,双方建立了连接。过了一段时间,C最开始的那个连接请求抵达了,那么S又该如何处理?可见两次握手有很多问题。

    ####四次挥手:

    在连接释放时,连接的两方都要同意才能够释放成功(就像情侣分手一样,分手时两个人的事儿)。连接的双方都可以提出释放连接,这里假设A先提出释放连接,首先双方都处于ESTABLISHED状态。

    (1)当A的数据传送完后,就可以向其TCP发起连接释放了,此后停止再发送数据,主动关闭TCP连接。首先A向B发送一个FIN报文段,报文段首部FIN=1,序列号seq=u(u为最后传送的数据的序列号加1),然后A进入FIN-WAIT-1(终止等待1)状态。FIN报文段不能携带数据,但要消耗一个序列号。

    (2)B收到释放连接的报文段后即发出确认报文段,报文首部ACK=1,ack=u+1,seq=v(v等于B前面传送过的数据的序列号加1),然后B进入CLOSE-WAIT(关闭等待)状态。这时从A到B这个方向的连接就释放了,TCP连接就处于半关闭状态。(注意:此后A不能主动向B发送数据,但是A可以给B发送确认报文段,也就是说A仍要接收来自B的报文,因为从B到A这个方向的连接还没有关闭)

    (3)当A收到B的确认报文后,就进入FIN-WAIT-2(终止等待2)状态,等待B发出的连接释放报文段。

    (4)当B的数据发送完毕后,其应用进程就通知TCP释放连接。B向A发送FIN报文,报文段首部FIN=1,ack=u+1(重复发送上一次已经发送过的确认号),seq=w(w为B最后发送报文段的序列号加1)。然后B进入LAST-ACK(最后确认)状态,等待A的确认。

    (5)A在接收到B的连接释放报文后,必须进行确认。A向B发送的确认报文段中报文首部ACK=1,ack=w+1,seq=u+1。然后A进入TIME-WAIT(时间等待)状态(如果无差错,此状态时间为2MSL),注意,此时TCP连接还没有释放掉,必须经过TIME-WAIT设置的时间2MSL后,A撤销相应的传输控制块TCB,才进入CLOSED状态,结束了此次TCP连接。MSL叫做最长报文段寿命,RFC793建议设为2分钟,但在现在实际网络情况中,常用值有三种:30秒,1分钟,2分钟。必须要在A进入CLOSED状态后才能开始建立下一个新的连接。

    (6)B收到A的确认报文后,也进入CLOSED状态,撤销相应的传输控制块TCB,此时,TCP连接全部断开。

    这样TCP四次挥手完成。

    ####TIME-WAIT存在的必要性:
    #####(1)可靠地实现了TCP全双工连接的终止
    第一是为了保证最后一个的ACK报文能到达B。这个ACK报文有可能丢失,因而使得处在LAST_ACK状态的B得不到对已发送的FIN+ACK报文的确认,B会超时重传这个FIN+ACk ,而A就能在这TIME_WAIT时间(2MSL)里收到这个重传的报文,A就可以重传一次确认,如果没有这个TIME_WAIT, 那B重传的FIN_ACK,可A早就走了,自然不会再重发确认,这样B就无法按照正常步骤进入CLOSE 状态。

    另一种解释说词是(本质是一样的):
    我们知道,TCP是比较可靠的。当TCP向另一端发送数据时,他要求对端返回一个确认(如同我们关闭时候的FIN和ACK)。如果没有收到确认,则会重发。

    回忆一下我们最终的那个FIN与ACK,被动关闭方发送FIN,并等待主动关闭方返回的ACK。我们假设最终的ACK丢失,被动关闭方将需要重新发送它的最终那个FIN,主动关闭方必须维护状态信息(TIME_WAIT),以允许它重发最终的那个ACK。

    如果没有了这个状态,当他第二次收到FIN时,会响应一个RST(也是一种类型的TCP分节),会被服务器解释成一个错误。

    为了TCP打算执行必要的工作以彻底终止某个连接两个方向上的数据流(即全双工关闭),那么他必须要正确处理连接终止四个分节中任何一个分节丢失的情况。

    #####(2)允许老的重复分节在网络中的消逝(为什么需要2MSL)
    首先,存在这样的情况,某个路由器崩溃或者两个路由器之间的某个链接断开时,路由协议需要花费数秒到数分钟的时间才能稳定找出另一条通路。在这段时间内,可能发生路由循环(路由器A把分组发送给B,B又发送回给A),这种情况我们称之为迷途。假设迷途的分组是一个TCP分节,在迷途期间,发送端TCP超时并重传该分组,重传分组通过某路径到达目的地,而后不久(最多MSL秒)路由循环修复,早先迷失在这个循环中的分组最终也被送到目的地。这种分组被称之为重复分组或者漫游的重复分组,TCP必须要正确处理这些重复的分组。

    我们假设ip1:port1和ip2:port2 之间有一个TCP连接。我们关闭了这个链接,过一段时间后在相同IP和端口之间建立了另一个连接。TCP必须防止来自之前那个连接的老的重复分组在新连接上出现。为了做到这一点,TCP将不复用处于TIME_WAIT状态的连接。2MSL的时间足以让某个方向上的分组存活MSL秒后被丢弃,另一个方向上的应答也最多存活MSL秒后被丢弃。

    ####聪明的你会发现谁先关闭谁就有一个TIME_WAIT的状态;
    在linux的网络编程中,如果服务器如果先关闭,你会发现,现在想要立马再次启动服务器,就会报错说这个端口号被占用着,那就是因为有这个TIME_WAIT,2msl的时间.那么怎么解决 ?
    解决:setsockopt()函数。设置地址重用,在这就不多说了。

    setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR,(const char*)&reuseFlag, sizeof(reuseFlag))
    

    备注:
    MSL
    MSL全称是maximum segment lifetime,最长分节生命期。MSL是任何IP数据报能够在因特网存活的最长时间。我们知道,这个时间是有限的,因为每个数据报都含有一个限跳(hop limit)的8位字段,它的最大值是255(简单的讲就是不同经过超过255个路由器)。尽管这个跳数限制而不是真正的时间限制,我们仍然假设最大限跳的分组在网络中存在的时间不可能超过MSL秒。
    主动关闭方
    跟握手不同,挥手可以由客户端发起,也可以是服务端发起。发起关闭的一端我们称之为主动关闭方,另一端称之为被动关闭方。

    ###6、流量控制
    接收端处理数据的速度是有限的,如果发送方发送数据的速度过快,导致接收端的缓冲区满,而发送方继续发送,就会造成丢包,继而引起丢包重传等一系列连锁反应。

    因此TCP支持根据接收端的处理能力,来决定发送端的发送速度,这个机制叫做流量控制。为了实现流量控制,引入了滑动窗口的概念。

    ####滑动窗口:

    • 窗口大小是指无需等待确认应答而可以继续发送数据的最大值
    • 在发送数据时并不是一次发送窗口的大小,而是将其分段,当第一段的数据收到一个ACK应答时,窗口就会向后滑动,继续发送下一段数据
    • 操作系统内核为了维护滑动窗口,开辟了一个发送缓冲区,用来记录当前还有哪些数据没有应,只要没有应答过的数据,都要在缓冲区内保存,只有确认应答过的数据才能删除!

    在TCP报文段首部中有一个16位窗口长度,当接收端接收到发送方的数据后,在应答报文ACK中就将自身缓冲区的剩余大小,放入16窗口大小中。这个大小随数据传输情况而变,窗口越大,网络吞吐量越高,而一旦接收方发现自身的缓冲区快满了,就将窗口设置为更小的值通知发送方。如果缓冲区满,就将窗口置为0,发送方收到后就不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端。

    注意:窗口大小不受16位窗口大小限制,在TCP首部40字节选项中还包含一个窗口扩大因子M,实际窗口大小是窗口字段的值*窗口扩大因子。

    ###7、拥塞控制
    流量控制解决了两台主机之间因传送速率而可能引起的丢包问题,在一方面保证了TCP数据传送的可靠性。然而如果网络非常拥堵,此时再发送数据就会加重网络负担,那么发送的数据段很可能超过了最大生存时间也没有到达接收方,就会产生丢包问题。

    为此TCP引入慢启动机制,先发出少量数据,就像探路一样,先摸清当前的网络拥堵状态后,再决定按照多大的速度传送数据。

    #####此处引入一个拥塞窗口的概念:
    发送开始时定义拥塞窗口个数为1;
    每次收到一个ACK应答,拥塞窗口加1;
    而在每次发送数据时,发送窗口取拥塞窗口与接送端接收窗口最小者。

    #####慢启动、乘法减小、快恢复:

    • (1)设置一个慢启动的阈值,在启动初期以指数增长方式增长(慢启动并非说增长速度慢,而是说起点低);
    • (2)当以指数增长达到阈值时就停止指数增长,按照线性增长方式增加;
    • (3)线性增长达到网络拥塞时立即“乘法减小”,拥塞窗口置回达到网络拥塞时的一半,同时新一轮的阈值变为原来的一半,窗口按照线性增长方式增加。

    #####还有一个快重传的概念:

    当10012000这段报文段丢失后,发送端就会以指收到1001的ACK,它告诉发送端,我收到了11000的数据报,我想要1001,但是后面的报文段它也会接收,却仍然发送丢失之前的应答。

    直到接收方连续三次收到同样一个“1001”这样的应答,就会将对应的数据“1001~2000”重新发送

    接收端收到10012000之后,就会直接返回ACK=7000(因为20017000接收端之前都收到过了,都放在了接收缓冲区中),这就是快重传。

    与快重传配合使用的还有快恢复算法,其过程有以下两点:

    • 当发送端连续收到三个重复确认,就执行“乘法减小”算法,把慢开始门限ssthresh减半,为了防止网络发送拥堵
    • 窗口减半之后,发送端现在认为网络很有可能没有发生拥堵,因此与慢开始不同之处是现在不执行慢开始算法,而是将cwnd的值设置为慢开始门限ssthresh减半后的数值,然后开始执行拥塞避免算法,使拥塞窗口缓慢地线性增大!

    整个机制可用图表示:


    每天进步一点点…
    喜欢的看官点个在看

    图 二进制人生公众号

    展开全文
  • TCP连接

    万次阅读 2018-05-24 15:48:55
    一条TCP连接唯一地被通信两端的两个端点所确定。那么,TCP连接的端点是什么呢?TCP连接的端点又叫套接字(socket),根据TCP协议的规定,端口号拼接到IP地址即构成了套接字,即套接字 socket = (IP地址:端口号)...
  • 传输控制协议---TCP协议【详解】

    千次阅读 2022-02-01 13:01:15
    TCP(Transmission Control Protocol)传输控制协议是种面向连接的、可靠的、基于字节流的传输层协议。TCP为了保证不发生丢包,就给每个字节个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收...
  • 1. TCP是基于连接的协议 传输控制协议(TCP,Transmission ...TCP连接的建立要经过三次握手,释放连接一般需要四次挥手。 2. 连接的建立 建立连接的过程一般需要三次握手,如下图所示: 在握手之前,主动打开连
  •     TCP通过三报文握手建立可靠地通信连接后,再完成了数据传输之后,会通过四报文挥手来释放连接。     假设TCP客户进程的应用进程通知其主动关闭TCP连接。     TCP客户进程会发送TCP连接释放报文段...
  • 文章目录1.TCP协议特点2.TCP报文段的首部格式3.TCP连接管理(1)连接的建立 - - - 三次握手(2)SYN泛洪攻击4.TCP连接释放----四次握手5.TCP连接建立和释放的总结如下 1.TCP协议特点 2.TCP报文段的首部格式 TCP...
  • TCP传输控制协议)

    千次阅读 2019-02-27 19:41:28
    传输控制协议(Transmission Control Protocol,TCP)是种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。 在因特网协议族(Internet protocol suite)中,TCP层是位于IP层之上,应用层之...
  • 传输控制协议TCP

    千次阅读 2022-03-16 10:20:39
    TCP(Transfer Control Protocol)传输控制协议,是...2、TCP连接只能有两个端点,每一条TCP连接只能点对点。 3、TCP提供可靠交付的服务。通过TCP连接传输数据,无差错、不丢失、不重复,并且按序到达。 4、TCP提供全双
  • 在讲述TCP三次握手,即建立TCP连接的过程之前,需要先介绍一下TCP协议的包结构。 TCP协议包结构: 这里只对涉及到三次握手过程的字段做解释 (1) 序号(Sequence number) 我们通过 TCP 协议将数据发送给对方,就...
  • 传输层协议 ——— TCP协议

    千次阅读 多人点赞 2022-06-11 14:58:17
    TCP全称为“传输控制协议(Transmission Control Protocol)”,TCP协议是当今互联网当中使用最为广泛的传输层协议,没有之TCP协议被广泛应用,其根本原因就是提供了详尽的可靠性保证,基于TCP的上层应用非常多...
  • TCP通信的每一方需要执行的系列行为。这些行为由特定算法规定,用于防止网络因为大规模的通信负载而瘫痪。 发送和接收方认为网络即将进人拥塞状态(或者已经由于拥塞而出现路由器丢包情况)时减缓TCP传输TCP...
  • 网络篇-传输控制协议TCP

    千次阅读 2022-02-28 22:00:11
    传输控制协议(TCP,Transmission Control Protocol)用句话概括的话,它是种面向连接的、可靠的、基于字节流的传输层通信协议。 TCP传输层)是位于网络层之上,应用层之下的中间层,不同的主机的应用层之间...
  • TCP连接管理

    万次阅读 2016-08-02 17:05:37
    一条TCP连接唯一地被通信两端的两个端点所确定。那么,TCP连接的端点是什么呢?TCP连接的端点又叫套接字(socket),根据TCP协议的规定,端口号拼接到IP地址即构成了套接字,即 套接字 socket = (IP地址:端口号...
  • 目录 攻击方法1:SYN 泛洪 攻击方法2:影响路径...服务器会为每一条TCP连接分配一定数量的连接资源。由于连接尚未完全建立,服务器为了维护大量的半打开连接会在耗尽自身内存后拒绝为后续的合法连接请求服务。 ...
  • 也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保 证可靠交付 3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的 UDP没有拥塞控制,...
  • TCP连接和断开过程详解

    千次阅读 2017-10-09 17:40:42
    TCP报文简介 TCP报文格式  TCP/IP协议的详细信息参看《TCP/IP协议详解》三卷本。下面是TCP报文格式图: 图1 TCP报文格式  上图中有几个字段需要重点介绍下:  (1)序号:Seq序号,占32位,用来标识从TCP源...
  • TCP 可靠传输机制详解

    万次阅读 多人点赞 2019-08-31 20:23:44
    TCP可靠传输 TCP流量控制 TCP拥塞控制 面试相关问题 前言 本篇博文主要是为了复习TCP协议而做的总结。其中很多内容都是来自于《计算机网络》,《Linux网络编程》,《TCP/IP详解》等书籍。首先可以从TCP协议思维...
  • TCP连接建立及释放连接过程

    千次阅读 2020-03-24 10:30:17
    (1)序号:Seq序号,占位32位,用来表示从TCP源端发送的字节流,发送方发送数据时对此进行标记。 (2)确认序号:Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。 (3)标志位:共6个,即...
  • TCP协议的连接建立过程和释放过程简单介绍

    万次阅读 多人点赞 2019-01-22 11:44:24
     计算机网络协议众多,TCP协议只是其中的一个。因为它的可靠性,很多数据传输都使用TCP协议,它也是面试时候考察的重点,所以有必要把学习到的知识点记录下来。  TCP协议的主要特点 ... 2、每一条TCP连接只...
  • TCP连接过程(3次握手)

    千次阅读 2018-12-17 09:01:56
    TCP连接过程(3次握手)   TCP连接要解决的三个问题: 要使通讯的每一方能够感知对方的存在。![在这里插入图片描述]0) 要允许双方协商一些参数包括:窗口的最大值、能否使用窗口扩大选项和时间戳选项及服务...
  • TCP建立连接三次握手和释放连接四次握手

    万次阅读 多人点赞 2016-09-14 14:55:09
    TCP建立连接三次握手和释放连接四次握手  【尊重原创,转载请注明出处】http://blog.csdn.net/guyuealian/article/details/52525724  在谈及TCP建立连接和释放连接过程,先来简单认识一下TCP报文段首部格式...
  • TCP的可靠传输机制

    万次阅读 多人点赞 2018-11-01 20:06:30
    TCP的报文是交给IP层传送的,但是IP层只能提供尽最大努力交付的服务,也就是说,TCP下面的网络所提供的是不可靠传输,其实就是传输信道是不可靠的(所谓的信道,就是指连接信号发送方和接收方的传输线路,包括双绞...
  • 1、TCP状态了解TCP之前,先了解几个命令:linux查看tcp的状态命令:1) netstat -nat 查看TCP各个状态的数量2)lsof -i:port 可以检测到打开套接字的状况3) sar -n SOCK 查看tcp创建的连接数4) tcpdump -iany tcp port...
  • 可靠连接TCP协议全解析

    千次阅读 多人点赞 2021-07-11 09:29:18
    文章目录、前言二、TCP报文结构三、TCP连接(重点:三次握手和四次挥手)3.1 三次握手建立连接3.2 四次挥手释放连接3.3 wireshark演示四、TCP可靠连接4.1 序号4.2 确认4.3 重传4.3.1 超时重传4.3.2 冗余确认(快速重...
  • [计算机网络]个完整的TCP连接

    千次阅读 2018-07-17 15:58:02
     为了实现可靠的数据传输TCP要在应用进程之间建立传输连接。它是在两个传输用户之间建立种逻辑联系,使得通信双方都确认对方为自己的传输端点。 建立连接:  建立连接前,服务器端首先被动打开其熟知的端口...
  • 本博文将讨论如何优雅断开互相连接的套接字。之前用的方法不够优雅,是因为,我们是调用 close()函数单方面断开连接的。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 38,044
精华内容 15,217
关键字:

一条tcp连接的数据传输方向