计算机网络——传输层探究

前言

我们知道,在 TCP/IP 五层协议模型中,最上层是应用层,也就是我们的应用程序(例如我们的浏览器,通过应用层http协议收发数据),之后数据通过传输层下发到网络世界(网络层)当中去。而像网络层的设备和协议,例如路由器和IP协议,数据交付是不可靠的,会存在丢包。而本篇的主角——传输层,解决的正是这样一个问题:两个实体怎样才能在一种会丢失或损坏数据的媒体上可靠地通信

首先,有几个概念要理清一下:

  1. 传输层的协议是一种逻辑协议。想象一下,两台计算机的应用程序通过网路连接,就好像直接相连一样。这是一种逻辑连接,物理上,其背后可能连接了成千上万的路由器和交换机。实际上,网络层也是逻辑通信,只不过网络层是不同的主机与主机的逻辑通信,而传输层是不同主机上的进程与进程之间的逻辑通信
  2. 传输层协议在端系统(计算机、主机)中实现(通常是操作系统),而不是路由器。
  3. 传输层将应用程序的报文(message)分组,每个分组就是一个报文段(segment),然后交付到网络层。

传输层协议概览

总的来说,网络层的目标是将一台主机的数据交付到另一台主机,而传输层的目标是,将到达一台主机(可以理解为网卡)的数据,分发给目标的应用程序,以及将应用程序的数据,打包发到主机(网卡)。

多路复用和多路分解

传输层协议最基本的责任是,将(不同主机上)两个主机之间的交付服务扩展为(不同主机上)两个进程之间的交付服务。这种扩展,称为 多路复用多路分解

用大白话来讲就是,多路复用相当于把一台主机多个进程多个端口号的数据,统一加工和收集,生成报文段,交付给网络层。多路分解则是把网络层到达主机的数据,根据发送时标记的信息,分发到各个进程各个端口。

multiplexing

那么,网路向进程传递数据,以及进程向网络传输数据,这件事是谁来干的呢? 答案就是我们既熟悉又陌生的 socket 套接字。 socket 是传输层协议和进程之间的接口。

如何实现多路复用和多路分解

  1. socket 有唯一标识符(毕竟 Unix 的哲学,万物皆文件)
  2. 报文段有特殊字段来指示要交付到哪个套接字

于是我们就引申出 端口号 这个概念。端口号用来识别是哪一个网络应用程序(socket)。端口号是一个 16 bits 的数字,取值在 0-65535 之间。因为传输层既需要 A 到 B,也需要 B 到 A,所以需要有 源端口号目标端口号,他们可以不一样。

面向连接和无连接的传输层协议

传输层有两种著名协议,分别是 UDP 和 TCP 。 UDP 是不可靠的,无连接的,只要把数据发了就完事了。而 TCP 是可靠的,面向连接的,必须准确无误。

因为 UDP 是无连接的,只需要一个 目的IP 和一个 目的端口 号就可以识别一个 UDP socket。所以,如果两个UDP报文有相同的目的IP和目的端口,但具有不同的源IP或端口,那么这两个报文段将通过相同的目的 socket 到达目的进程。

而 TCP 是面向连接的,需要 源IP源端口目的IP目的端口 四个组合来唯一确定一个 TCP socket。任意一个不一样,都不会是同一个 socket。


设计可靠的数据传输协议

如果让我们来设计一个可靠的数据传输协议,我们会怎样设计呢?无非是两点:

  1. 数据比特不会损坏或丢失;
  2. 所有数据都是按照发送顺序进行交付,不会错乱。

如何保证数据比特不会损坏(例如发送1却收到0)呢?想象我们打电话,当我们讲了几句话,对方会用 “嗯”、“OK” 等回复来表示对方收到了,或者用 “没听清”,“请再说一遍” 来表示没收到。 这是一种 肯定确认否定确认 的机制,可以用来解决这个问题。使用这种机制的可靠数据传输协议,称为 自动重传请求协议。它需要有 3 个功能:

  1. 差错检测(checksum)
  2. 接收方反馈
  3. 重传(检测到差错或对方太久没反馈,就重传)

有了重传机制,我怎么知道哪些数据是重复的呢?(例如第一次发因为网络阻塞迟迟没到,触发了重传,第二次发的收到后,第一次发的同一份数据终于姗姗来迟),且由于网络层是不可靠的,来自对方的肯定确认或否定确认也可能丢失。再次,先发的数据可能后到,要怎么样保证数据的顺序性?解决这个问题的方法其实很简单,给每个数据块分组加上 序号 就行了。这样我就能知道哪些数据丢了需要重传,哪些数据需要放在哪个顺序位置上。

到这里,看似已经很完美了。但还有一个重要缺陷,如果有很多个分组需要传输,每个分组发送出去后都必须等待收到对方的确认回复后才能继续发下一个分组,如果网络延迟很高,那这种方式效率就会非常差。解决这一问题的方法是,可以一批一批地发,在同一批次内不必顺序等待确认。这种方式叫 流水线(pipelining),在流水线中,我们可以设置一个滑动窗口,来解决错误重传的问题,顺便也控制了速率。

总结起来,设计一个可靠的数据传输协议,需要这么几个机制:

  1. 校验和:用来检测错误
  2. 定时器: 用于超时重传一个分组
  3. 序号: 为每个分组编号,便于区分哪些分组丢失需要重传,哪些分组重复了
  4. 肯定确认和否定确认: 肯定确认用于告诉发送方分组已经被正确接收,否定确认用于告知发送方接收错误,需要对方重传
  5. 流水线窗口: 限制一个范围内的分组可以批量发送,而不必停等,窗口大小对控制速率也很有帮助

TCP 协议

参考:TCP之旅

UDP 协议

待补充