5.1 名词解释

  1. 进程(process) :指计算机中正在运行的程序实体。
  2. 应用进程互相通信 :一台主机的进程和另一台主机中的一个进程交换数据的过程(另外注意通信真正的端点不是主机而是主机中的进程,也就是说端到端的通信是应用进程之间的通信)。
  3. 传输层的复用与分用 :复用指发送方不同的进程都可以通过统一个运输层协议传送数据。分用指接收方的运输层在剥去报文的首部后能把这些数据正确的交付到目的应用进程。
  4. TCP(Transmission Control Protocol) :传输控制协议。
  5. UDP(User Datagram Protocol) :用户数据报协议。
  6. 端口(port) :端口的目的是为了确认对方机器是那个进程在于自己进行交互,比如 MSN 和 QQ 的端口不同,如果没有端口就可能出现 QQ 进程和 MSN 交互错误。端口又称协议端口号。
  7. 停止等待协议(stop-and-wait) :指发送方每发送完一个分组就停止发送,等待对方确认,在收到确认之后在发送下一个分组。
  8. 流量控制 : 就是让发送方的发送速率不要太快,既要让接收方来得及接收,也不要使网络发生拥塞。
  9. 拥塞控制 :防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。

5.2 运输层概述

5.2.1 逻辑通信

运输层提供应用进程之间的逻辑通信,也就是说,运输层之间的通信并不是真正在两个运输层之间直接传输数据。运输层向应用层屏蔽了下面网络的细节(如网络拓补,所采用的路由选择协议等),它使应用进程之间看起来好像两个运输层实体之间有一条端到端的逻辑通信信道。
在对比一下网络层:网络层主机提供逻辑通信,而运输层应用进程之间提供端到端的逻辑通信。
运输层逻辑通信.png

5.2.2 端口号

端口号可以分为两种:硬件端口和软件端口

  • 硬件端口,是不同硬件设备之间进行交互的接口。
  • 软件端口,是应用层的各种协议进程与运输实体进行层间交互的一种地址。

TCP/IP 协议用一个16位(最大值 65535)端口号来表示一个端口,端口号只具有本地意义,也就是说不同计算机中相同的端口号是没有关联的。

两个计算机的进程之间进行通讯,不仅需要知道 IP 地址,还需要知道端口号。互联网上计算机通信采用的是客户-服务器模式,端口号也分为这两类:

  • 服务器端使用的端口号,这里又分成了两类,一类是熟知端口号(或者系统端口号),数值为 0 - 1023。另一类叫做登记端口号,数值在1024 - 49151,是给没有熟知端口号的应用程序使用的,使用必须按照规定进行注册,防止重复。

熟知端口号.png

  • 客户端使用的端口号,49152 - 65535。仅在客户进程运行时动态进行选择。

    5.2.3 UDP 协议

    UDP协议(User Datagram Protocol、用户数据报协议)。根据这个协议传输的数据单元称之为 UDP 用户数据报

    5.2.3.1 特点

  • 无连接的,即在传输之前不需要先建立连接,接收方接收到 UDP 报文后,也不需要给出任何确认。

  • 尽最大努力交付,即不保证可靠交付。
  • 面向报文,应用层给 UDP 多长的报文,UDP 就发送多长的报文,一次发送一个完整的报文。
  • 没有拥塞控制,网络上出现的拥塞不会让主机的发送速率降低。
  • 支持各种交互方式,支持1-1,1-多,多-1,多-多的交互通信。
  • 首部开销小,只有8个字节。

5.2.3.2 首部格式

UDP首部.png
UDP 首部只有8个字节,由4个字段组成,每个字段2字节:

  • 源端口,源端口号,在需要目标回应的时候使用,不用就全设为0。
  • 目的端口,在终点交付报文时使用。
  • 长度,UDP 数据报的长度,最小为8(只有首部)。
  • 检验和,检查 UDP 数据报在传输过程中是否有错,有则丢弃。
    • 检验和的计算有点特殊,需要在 UDP 数据报前面加上一个12字节的伪首部,检验和就是根据添加了伪首部之后的UDP数据报进行计算的。
    • 注意:这个伪首部仅仅是用来计算检验和,相当于计算完就删去,既不向上提交也不向下发送。

