计算机网络 - 运输层(上)

alva

计算机网络 - 运输层(上)

概述

运输层在某种程度上起到了用户功能与通信部份之间桥梁的作用。
运输层向它上面的应用层提供通信服务,它属于面向通信部分的最高层,同时也是用户功能中的最低层。

在一开始学习TCP/IP五层协议时我们已经了解到,只有位于网络边缘部分的主机的协议栈才有运输层,而网络核心部分中的路由器在转发分组时都只用到下三层的功能。
信息流

“进程间的通信”

从IP层来说,通信的两端是两台主机。但“两台主机之间的通信”这种说法还不够清楚。

从运输层的角度看,通信的真正端点并不是主机而是主机中的进程。也就是说,端到端的通信是应用进程之间的通信。

在一台主机中经常有多个应用进程同时分别和另一台主机中的多个应用进程通信
这表明运输层有一个很重要的功能——复用 (multiplexing)和分用 (demultiplexing)。
根据应用程序的不同需求,运输层需要有两种不同的运输协议,即面向连接的 TCP无连接的 UDP

在详细介绍两种运输协议之前,我们先对二者各自的特点有一个直观认识:

TCP UDP
传输对象 面向字节流 面向报文
只能提供点对点的通信 可实现一对一、一对多、多对多
是否连接 面向连接 面向非连接
可靠性 可靠 不可靠
优点 可靠、稳定
建立连接时需要三次握手 没有TCP的那一系列机制
传输时有重传、拥塞控制等机制 一种无状态的传输协议
传输结束后断开连接节约资源
缺点 慢、效率低 不可靠、不稳定
容易被攻击 网络状态不好时,容易丢包
适用场合 对网络通讯质量有要求时 对网络通讯质量要求不高
如传输HTTP/FTP/SMTP等协议 要求通讯速度尽量的快
传输大量数据时 实时应用程序
传输少量数据时
常见应用 浏览器(HTTP) QQ语音、QQ视频
Outlook(POP/SMTP)
首部开销 20字节 8字节

可以看出,二者主要的差别就在于UDP的“面向报文无连接”与TCP的“面向流有连接”。
TCP与UDP的区别

看到这里,你可能对于TCP所谓的“可靠性”有所疑问:明明无论是网络层的IP协议还是数据链路层的协议提供的都是“不可靠”(尽最大可能交付)的,为什么到了运输层就可以实现“可靠传输”了呢?
这其实是因为TCP协议在运输层建立起的“逻辑通信信道”中采用了一系列方法来保证运输的可靠性,具体哪些方法呢?马上就会揭晓了。

端口(port)

为了使运行不同操作系统的计算机的应用进程能够互相通信,就必须用统一的方法对 TCP/IP 体系的应用进程进行标志。解决这个问题的方法就是在运输层使用协议端口号 (protocol port number),或通常简称为端口 (port)。

端口用一个16位端口号进行标志。
端口号只具有本地意义,即端口号只是为了标志本计算机应用层中的各进程。

端口分为两大类

1、服务器端使用的端口号

  • 熟知端口(well-known ports),数值一般为 0~1023。
  • 登记端口号,数值为 1024~49151,为没有熟知端口号的应用程序使用的。

熟知端口号

2、客户端使用的端口号

  • 又称为短暂端口号,数值为 49152~65535,留给客户进程选择暂时使用。

用户数据报协议UDP

UDP 只在 IP 的数据报服务之上增加了很少一点的功能:

  • 复用和分用的功能
  • 差错检测的功能

面向报文的UDP

UDP一个中最大的特点就是“面向报文”(无连接)。

  • 发送方UDP 对应用程序交下来的报文,在添加首部后就向下交付 IP 层。UDP 对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。
    若报文太长,UDP 把它交给 IP 层后,IP 层在传送时可能要进行分片。
  • 接收方UDP 对 IP 层交上来的 UDP 用户数据报,在去除首部后就原封不动地交付上层的应用进程,一次交付一个完整的报文。

UDP的首部格式

UDP报文结构

伪首部
在计算检验和时,临时把“伪首部”和 UDP 用户数据报连接在一起。伪首部仅仅是为了计算检验和。

传输控制协议TCP

我们先回顾熟悉一下TCP的特点:

  • TCP 是面向连接的运输层协议
  • 每一条 TCP 连接只能有两个端点 (endpoint),每一条 TCP 连接只能是点对点的(一对一)
  • TCP 提供可靠交付的服务。
  • TCP 提供全双工通信。
  • 面向字节流

