《计算机网络-自顶向下的方法》阅读笔记-第三章-运输层

写在前面

计算机网络分了五层(应用层,运输层,网络层,链路层,物理层),《自顶向下》的第三章详细讲解了运输层的两大协议以及其背后原理,其中tcp协议是本章乃至整个计算机网络的一个重点内容,鉴于此,我觉得有必要对本章作个笔记,以巩固运输层的知识。

正文

运输层的目的在于为不同主机(host)上的应用或者说进程提供逻辑通信——通过运输层协议,两台电脑仿佛直接相连一样。应用无需知道底层内部实现的原理和细节,比如怎么把远隔世界两地电脑上的数据进行相互传输。

本章的一些注意点:

  • 运输层协议仅仅实现在网络的边缘处,例如主机,电脑,手机等。如路由器,交换机这些网络核心设备,是没有实现运输层协议的
  • 每一层协议仅仅检查分组相应的协议层字段
  • 最常用的两种输入层协议,tcp和udp
  • 运输层的下面是网络层,网络层的目的在于为不同的主机提供逻辑通信,而非主机上的进程
  • 网络层常用协议是IP,其提供的是一种不可靠的数据传输服务
  • 为了做到为不同主机(host)上的应用或者说进程提供逻辑通信这一目的,运输层协议必须能分辨出数据的来源和去向。为此,运输层存在着两种行为,多路复用和多路分解

多路复用

当运输层收到来自上方应用层的数据时,运输层会为数据封上一些头部信息,根据所有协议不同,封上的信息也不一样。这一行为,被称为多路复用。

多路分解

当运输层收到下方网络层传输来的数据时,运输层会检查多路复用时封上的信息,从而正确的把数据定向到相应的进程。

如何使用运输层的协议?
操作系统提供了被称为socket的接口api供编程人员调用,对socket的形象理解是其是一种抽象,将复杂的实现(tcp, udp)协议的各种行为抽形成简单的几个函数给开发人员使用。就像浏览器将发送请求报文这一http协议规定的行为,抽象成我们只需要输入url然后回车即可。

这里需要注意的一点是,在一般情况下,一个计算机端口只能被一个进程占用。一个进程可以创建多个socket,多个tcp socket可以监听同一个端口,并保证接受的数据依旧是正确的。但多个udp socket就无法监听同一端口。这其中的差异源于tcp和udp协议的不同,tcp是面向连接的,其有足够状态的信息来分辨数据来源,后定向到正确的socket。但udp不需要维持连接,仅仅通过端口号来决定数据的去向,所以会导致冲突。

udp的多路复用和分解

一个udp socket通过一个二元组(目的ip地址,目的端口号)来标识,当输入层收到数据时,通过检查这个二元组,来定向数据该去往哪一个udp socket。这也是为什么多个udp socket无法监听同一个端口的原因,因为系统无法再根据二元组来确定消息的去向(它们的二元组是一样的)。

TCP的多路复用分解

一个tcp socket通过一个四元组(源ip,源端口,目的ip,目的端口号)来标识,这也解释为什么多个tcp socket可以监听同一个端口,尽管目的ip和目的端口号是一样的,但是源ip和源端口的组合总是不同的。

UDP

相比于TCP来讲,UDP是一个简单的协议,就是把网络层IP提供的服务封装了下,实现了多路复用和分解,提供了端到端进程间的通信和错误检验服务。

相对于TCP来说:

  • UDP是不可靠的传输服务
  • 没有流量和拥塞控制

但:

  • 能够够精细的控制数据的发送时间和速率
  • 无需事先建立连接
  • 无连接状态
  • 分组首部开销小

封装

在接收到应用层传输来数据后,在数据前方加上了四个字段,分别是源端口号、目的端口号、长度(包括头部)、检验和,应用数据。

可靠数据传输

数据传输可能遇到的问题:

  • 传输中数据被损坏
  • 数据丢失
  • 数据可能乱序到达

解决方法:

  • 检验和
  • 序号
  • 定时器
  • 肯定和否定反馈分组

如何在保证可靠性的前提下,提高其性能?

  • 通过引入流水线(pipelining)技术

引入流水线导致了:

  • 序号范围需要增加
  • 收发双方可能需要缓存乱序到达的分组
  • 以上两个的具体实现取决于传输协议如何处理分组丢失、损坏的问题(是选择回退N步,还是选择重传)

回退N步