5.2.4 TCP 协议

TCP 协议(Transmission Control Protocol、传输控制协议)。根据这个协议传输的数据单元称之为 TCP 报文段

5.2.4.1 特点

  • 面向连接的,即在使用 TCP 协议之前,必须先建立 TCP 连接;使用完毕也要释放连接。
  • 一对一,每一个 TCP 连接只能有两个端点,也就是说只能是点对点的。
  • 提供可靠交付的服务,通过 TCP 连接传送的数据无差错、无丢失、无重复、无失序。
  • 全双工通信,TCP 连接双方任何时候都可以发送数据。
  • 面向字节流,TCP 中的“流”是指流进或者流出进程的字节序列。TCP 并不知道所传送的字节流的含义,仅仅把把应用程序交下来的数据看成是一连串的无结构字节流。

    5.2.4.2 TCP 的连接

    每一个 TCP 连接有两个端点,这两个端点叫做套接字(socket)或者插口
    套接字 = IP 地址 + 端口号。
    套接字.png

    5.2.4.3 首部格式

    TCP首部.png
    各字段意义如下:

  • 源端口和目的端口,各占2个字节。

  • 序号,占4个字节,在一个 TCP 连接中传送的字节流中的每一个字节都是按顺序编号,这个序号字段就表示本报文段所传送的数据的第一个字节的序号。
  • 确认号,占4个字节,表示期望收到对方下一个报文段的第一个数据字节的序号。假如确认号 = N,则表示 N - 1之前的数据都已经正确收到。
  • 数据偏移,占4位,表示 TCP 报文段中数据部分的起始位置到 TCP 报文段的起始位置有多远。
  • 保留,占6位,目前没有意义,全为0。
  • 紧急 URG,= 1 时表明此报文段中存在紧急数据,并把紧急数据放在报文段的最前面。需要和紧急指针配合使用。
  • 确认 ACK,TCP 规定在建立连接之后的所有传送的报文段中必须让 ACK = 1。
  • 推送 PSH,表示希望本报文段尽快被交付给目标进程,而不等待缓存满了再提交。
  • 复位 RST,= 1 时表明 TCP 连接出现严重错误,需要释放当前连接,再重新建立连接。也可以用来拒绝一个非法报文段或者拒绝打开一个连接。
  • 同步 SYN,SYN = 1 且 ACK = 0时,表明这是一个连接请求报文段。若对方同意,则响应的报文段中 SYN = 1 且 ACK = 1。
  • 终止 FIN,FIN = 1 时表明此报文段的发送方数据已经发送完毕,并要求释放连接。
  • 窗口,占2字节,从确认号开始算起,自己允许对方发送的数据量(以字节为单位)。
    • 假设确认号 = 1,窗口值 = 100,则表示自己还可以接受100字节的数据,序号为 1 - 100。
  • 检验和,占2字节,检验范围包括首部和数据部分,和 UDP 一样需要加上伪首部。
  • 紧急指针,占2字节,只有在 URG = 1 时才有意义。指出本报文段中紧急数据的字节数。
  • 选项,长度可变,最长为 40 字节。

5.2.4.4 TCP 可靠传输的工作原理

停止等待协议

  • 无差错情况
    • A 发送分组 M1,发完就暂停发送,等待 B 的确认。B 收到了 M1 就向 A 发送确认。A 在收到了对 M1 的确认后,就再发送下一个分组 M2。同样,在收到B 对M2 的确认后,再发送M3。
  • 出现差错的情况
    • 可能有两种情况:B 接收 M1 时检测出了差错;也可能是 M1 在传输过程中丢失了。
    • 在这两种情况下,B 都不会发送任何信息。可靠传输协议是这样设计的:A 只要超过了一段时间仍然没有收到确认,就认为刚才发送的分组丢失了,因而重传前面发送过的分组。这就叫做超时重传
    • 要实现超时重传,就要在每发送完一个分组时设置一个超时计时器。如果在超时计时器到期之前收到了对方的确认,就撤销已设置的超时计时器。A 为每一个已发送的分组都设置了一个超时计时器。但 A 只要在超时计时器到期之前收到了相应的确认,就撤销该超时计时器。
    • 对于发送方A,需要注意几点:
      • A 需要保留已发送的分组,以便在发生超时重传的时候使用。
      • 分组和确认分组必须编号,这样才知道是哪个分组收到了确认。
      • 超时计时器的重传时间应该比数据在分组传输的平均往返时间长一点