由此我们对于TCP有了一个大概的概念:面向流,有连接,很可靠
下面就从这些特点开始细说。

TCP面向流的概念

与UDP不同,TCP 不关心应用进程一次把多长的报文发送到 TCP 缓存。这和UDP的“给多少装多少”正相反
TCP 对连续的字节流进行分段,形成 TCP 报文段。
TCP的分段

TCP的连接

首先注意,TCP连接是一条虚连接而不是一条真正的物理连接。
在这个连接的基础上,TCP可以根据对方给出的窗口值和当前网络拥塞的程度来决定一个报文段应包含多少个字节(UDP 发送的报文长度是应用进程给出的)。

每一条 TCP 连接有两个端点,并且唯一地被通信两端的两个端点所确定。
这里的端点不是应用进程也不是端口,它有一个特殊的名字叫“套接字”(socket)。

套接字(socket) = (IP地址 : 端口号)
TCP 连接 ::= {socket1, socket2}

在解释TCP的可靠性之前,我们先了解一下到底什么是可靠传输

可靠传输的工作原理

所谓“可靠性”主要靠两大法宝协议实现:

  • 停止等待协议
  • 连续ARQ协议
一个直观的理解就是:“只有确认我发的东西你收到了,我才能继续往下发。(停止等待协议)但是每发一个报文都要等待确认效率太低,所以规定那么一个区域范围,范围之内的分组可以一次发送出去而不需要确认。(连续ARQ协议)”
停止等待协议

“停止等待”就是每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。

超时重传

发送方为每一个已发送的分组都设置了一个超时计时器。
只要在超时计时器到期之前收到了相应的确认,发送方就撤销该超时计时器,继续发送下一个分组。
否则,发送方将重传分组。

自动重传请求 ARQ

重传的请求是自动进行的,接收方不需要请求发送方重传某个出错的分组。
使用这种确认和重传机制,我们就可以在不可靠的传输网络上实现可靠的通信。

通过滑动窗口实现的连续ARQ协议

发送方维持一个发送窗口,位于发送窗口内的分组都可连续发送出去,而不需要等待对方的确认。
连续ARQ协议规定,发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置。

累计确认

接收方一般采用累积确认的方式。即不必对收到的分组逐个发送确认,而是对按序到达的最后一个分组发送确认,表示到这个分组为止的所有分组都已正确收到了

在了解TCP具体如何实现“可靠传输”之前,我们先来了解一下TCP报文的组成结构。

TCP报文段的首部格式

TCP报文结构

  • 源端口和目的端口:各占两个字节(16位),端口(Port)是运输层与应用层的服务接口,以实现复用和分用。
  • 序号(sequence number):占4个字节,指本报文段所发送的数据的第一个字节的序号。
  • 确认号(acknowledgement number):同样占4个字节,是期望收到对方下一个报文段的第一个字节的序号。(即收到对方的报文段最后一个字节的序号加一)
  • 数据偏移:占4位,指首部的长度,以4字节为单位。
  • 标志位(Flags)

    • 紧急(URG):URG=1时表示报文段中有紧急数据,应尽快传送;
    • 确认(ACK):ACK=1时确认号字段(acknowledgement number)才有效;
    • 推送(PSH):接收TCP收到 PSH = 1 的报文段,就尽快地交付接收应用进程,而不再等到整个缓存都填满了后再向上交付;
    • 复位(RST):RST=1时表明连接中出现严重差错,需要重连;
    • 同步(SYN):SYN=1表示这是一个“连接请求(SYN)”或“连接接受(SYN-ACK)”报文;
    • 终止(FIN):用于释放连接,FIN=1表明报文段发送完毕,要求释放连接。
  • 窗口:占2字节,是用来让对方设置发送窗口的依据。
  • 检验和:占2字节。检验和字段检验的范围包括首部和数据这两部分在计算检验和时,要在 TCP 报文段的前面加上 12 字节的伪首部
  • 可变部分中的最大报文长度MSS:MSS (Maximum Segment Size)是 TCP 报文段中的数据字段(不包括首部)的最大长度。通过这个告诉对方“我的缓存所能接收的报文段的数据字段的最大长度是 MSS 个字节”。
  • 填充字段:为了使整个首部长度是 4 字节的整数倍。

TCP可靠传输的实现

以字节为单位的滑动窗口

之前已经介绍过了连续ARQ协议,这个协议在TCP中的应用就是“滑动窗口”,这个窗口以字节为单位。
现在假设,A收到了B发来的确认号为32(表明B期望收到的下一个报文段的开头序号为32),窗口为20字节的确认报文段,那么A便可以根据这两个数据构建出如下发送窗口:
发送窗口1