其核心在于,发送方会维持一个窗口,发送方能发送的数据量取决于窗口长度,并且当丢失时会重送所有未确认的分组。

接收方会丢弃乱序到达的缓存

特点:

  • 累计性ACK
  • 单一定时器

选择重传

核心在于,收发双方都会维持一个窗口,并且尽力保证窗口的状态是同步的,因此当分包丢失时,发送方只会重送丢失的分组。

接收方会缓存乱序到达的分组

特点:

  • 独立性ACK
  • 多个定时器

TCP

特点:

  • 面向连接
  • 全双工的
  • 点对点,不存在一次发送将数据传递给多个接收方、
  • 在合适的时候发送 发送缓存 里的数据
  • 为每个数据封上一个TCP头部
  • TCP连接的每一端都具有发送缓存和接受缓存

报文段结构

  • 源端口号
  • 目的端口号
  • 序号(seq) - 所带数据的第一个比特的序号,同时也是接收方期待的序号,等于接收方回复报文中的ACK(n)中的n

  • 确认号(ACK) - 对于一个ACK(n)来说,告诉对方n-1前的数据已经收到,下一次期待的序列号为n

  • 首部长度

  • URG
  • ACK - 指示,用于指示报文中确认号字段的值是有效的
  • PSH - 指示,立即发送发送缓存里的数据
  • RST - 指示,用于强制关闭连接
  • SYN - 指示,用于握手阶段也就是建立连接的阶段
  • FIN - 指示,用于正常关闭连接
  • 接受窗口 - 用于TCP的流量控制功能
  • 因特网检验和
  • 紧急数据指针
  • 选项
  • 应用数据

可靠数据传输

  • TCP协议为数据的每一Byte都编号,而非针对报文段
  • 总是维持最老未经确认的1 Byte的计时器
  • 每一次超时重置的计时器时间会加倍
  • 其错误恢复机制是回退N步和选择重传的混合体
    • 不会丢弃乱序到达的分组,而是缓存起来
    • 采用累计性ACK
    • 只会重传丢失报文段中的数据

快速重传

当连续收到4个相同的ACK时(一个正常的ACK,三个正常ACK的重复),会触发快速重传,立即重发分组

流量控制

为了防止过高数据流量导致接收者的接受缓存爆掉,接收者会在其tcp报文中通过 接受窗口 指示发收者还能发送多少数据

接受窗口(rwnd)公式:

  • rwnd = RcvBuffer - [LastByteRead - LastbyteRead]
  • 且:LastByteSent - LastByteAcked <= rwnd

TCP连接管理

建立连接(三次握手)

  1. 客户端发送SYN位置1的报文段
  2. 服务端返回SYN为1,ACK为1的报文段
  3. 客户端发送ACK为1,且附带数据的报文段

断掉连接

  1. 客户发送FIN为1的报文段
  2. 服务端返回ACK为1的报文段
  3. 服务端发送FIN为1的报文段
  4. 客户端返回ACK为1的报文段
  5. 客户端在一段时间后,关闭连接

拥塞控制

拥塞的代价

  • 导致分组过长的排队时延
  • 需要重传因缓存溢出丢失的分组
  • 高延时导致重送分组
  • 丢包导致运输相关分组的分组交换器所作的工作全部白费

TCP的拥塞控制

TCP采用端到端的拥塞控制

三个主要问题:

  1. 一个TCP的发送方如何限制自己的发送流量的速率?

通过设置一个拥塞窗口(cwnd),并且保证:LastByteSent - LastByteAcked <= min{cwnd, rwnd}

  1. 如何感知其发送路径拥塞了?
  • timeout
  • 收到一次正常ACK后连续收到三次正常ACK的重复
  1. 感到拥塞时,采用什么样的算法改变发送速率?
  • 慢启动

cwnd的值从1 MSS开始,并且对每一个ACK,cwnd值变为原来的2倍,直到超过阈值(ssthresh),转为拥塞避免模式

  • 拥塞避免

在每一个RRT时间,cwnd的值增加一个MSS

  • 快速恢复

cwnd的值降为一半加上重复收到的重复ACK的数量,并且每一个ACK,cwnd的值增加一个MSS

在实践中,一旦timeout就会会到慢启动的状态,多次重复ACK则会进入快速恢复状态

TCP公平

TCP的公平性在于保证每个连接的吞吐量是平均的,而不是应用或进程间。