停止等待协议.png

  • 确认丢失和确认迟到
    • B对A发送的确认丢失了:A 在超时时间内没有收到;或者 B 的确认报文丢失了。在这种情况下,A 会在超级计时器到期后重传 M1,假设 B 又收到了 M1报文,这时候 B 要采取两个动作:
      • 丢弃这个重复的分组 M1。
      • 向 A 发送确认。

确认丢失和确认收到.png

通过前面所说的确认-重传机制,就可以在不可靠的网络上实现可靠的通信。这种协议也常称之为自动重传请求(Automatic Repeat reQuest、ARQ)

5.2.4.5 滑动窗口

TCP 的滑动窗口是以字节为单位。下面将举例子说明滑动窗口的工作原理。

  • 假设 A 收到 B 发来的确认报文,其中窗口为 20 ,确认号为 31。根据这两个信息,A 构造出自己的发送窗口。
    • 这个发送窗口表示:在没有收到 B 的确认的情况下,可以连续把窗口内的数据都发送出去。同时,在未收到确认之前,这些已发送的数据需要暂时保存,以便后续的超时重传。
    • 发送窗口的后沿表示已发送,并确认。其变化情况只能是不动(没有收到新的确认)和前移(收到新的确认)。
    • 前沿表示不允许发送。其变化情况通常是不断前移(没有收到新的确认,对方通知的窗口也没有变化),或者不动(收到新的确认,但通知的窗口变小, 恰好就不动了)。TCP 的标准强烈建议前沿不后缩。

发送窗口.png

  • 假定A发送了序号为31~41的数据。这时,发送窗口位置并未改变
    • 发送窗口内靠后面有11个字节(灰色小方框表示)表示已发送但未收到确认。而发送窗口内靠前面的9个字节(42~50)是允许发送但尚未发送的。
    • 用三个指针P1、P2、P3来表示发送窗口的不同部分。
      • 发送窗口:P3 - P1
      • 已发送但未收到确认:P2 - P1
      • 运行发送但未发送:P3 - P2
  • B 收到了序号为32、33的数据,但是因为 31 还没有收到,所以32、33是无效的(没有按序到达)。
    • B 只能对按序收到的数据的最高序号给出确认,这里给出的确认号仍然是 31。

发送窗口2.png

  • 假定B收到了序号为 31 的数据,并把序号为 31~33 的数据交付主机,然后B删除这些数据。接着把接收窗口向前移动3个序号,同时给A发送确认,其中窗口值仍为 20,但确认号是 34。这表明B已经收到了到序号 33 为止的数据。
    • 另外,B 还收到了序号为 37、38 和 40 的数据,但这些都没有按序到达,只能先暂存在接收窗口中。A 收到 B 的确认后,就可以把发送窗口向前滑动 3 个序号,但指针 P2 不动。可以看出,现在 A 的可用窗口增大了,可发送的序号范围是 42~53。

发送窗口3.png

  • A 继续发送完序号 42~53 的数据后,指针 P2 向前移动和 P3 重合。发送窗口内的序号都已用完,但还没有再收到确认。
    • 由于 A 的发送窗口己满,可用窗口已减小到零,因此必须停止发送。
    • 请注意,存在下面这种可能性,就是发送窗口内所有的数据都已正确到达 B,B 也早已发出了确认。但是所有这些确认都滞留在网络中。在没有收到 B 的确认时,为了保证可靠传输,A 只能认为 B 还没有收到这些数据。于是,A 在经过一段时间后(由超时计时器控制)就重传这部分数据,重新设置超时计时器,直到收到 B 的确认为止。
    • 如果 A 收到确认号落在发送窗口内,那么 A 就可以使发送窗口继续向前滑动,并发送新的数据。

发送窗口4.png

5.2.4.6 流量控制

