TCP原理学习笔记

Mar 26, 2016


TCP概述

TCP特点

  • 面向连接: 在连接的基础上双方才可以通信
  • 端到端: 通信只有两个端点
  • 可靠交付: 传输什么样的数据, 就接受什么样的数据
  • 全双工通信: 通信是双向的, 而且不会相互干扰
  • 面向字节流: 数据以字节传输, 表明TCP不知道数据的含义

TCP连接

什么是TCP连接? TCP连接就是一个抽象表示, 指出了通信双方的身份.

通信中的一方我们用套接字(socket)表示

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

而且我们知道TCP连接是端到端的, 所以TCP连接可以这样表示:

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

TCP报文段

TCP报文段的组成很简单, 只有首部和数据, 表示如下:

TCP报文段 ::= TCP首部 + 数据部分

TCP首部信息如下图所示(注:数字表示位数): TCP首部信息

首部信息中各字段解释如下:

  • 源端口: 发送方port, 可知端口范围为0~65535
  • 目的端口: 接收方port, 可知端口范围为0~65535
  • 序号: 报文段的编号, 可知序号范围0~2^32^-1, 由于TCP是面向字节流的,所以一个序号表示一个字节
  • 确认号: 告知对方自己期望的下一个报文段编号
  • 数据偏移: TCP首部长度, 表示到达数据部分的偏移量, 范围为0~15, 所以首部长度最多为60字节
  • 保留: 未定义
  • URG: 紧急位, 用于设置紧急指针
  • ACK: 确认位, 用于设置确认号
  • PSH: 推送位, 告知TCP数据发送
  • RST: 重置位, 告知对方TCP出现错误,需要重新连接
  • SYN: 同步位, 建立连接的标志
  • FIN: 结束位, 释放连接的标志
  • 窗口: 告知对方自己的接受窗口
  • 检验和: 检验TCP报文段的有效性, 包括TCP首部和数据部分
  • 紧急指针: URG置1有效, 定位紧急数据
  • 选项(长度可变): 用于扩展TCP首部信息
  • 填充: 填充TCP首部格式使得满足以4字节为单位

TCP报文段的规则中定义了MSS(Maximum Segment Size:最大报文段长度), 表示TCP报文段中数据部分的最大长度, 默认值是536, 所以因特网中的所有主机都应能够接受536+20=556字节的最大报文段


TCP可靠传输

可靠传输有三个方面的含义:

  • 无差错: 数据传输之前之后是一样的
  • 不丢失: 发送方发送的数据, 接收方一定可以接收到
  • 不重复: 发送方发送的数据, 接收方不会重复接受

滑动窗口

滑动窗口是基于字节流的, 所以滑动的单位为1字节, 其结构如下图所示:

滑动窗口结构图

说明如下:

  • Category #1: 已发送, 已确认
  • Category #2: 已发送, 未确认
  • Category #3: 未发送, 可发送
  • Category #4: 未发送, 不可发送

需要注意的细节问题:

  • 发送窗口可能要比接受窗口小, 因为发送方要考虑网络拥塞问题
  • 对于不按序到达的数据如何处理, TCP标准没有说明. 但是考虑网络资源的珍贵性, 一般情况下接收方都会接受下来暂时保存着
  • 接收方必须要有累计确认的功能, 因为这样可以很好的减小开销

超时重传

超时重传是为了保证可靠传输, 其实现涉及两个东西: RTT(Round-Time Trip)和RTO(Retransmission Time-Out).

  • RTT: 报文段往返时间
  • RTO: 超时重传时间, 略大于RTT

一个报文段发送出去, 如果过了RTO时间还没有接收到确认, 那么就需要重传报文段

我们知道网络情况是动态的, 有时候网络良好, 有时候网络拥塞, 那么RTT就应该也是动态变化的,RTO自然而然也就跟着RTT动态变化. 这个动态变化就需要设计一个算法来计算, 我们这里就简单讲一下3个算法.

  • 自适应算法:

      RTTs = (1-a)*(旧RTTs) + a*(新RTT样本)
      RTO = RTTs + 4*RTTD (注:RTTD表示RTT偏差加权平均值)
    
  • Karn算法: 在自适应算法基础上, 提出只要报文段重传, 那么就不采用其样本RTT
  • Karn修正算法: 在Karn算法基础上, 剔除只要报文段重传, 那么RTO就增大一点

选择确认

SAK(Selective ACK)选择确认是指在发生数据未按序到达的时候, 接收方告诉发送方自己接收了哪些数据, 从而发送发可以决定重发丢失的数据, 而不是全部重新重发, 使得网络资源得到更好的利用