A发送11个字节的数据后,发送窗口位置不变,可用窗口变小:
发送窗口2

此时,B收到了前三个字节,向A发送确认号为35的确认报文字段(同时接收窗口向前移动三个字节);
A的发送窗口在收到B的确认报文后也向前移动三个字节。
发送窗口3

接着,A继续向B发送报文,但是B一直没有发回确认报文。
A在发送至可用窗口为零时便停止发送。
发送窗口4

发送缓存与接收缓存

发送缓存与接收缓存

发送方在发送报文前,应用进程是先把字节流写入 TCP 的发送缓存中。
同时,发送端发送出去的报文在没有收到确认报文时,也会暂存在发送缓存中。发送窗口通常只是发送缓存的一部分。

在接收端,接收缓存负责暂存按序到达的、但尚未被接收应用程序读取的数据;以及不按序到达的数据。

这里有几点需要我们注意

  • A的发送窗口并不总是和B的接收窗口一样大(因为有一定的时间滞后)。
  • TCP标准没有规定对不按序到达的数据应如何处理。通常是先临时存放在接收窗口中,等到字节流中所缺少的字节收到后,再按序交付上层的应用进程。(或者使用*选择确认SACK)
  • TCP 要求接收方必须有累积确认的功能,这样可以减小传输开销。
  • 接收方可以在合适的时候发送确认,也可以在自己有数据要发送时把确认信息顺便捎带上。

在找资料时看到一篇很好的解释TCP传输控制的文章,大家可以check it out~

*关于选择确认SACK:
接收方收到了和前面的字节流不连续的两个字节块。
如果这些字节的序号都在接收窗口之内,那么接收方就先收下这些数据,并把这些信息准确地告诉发送方,使发送方不要再重复发送这些已收到的数据。
首先,需在TCP首部选项中加上“允许 SACK”的选项。
原来首部中的“确认号字段”的用法仍然不变,只是以后在 TCP 报文段的首部中增加了SACK选项,以便报告收到的不连续的字节块的边界。
超时重传时间的选择

重传机制是TCP中最重要和最复杂的问题之一。
TCP 每发送一个报文段,就对这个报文段设置一次计时器。只要计时器设置的重传时间到但还没有收到确认,就要重传这一报文段。

那么如何确定重传时间呢?这是TCP最复杂的问题之一。
如果把超时重传时间设置得太短,就会引起很多报文段的不必要的重传,使网络负荷增大。
但若把超时重传时间设置得过长,则又使网络的空闲时间增大,降低了传输效率。

为了得到较为合理的重传时间,TCP 采用了一种自适应算法。
RTO算法

算法中最关键的就是往返时间(RTT)的测量。
测量往返时间时,由于有的报文经过重传后,无法判断收到的确认报文是重传报文的确认报文还是原报文的确认报文,故采用了Karn算法
在计算平均往返时间 RTT 时,只要报文段重传了,就不采用其往返时间样本。
并且,报文段每重传一次,就把 RTO 增大一些,以弥补重传时间的无法更新。

TCP的流量控制

流量控制(flow control)就是让发送方的发送速率不要太快,既要让接收方来得及接收,也不要使网络发生拥塞。

利用滑动窗口机制可以很方便地在 TCP 连接上实现流量控制。
发送方根据收到的接收方报文段中的窗口(rwnd)字段调整发送窗口的大小。

死锁问题
接收方在发送给发送方零报文段后,假设接收方又有了缓存空间,便会给发送方发送带有新窗口大小的报文段。
但是这个报文段在传送过程中丢失了。发送方仍在等待窗口非零的报文;而接收方在等待发送方的数据,场面一度十分尴尬。这就形成了“死锁”。
为了解决这个问题,TCP 为每一个连接设有一个持续计时器 (persistence timer)。
只要TCP连接的一方收到对方的零窗口通知,就启动该持续计时器。
若持续计时器设置的时间到期,就发送一个零窗口探测报文段(仅携带 1 字节的数据),而对方就在确认这个探测报文段时给出了现在的窗口值。
如果还是零窗口值,那么重新设置计时器,继续等。如此一来就解决了“互相干等”的死锁问题。

下一节会介绍TCP协议中很重要的“拥塞避免”内容,later~

阅读 1.6k

兔子洞精选
图比较好看。

乌龟爬兔子洞。

71 声望
18 粉丝
0 条评论

乌龟爬兔子洞。

71 声望
18 粉丝
文章目录
宣传栏