是一个端到端的问题,让发送方的发送速率不要太快,让接收方来得及接收数据。

使用滑动窗口可以很好的实现流量控制。下面用一个例子来说明。
流量控制.png

5.2.4.7 拥塞控制

是一个全局性的过程,涉及到所有的主机和路由器。防止过多的数据进入网络,避免网络中的路由器或者链路过载。

TCP 的拥塞控制算法有四种:慢开始,拥塞避免、快重传、快恢复。下面一一介绍。

基于窗口的拥塞控制,发送方维持一个状态变量,叫做拥塞窗口 cwnd (congestion window),发送方让自己的发送窗口 = 拥塞窗口。
为了防止 cwnd 过快增大,还需要设置一个状态变量,叫做慢开始门限 ssthresh

  • 慢开始(slow-start)

慢开始的算法思路是:从小到大逐渐增加发送窗口(拥塞窗口)
具体实现是:

  • 一开始发送方设置 cwnd = 1。
  • 每收到一个报文段的确认,就将 cwnd 翻倍

慢开始.png

  • 拥塞避免(congestion avoidance)

和慢开始十分相似,只不过每次增加不是翻倍,而是 + 1

  • 快重传(fast retransmit)

快重传算法可以让发送方尽快知道个别报文段发生了丢失。
快重传算法的思路是:

  • 要求接收方立即发送确认,即使收到了失序的报文段,也要立即发出已收到的、有序的报文段的重复确认
  • 发送方只要连续收到 3 个重复确认,就应该立即进行重传操作。

快重传.png

  • 快恢复(fast recovery)

发生快重传之后,发送方就知道只是丢失了个别的报文段。于是不启动慢开始,而是启动快恢复。
快恢复的思路是:尽快恢复高效的信息传送。

  • 调整 ssthresh = cwnd / 2。
  • 然后设置 cwnd = ssthresh。
  • 最后执行拥塞避免算法。
  • TCP 拥塞控制流程图

流程图如下所示:
TCP拥塞控制流程图.png
结合流程图和下面的例子就很好理解了。
TCP拥塞控制变化情况.png
有几个关键点需要注意一下:

  • 点1。ssthresh 的初始值,在这之前使用慢开始算法,在这之后使用拥塞避免算法
  • 点2。在这里发生了超时,就设置 ssthresh = 2 / 此时的cwnd。然后把 cwnd 设置为1,再重新执行慢开始算法
  • 点3。注意这里的 ssthresh 已经不是最开始的初始值,而是经过点 2 超时之后重新设置的。
  • 点4。这里接收方连续收到 3 个相同的确认发生了快重传
  • 点5。发生快重传之后,将 ssthresh 和 cwnd 都设置为此时 cwnd 的一半,然后再执行拥塞避免算法


5.2.4.8 连接管理

TCP 的连接分为三步:建立连接、使用连接、释放连接。

  • 建立连接(三次握手)

三次握手.png
先来看一个简单的描述:

  • A -> B:我要请求连接
  • B -> A:你真的要请求连接吗
  • A -> B:我真的要请求连接
  • 连接成功。

下面再看看详细的解释:

  • A 向 B 发出请求连接报文段,其中首部的同步位 SYN = 1,初始序号为seq = x 。然后 A 进入 SYN-SENT(同步-已发送)状态。
    • TCP 规定请求连接报文段不能携带数据,但要消耗一个序号
  • B 收到请求后,如果同意连接,则向 A 发送确认。其中 SYN = 1,ACK = 1,确认号 ack = x + 1,同时也为自己选择一个初始序号 seq = y。然后 B 进入 SYN-RCVD(同步-收到)状态。
    • 这个报文段也不能携带数据,同样也要消耗一个序号
    • 这一步可以拆成两个报文段:确认报文段(ACK = 1,ack = x + 1)和同步报文段(SYN = 1,seq = y)。这样的话过程就变成了四握手,效果是一样的。
  • A 收到 B 的确认后,还需要再给 B 发送一个确认。其中 ACK = 1,seq = x + 1,ack = y + 1。然后 A 进入 ESTAB-LISHED(已建立连接)状态。
    • 这个报文段可以携带数据,如果不携带数据则不消耗序号
    • 这一步是为了防止已经失效的连接请求突然又传送到 B。
  • B 收到 A的确认后,也进入ESTAB-LISHED(已建立连接)状态。


  • 释放连接(四次挥手)

