前言:本文为计算机网络系列第五章笔记,陆续会更新余下内容。文章参了:计算机网络微课堂、《王道考研计算机网络考研复习指导》、《计算机网络( 第7版 )》—— 谢希仁 。本文仅供学习使用,若有侵权联系删除。
系列文章:
计算机网络「一」计算机网络概述
计算机网络「二」物理层
计算机网络「三」 数据链路层
计算机网络「四」 网络层
之前所介绍的计算机网络体系结构中的物理层、数据链路层以及网络层,它们共同解决了将主机通过异构网络互联起来所面临的问题,实现了主机到主机的通信。
但实际上在计算机网络中进行 通信的真正实体是位于通信两端主机中的进程。
而 如何为运行在不同主机上的应用进程提供直接的通信服务 是运输层的任务,运输层协议又称为 端到端 协议,所谓端到端也就是进程到进程。
从计算机网络体系结构的角度来看运输层
假设 AP1 与 AP4 之间进行基于网络的通信,AP2 与 AP3 之间进行基于网络的通信。在运输层采用不同端口对应不同进程,然后通过网络层及其下层来传输应用层报文。
接收方的运输层通过不同的端口,将收到的应用层报文交付给应用层中相应的应用进程。如图所示
可以简单地认为,运输层直接为应用进程间的逻辑通信提供服务。“ 逻辑通信 ” 的意思为运输层间的通信好像是沿水平方向传送数据,但事实上并没有一条水平上的物理连接。
运输层向高层用户屏蔽了下面网络核心的细节(如网络拓扑、所采用的路由选择协议等),它使应用进程看起来就好像 在两个运输层实体之间有一条端到端的逻辑通信信道 。
根据应用需求的不同,因特网的运输层为应用层提供了两种不同的运输协议,即 面向连接的TCP 和 无连接的UDP,这两种协议就是本章要讨论的主要内容。
我们知道,运行在计算机上的进程使用 进程标识符 PID 来标志。而因特网上的计算机并不是使用统一的操作系统,不同的操作系统(windows、Linux、Mac OS)又使用 不同格式的进程标识符 。
为了使运行不同操作系统的计算机的应用进程之间能够进行网络通信,就必须 使用统一的方法对 TCP/IP 体系的应用进程进行标识 。
TCP/IP 体系的运输层使用 端口号 来区分应用层的不同应用进程。
端口号
应用进程通过端口号进行标识。端口号只具有本地意义,即端口号只是为了 标识本计算机应用层中的各进程,在因特网中,不同计算机中的相同端口号是没有联系的 。端口号在运输层的作用相当于 IP 地址 在网络层的作用,或者是 MAC 地址 在数据链路层的作用,只不过 IP 地址和 MAC 地址标识的是主机,而端口标识的是主机中的应用进程。
端口号使用 16 比特表示,取值范围 0 ~ 65535
发送方的复用和接收方的分用
复用和分用概念:
补充说明:网络层也有分用和复用:复用是指发送方不同协议的数据都可以封装成 IP 数据报发送出去;分用是指接收方的网络层在剥去首部后把数据交付给相应的协议。
具体说明复用和分用的过程:
发送方
接收方
一些常用的熟知端口号
下图所示为 TCP/IP 体系的应用层常用协议,及它们所使用的运输层熟知端口号:
不管在运输层使用 UDP 协议还是 TCP 协议,在网络层都需要使用 IP 协议。IP 数据报首部中协议字段 的值,表明了 IP 数据报数据载荷部分封装的是何种协议数据单元。
运输层端口号的应用
如下图,用户 PC、DNS 服务器、Web 服务器通过交换机进行互联,处于同一以太网中。
DNS(Domain Name Server,域名服务器)是进行域名 (domain name)和与之相对应的IP地址 (IP address)转换的服务器。DNS 中保存了一张域名和与之相对应的 IP 地址的表,以 解析消息的域名。
Web 服务器 一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,可以处理浏览器等 Web 客户端的请求并返回相应响应,也可以放置网站文件,让全世界浏览;同时还可以放置数据文件,让全世界下载。
我们在用户 PC 中使用网页浏览器来访问 Web 服务器的内容。在网页浏览器的地址栏中输入 Web 服务器的域名,用户 PC 中的 DNS 客户端 进程会发送一个 DNS 查询请求报文。该报文使用 UDP 协议封装成 UDP 用户数据报,之后将其封装在 IP 数据报中发送给 DNS 服务器。
DNS 服务器收到后,从中解封出 UDP 用户数据报。因为端口号 53 (DNS 应用程序端口号为 53),所以将数据报的数据载荷部分(查询请求报文)交付给本服务器中的 DNS 服务器端进程。该进程解析查询请求报文的内容后,给用户 PC 发送 DNS 响应报文。之后,通过系列封装成为 IP 数据报,通过以太网发送给用户 PC。
用户 PC 收到后,解封出 UDP 用户数据报,根据端口号,将其交付给 PC 中 DNS 客户端进程。该进程解析 DNS 响应报文的内容,得到之前请求域名所对应的 IP 地址为 192.168.0.3 。
于是,用户 PC 中的 HTTP 客户端进程 可以向 Web 服务器发送 HTTP 请求报文了。该报文需要 TCP 协议封装为 TCP 报文段,之后封装在 IP 数据报中由以太网传输。
Web 服务器收到该数据包后解封出 TCP 报文段,根据其首部中目的端口号为 80,因此将 HTTP 请求报文交给本服务器中的 HTTP 服务器端进程 。该进程解析报文查找内容后,给用户 PC 发送 HTTP 响应报文。
用户 PC 收到并解封,将 HTTP 响应报文交付给用户 PC 中的 HTTP 客户端进程。该进程解析 HTTP 响应报文内容,并在网页浏览器中进行显示。这样,我们就可以成功地从网页中看到 Web 服务器提供的首页内容。
通过上面这个案例的过程分析,你就可以清楚地了解到端口号的妙用,它为主机中各种应用进程作标识,使得各种请求和响应能够有条不紊的进行。
套接字
在网络中通过 IP 地址来标识和区别不同的主机,通过端口号来标识和区分一台主机中的不同应用进程,端口号拼接到 IP 地址 即构成 套接字 Socket 。
在网络中采用发送方和接收方的套接字来识别端点。
套接字 Socket = (IP 地址: 端口号)
TCP/IP 协议族在运输层中使用了两个传输协议:
无连接的用户数据报协议 UDP
无连接服务是指两个实体之间的通信不需要先建立好连接,需要通信时,直接将信息发送到 “ 网络 ” 中,让该信息的传递在网上尽力而为地传送到目的地。
面向连接的传输控制协议 TCP
面向连接就是在通信双方通信前,必须先建立连接,整个连接的情况一直被实时地监控和管理。通信结束后,应该释放这个连接。
UDP协议
UDP 协议只在 IP 的数据报服务上增加了两个最基本的服务:复用和分用 和 差错检测 。
UDP 协议有如下优点:
UDP 常用于一次性传输较少数据的网络应用,如 DNS、SNMP 等。
UDP 不保证可靠交付,但不意味着应用对数据的要求是不可靠的,所有维护可靠性的工作可以由用户在应用层 来完成。
UDP 是面向报文的。发送方 UDP 对应用层交下来的报文,在添加首部后就向下交付给 IP 层,一次发送一个报文,不合并也不拆分。
UDP 可以对数据报做包括数据段在内的差错检测( IP 只对其首部做差错检测)。
TCP 协议
TCP 是在不可靠的 IP 层之上实现的可靠的数据传输协议,它主要解决传输的可靠、有序、无丢失和不重复问题。
TCP 主要有以下特点:
下面从几个方面对 UDP 和 TCP 进行比较说明
数据传输
比较是否可以 单播、多播、广播
UDP 支持单播、多播以及广播。也就是支持一对一、一对多、一对全的通信。
TCP 仅支持单播 ,也就是一对一通信。
使用 TCP 协议的通信双方,在进行数据传输之前,必须使用 “三报文握手” 来建立 TCP 连接。TCP 连接建立成功后,通信双方之间就好像是有一条可靠的通信信道进行通信。
处理应用报文方式的比较
UDP 是面向应用报文的。
UDP 对应用进程交下来的报文既不合并也不拆分,而是保留这些报文的边界。
TCP 是面向字节流的。
传输服务的比较
首部的比较
前文中提到,为了实现可靠传输,TCP 采用了 面向字节流 的方式。将应用进程交付下的应用报文看作字节流,存入到 TCP 的发送缓存 中。TCP 在发送数据时,是从发送缓存取出一部分或全部字节给其添加一个首部使其成为 TCP 报文段 后进行发送。如下图:
一个 TCP 报文段由 首部 和 数据载荷 两部分构成。TCP 的全部功能都体现在它首部中各字段的作用。
TCP 报文段的首部格式
一般来说,我们总是希望数据传输可以更快一些。但是如果发送方把数据发送得过快,接收方就可能来不及接收,这就会造成数据的丢失。
TCP 提供流量控制服务来消除发送方(发送速率太快)使接收方缓存区溢出的可能性。也就是让发送方的发送速率不要太快,要让接收方来得及接收。
采用 滑动窗口 机制可以很方便地在 TCP 连接上实现对发送方的流量控制。
举例说明
实际上 TCP 发送方的 发送窗口为 rwnd 和 cwnd 中的最小值,此例中暂不考虑 cwnd。
例如,在通信中,有效数据只从 A 发往 B,而 B 仅向 A 发送确认报文。如下:
持续计时器 与 零窗口探测报文
为解决上面死锁的局面,TCP 为每一个连接设有一个 持续计时器 :
问题 1:接收窗口为 0 还怎么接收探测报文 ?
答:TCP 规定,即使接收窗口为 0,也必须接收零窗口探测报文段、确认报文段以及携带有紧急数据的报文段。
问题 2:如果零窗口探测报文也丢失了呢 ?
答:零窗口探测报文段也有重传计时器。
练习
在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏。这种情况就叫做 拥塞 。若出现拥塞而不进行控制,整个网络的吞吐量将随输入负荷的增大而下降。见下图:
出现网络拥塞如何进行控制呢 ?
发送方维护一个叫做 拥塞窗口 cwnd 的状态变量,其值取决于网络的拥塞程度,并且动态变化。
发送方将拥塞窗口作为 发送窗口 swnd,即 swnd = cwnd 。
发送方还需维护一个叫做 慢开始门限 ssthresh 的 状态变量:
慢开始和拥塞避免
1. 慢开始算法
在 TCP 刚刚连接好并开始发送 TCP 报文段时,先令拥塞窗口 cwnd = 1,即一个最大报文段长度 MSS。每收到一个对新报文段的确认后,将 cwnd 加 1,即增大一个 MSS。用这样的方法逐步增大发送方的 cwnd。cwnd 增大到一个规定的慢开始门限 ssthresh(阈值),然后改用 拥塞避免算法。
说明:慢开始的 “慢” 并不是指 cwnd 增长速率慢,它是指数增长的。它的慢是说,开始时 cwnd = 1,只发送一个报文段。
2. 拥塞避免算法
其思路是让拥塞窗口 cwnd 缓慢增大。具体做法:每经过一个往返时延 RTT 就把发送方的拥塞窗口 cwnd 加 1,使其线性缓慢增大。
具体应用分析
无论慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(未按时收到确认),就要把慢开始门限 ssthresh 设置为出现拥塞时的发送方的 cwnd 值的一半(但不能小于 2)。
然后把拥塞窗口 cwnd 重新设置为 1,执行慢开始算法。
拥塞避免并不能完全避免拥塞,只是使网络比较不容易出现拥塞。
快重传和快恢复
有时,个别报文段会在网络中丢失,但实际上网络并没有出现拥塞。但是它导致了发送方超时重传,因此被误认为网络发生了拥塞。这使得传输效率有所降低。于是,快重传和快恢复算法对慢开始和拥塞避免算法进行了改进。
1. 快重传算法
所谓快重传,就是使发送方尽快进行重传,而不是等超时重传计时器超时再重传。这就要求发送方:
对于个别丢失的报文段,发送方不会出现超时重传,也就不会误认为出现了拥塞(进而降低拥塞窗口 cwnd 为 1)。使用快重传可以使整个网络的吞吐量提高约 20% 。
2. 快恢复算法
发送方一旦收到 3 个重复确认,就知道现在只是丢失了个别的报文段。于是不启动慢开始算法,而执行快恢复算法:
练习
这个题刚开始做时以为是快重传和快恢复,原因是没有注意到是发生了超时。发生超时应该使用慢开始和拥塞避免算法。
超时重传时间的选择是 TCP 最复杂的问题之一。
我们 不能直接使用某次测量得到的 RTT 样本来计算超时重传时间 RTO,如下图:
但是可以利用每次测量得到的 RTT 样本,计算 加权平均往返时间 RTTs(又称为平滑的往返时间)
测量方法如下所示,用这种方法得出的加权平均往返时间 RTTs 就比测量出的 RTT 值更加平滑
计算超时重传时间 RTO
显然,超时重传时间 RTO 应略大于加权平均往返时间 RTTs 。
RFC6298 建议使用下式计算超时重传时间 RTO:
往返时间 RTT 的测量
往返时间 RTT 的测量比较复杂。下面举两个例子来进行说明:
针对出现超时重传时无法测准往返时间 RTT 的问题,Karn 提出了一个算法:在计算加权平均往返时间 RTTs 时,只要报文段重传,就不采用其往返时间 RTT 样本。
也就是说,出现重传时,不重新计算 RTTs ,进而超时重传时间 RTO 也不会重新计算。
此外,为解决时延突然增大并且长时间维持高时延时,报文段都会重传,导致超时重传时间无法更新,报文段反复重传的问题。需要 对 Karn 算法进行修正:报文段每重传一次,就把超时重传时间 RTO 增大一些。典型的做法时将新 RTO 的值取为旧 RTO 值的 2 倍。
TCP 基于 以字节为单位的滑动窗口 来实现可靠传输
如何描述发送窗口的状态?(编程实现)
TCP 可靠传输的实现还需注意以下几点:
习题
TCP 是面向连接的协议,它基于运输连接来传送 TCP 报文段。TCP 运输连接的建立和释放是每一次面向连接的通信中必不可少的过程。TCP 的运输连接管理就是使 运输连接的建立和释放 都能正常地进行。
TCP 运输连接有以下三个阶段:
TCP 把连接作为最基本的抽象,每条 TCP 连接有两个端点,TCP 连接的端点不是主机,不是主机的 IP 地址,不是应用进程,也不是传输层的协议端口。TCP 连接的端口即为 套接字 或 插口,每条 TCP 连接唯一地被通信的两个端点(即两个套接字)确定。
TCP 连接的建立采用 客户/服务器方式 。主动发起连接建立的应用进程称为 客户(Client),而被动等待连接建立的应用进程称为 服务器(Sever)。
TCP 的连接建立
TCP 的连接建立要解决以下三个问题
详细描述见 《计算机网络( 第7版 )》—— 谢希仁 P239
最初,两端的 TCP 进程都处于 CLOSED(关闭)状态。
一开始,TCP 服务进程首先创建 传输控制块 TCB(存储连接的重要信息,如 TCP 连接表、指向发送和接收缓存的指针、当前的发送和接收序号等),之后就准备接受连接请求。此时, TCP 服务器进程就进入了 LISTEN(收听) 状态,等待客户的连接请求。
TCP 客户进程也是首先创建 传输控制块 TCB 。然后,在打算建立连接时,分为以下三步进行操作:
TCP 客户进程向 TCP 服务进程发送 TCP 连接请求报文段,并进入 SYN - SENT(同步已发送)状态。TCP 连接请求报文首部中的同步位置 SYN 被设置为 1,表明这时一个 TCP 连接请求报文段。序号字段 seq 被设置了一个初始值 x,作为 TCP 客户进程所选择的初始序号。
注意: TCP 规定,SYN 被设置为 1 的报文段不能携带数据,但要消耗掉一个序号。
TCP 服务进程收到连接请求报文端后,如果同意建立连接,则向 TCP 客户进程发送 TCP 连接请求确认报文段,并进入 SYN - RCVD(同步收到)状态。该报文段首部中的同步位 SYN 和确认位 ACK 都设置为 1,表明这时一个 TCP 连接请求确认报文段。序号字段 seq 被设置了一个初始值 y,作为 TCP 服务器进程所选择的初始序号。确认号字段 ack 的值被设置成了 x + 1,这是对 TCP 客户进程所选择的初始序号的确认。
注意:确认报文段也不能携带数据,但也要消耗掉一个序号。
当 TCP 客户进程收到 TCP 连接请求确认报文段后,还要向 TCP 服务器发送一个普通的 TCP 确认报文段,并进入 ESTABLISHED(已建立连接)状态。该报文段首部中的确认位 ACK 被设置为 1,表明这是一个普通 TCP 确认报文段。序号字段 seq 被设置为 x + 1,这是因为 TCP 客户进程发送的第一个 TCP 报文段的序号为 x,因此第二个序号为 x + 1。确认号字段 ack 被设置为 y + 1,这是对 TCP 服务器进程所选择的初始序号的确认。TCP 服务器进程收到该确认报文段后,也进入 ESTABLISHED(已建立连接)状态。
注意:TCP 规定,普通的 TCP 确认报文段可以携带数据。但如果不携带数据,则不消耗序号,这种情况下,所发送的下一个数据报文段序号仍是 x + 1。
思考一个问题:为什么 TCP 客户进程最后还要发送一个普通的 TCP 确认报文段呢 ? 能否简化为 “两报文握手” 来建立连接呢 ?
答案是否定的。这主要是为了防止已失效的连接请求报文段突然又传送到了 TCP 服务器进程,因而产生错误。如下图:
TCP 的连接释放
TCP 通过 “ 四报文挥手 ” 来释放连接
假设使用 TCP 客户进程的应用进程通知其主动关闭 TCP 连接,TCP 客户进程会发送 TCP 连接释放报文段,并进入 FIN - WAIT - 1(终止等待 1)状态。该报文段首部中的终止位 FIN 和确认位 ACK 的值都被设置为 1,表明这是一个 TCP 连接释放报文段,同时也对此前收到的报文段进行确认。序号 seq 字段设置为 u,它等于前面已传送过的、数据的最后一个字节的序号加 1。确认号 ack 字段的值设置为 v,它等于 TCP 客户进程之前已收到的、数据的最后一个字节的序号加 1。
注意:TCP 规定,终止位 FIN 等于 1 的报文段即使不携带数据,也要消耗掉一个序号。
TCP 服务器进程收到 TCP 连接释放报文段后,会发送一个普通的 TCP 确认报文段,并进入 CLOSE - WAIT(关闭等待)状态。该报文段首部中的确认位 ACK 的值被设置为 1,表明这是一个普通的 TCP 确认报文段。序号 seq 字段的值设置为 v,它等于 TCP 服务器进程之前已传送过的数据的最后一个字节的序号加 1。确认号 ack 字段的值设置为 u + 1,这是对 TCP 连接释放报文段的确认。
TCP 服务器进程这时应通知高层应用进程:TCP 客户进程要断开与自己的 TCP 连接。此时,从 TCP 客户进程到 TCP 服务器进程这个方向上的连接就释放了,TCP 连接属于 半关闭状态 。但若服务器发送数据,客户进程仍要接收,即从服务器进程到客户进程这个方向上的连接未关闭。
TCP 客户进程收到 TCP 确认报文段后就进入 FIN - WAIT - 2(终止等待 2) 状态,等待 TCP 服务进程发出的 TCP 连接释放报文段。若 TCP 服务器进程已没有数据发送,应用进程就通知其 TCP 服务器进程释放连接。TCP 服务器进程发送 TCP 释放报文段并进入 LAST - ACK(最后确认)状态。该报文段首部中的终止位 FIN 和确认位 ACK 都被设置为 1,表明这是一个 TCP 连接释放报文,同时也对之前收到的报文段进行确认。假设 seq 为 w,这是因为半关闭状态可能又发送了一些数据。确认号 ack 字段的值为 u + 1,这是对之前收到的 TCP 连接释放报文段的重复确认。
TCP 客户进程收到 TCP 连接释放报文段后,必须针对该报文段发送普通的 TCP 确认报文段,之后进入 TIME - WAIT(时间等待)状态。该报文段首部中的 ACK 的值被设置为 1,表明这是一个普通的 TCP 确认报文段。序号 seq 字段的值设置为 u + 1,这是因为 TCP 客户进程之前发送的 TCP 连接释放报文段虽然不携带数据,但要消耗掉一个序号。确认号 ack 字段的值设置为 w + 1,这是对所收到的 TCP 连接释放报文段的确认。TCP 服务器进程收到该报文段后,就进入 CLOSED(关闭)状态。而 TCP 客户进程还需经过 2MSL 后才进入关闭状态。
MSL (Maximum Segment Lifetime):最大报文段寿命。RFC793 建议 2 分钟。对于目前的网络,2 分钟可能过长,因此 TCP 允许不同的实现可根据具体情况使用更小的 MSL 值。
又有一个问题:TCP 客户进程发送完最后一个确认报文段后,为什么不直接进入关闭状态,而是 2MSL 后才进入关闭状态 ?