SAK的实现原理是借助TCP首部信息中的选项(可选长度)这一部分. 我们知道一个连续的字节段可以由头序号和尾序号表示, 一个序号需要4个字节, 所以一个字节段需要8个字节表示, 那么我们是不是一次告诉发送方5个字节段呢?(首部最长60字节,20字节是固定的,所以有40字节/8字节=5). 然而并没有, 因为还需要2个字节, 分别表明使用SAK选项和选项长度, 所以只有40-2=38个字节可用, 所以一次最多告诉发送方4个字节段


TCP流量控制

所谓流量控制, 就是接收方让发送方不要发送太多的数据, 好让接收方来得及接受, 过程如下图所示:

TCP流量控制

从上图可以看出, 接收方一共进行了3次流量控制

注意: 现在考虑一种情况, 在上图中, 接下来B处理完了缓冲区的数据, 那么B就会发送一个rwnd=400的报文段, 然而报文段却在中途丢失了, 那么A就一直等B的通知, 而B也一直等A发数据, 因为B认为自己已经通知A可以发送了, 这样就形成了一个死锁. 所以TCP为每一个连接都设置了一个持续计时器, 每当接收方得知发送方的发送窗口值为0的时候, 启动持续计时器, 时间一到接收方就发送一个探测报文段, 一直持续这个过程直到发送方的发送窗口不为0

TCP拥塞控制

所谓拥塞控制, 就是防止过多的数据注入到网络中, 使得网络中的路由器或链路不致过载

注意: 拥塞控制不同于流量控制, 拥塞控制是基于整个网络考虑, 而流量控制是基于通信双方考虑

拥塞控制原理

拥塞控制从大方面考虑可以分为两种: 开环控制闭环控制

开环控制是指在网络设计的时候把相关拥塞因素考虑进来, 一旦网络运行起来就不变更了; 闭环控制则是动态地考虑拥塞情况并做出控制, 我们这里讲一下闭环控制的原理

闭环控制是基于反馈环路的概念.属于闭环控制有以下几步

  1. 检测网络系统得知拥塞发生的时间地点
  2. 把拥塞信息发送到可以采取行动的地方
  3. 调整网络系统的运行以解决拥塞问题

拥塞控制方法

  • 慢开始(slow-start): 从发送数据开始, 拥塞窗口逐渐增大
  • 拥塞避免(congestion avoidance): 从感知到拥塞开始, 拥塞窗口缓慢增大
  • 快重传(fast retransmit): 从丢失报文段开始, 不断重复确认丢失报文段, 使得尽快重传报文段
  • 快恢复(fast recovery): 从发现快重传开始, 缩小一半拥塞窗口

一般情况下慢开始拥塞避免一起使用, 快重传快恢复一起使用

  • 慢开始拥塞避免

慢开始和拥塞避免

  • 快重传快恢复

快重传和快恢复

注:

  • ssthresh是指慢开始门限, 也就是慢开始算法切换掉的分割线
  • 慢开始算法的拥塞窗口起始值是1
  • 发送方窗口上限值 = Min [ rwnd, cwnd ]

TCP连接管理

建立连接

TCP建立连接

这里为什么需要三次握手, 而不是两次握手呢?

主要是从服务端角度考虑, 目的是为了不浪费服务端资源

考虑两次握手的情景, 客户端发送了请求连接(1), 但是延迟了, 于是客户端再发送一次请求连接(2), 并且完成了通信和释放了连接(2), 之后服务端收到了连接(1)并建立连接, 但是客户端已经不需要连接(1)了, 所以连接(1)会造成服务端网络资源的浪费

但是使用三次握手会引来另外一个问题: 客户端资源的浪费

主要是因为客户端发送第三次握手过后, 客户端就可以发送数据了, 但是连接是否建立成功还不一定, 所以就有可能造成客户端资源的浪费

释放连接

TCP建立连接

TCP连接建立和释放过程中, 每一步报文段分别发生丢失我们要怎么处理呢?

关键在于理解: 无论客户端还是服务端, 只要是状态变更的发起者, 那么就要负责重传服务

例如1: 在TCP连接建立过程中, 客户端从CLOSED到SYN-SENT, 状态发生变更发起者是客户端, 客户端就要负责失败重传; 服务端从LISTEN到SYN-RCVD, 状态发生变更发起者是服务器, 服务端要负责失败重传

例如2:在TCP连接释放过程中, 客户端从ESTABLISHED到FIN-WAIT1, 状态发生变更发起者是客户端, 客户端份负责重传; 服务端从CLOSE-WAIT到LAST-ACK, 状态发生变更发起者是服务端, 服务端负责重传


上一篇博客:Jekyll学习笔记
下一篇博客:回溯法原理学习笔记