四次挥手.png

先来看一个简单的描述:

  • A -> B:请求关闭连接
  • B -> A:收到关闭连接的请求
  • B:可能还会向 A 发送一些数据。
  • B -> A:你现在可以关闭连接了
  • A -> B:收到,我的连接现在关闭了
  • B:收到,我的连接也关闭了。

下面再看看详细的解释:
数据传输结束后,通信的双方都可释放连接。此时 A 和 B 都处于 ESTABLISHED 状态:

  • 假设 A 先向 B 发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。其中首部的终止控制位 FIN = 1,序号 seq = u,它等于前面已发送的数据的最后一个字节的序号加1。然后 A 进入 FIN-WAIT-1 (终止等待1)状态,等待 B 的确认。
    • TCP规定,FIN 报文段即使不携带数据,它也消耗掉一个序号
  • B 收到连接释放报文段后即发出确认,确认号 ack = u +1,自己的序号 seq = v,等于 B 前面已发送的数据的最后一个字节的序号加 1。然后 B 就进入 CLOSE-WAIT (关闭等待)状态。
    • 这时的 TCP 连接处于半关闭(half-close)状态,即从 A 到 B 的连接已经关闭,但从 B 到 A 这个方向的连接并未关闭,这个状态可能会持续一段时间。
  • A 收到来自 B 的确认后,就进入FIN-WAIT-2(终止等待2)状态,等待 B 发出的连接释放报文段。
  • 若 B 已经没有要向 A 发送的数据,这时 B 发出的连接释放报文段中 FIN = 1。现假定 B 的序号为 w (在半关闭状态 B 可能又发送了一些数据)。B 还必须重复上次已发送过的确认号 ack = u +1。这时 B 就进入 LAST-ACK (最后确认)状态,等待 A 的确认。
  • A 收到 B 的连接释放报文段,就必须发出确认。其中 ACK = 1,确认号 ack = w + 1,自己的序号 seq = u + 1。然后 A 进入 TIME-WAIT(时间等待)状态。此时 TCP 连接还没有释放,必须经过时间等待计时器设置的时间 2MSL 后, A 才进入 CLOSED 状态。
    • 为了保证 A 发送的最后一个报文段能到达 B。
    • 让本连接产生的所有报文都从网络上消失,避免新的连接出现旧的报文。
  • B 收到 A 的确认,就进入 CLOSED 状态。
  • 为什么要进行三次握手和四次挥手?

先来说下三次握手:

  • 一次握手:客户端向服务器端发送一个请求连接的请求,客户端就默认连接建立,就发送数据。这显然不行。
  • 两次握手:客户端向服务器端发送一个请求连接的请求,服务器端给客户端一个响应。然后客户端就可以发送数据了。
    • 看起来两次握手没问题,实际上客户端向服务器端发送的连接请求,可能中途就丢失了,过了一段时间客户端没有收到响应,就再一次发送了一次请求。然而实际上第一次发送的请求没有丢失,只不过在某个地方阻塞了一下。服务器端可能就先后收到了两个连接请求,也就建立了两个响应在等待。极端情况下,客户端可能建立 N 个响应,实际上只有 1 个是真正有用的。这显然会造成极大的浪费。
  • 三次握手:在两次握手的基础上,再增加一次握手。这样可以防止失效的请求到达了服务器端就直接建立响应。

四次挥手:

  • 建立连接需要客户端和服务器端都同意,释放连接也一样。
  • 客户端没有数据需要发送了,请求释放连接。但服务器端可能还有数据需要发送,等服务器端数据发送完了再给客户端响应可以释放连接的消息,客户端这才能释放连接。服务器端收到客户端已经释放连接的响应,服务器端再释放连接。
  • 说简单一点,就是连接双方是平等的,需要保证一个连接的完整执行。


5.2.5 使用 TCP/UDP 协议的协议

使用TCP-UDP协议的协议.png

参考