🎃 OSI 七层模型
运输层也叫传输层
应用层 (application layer)
应用层是体系结构中的最高层。应用层的任务是通过应用进程间的交互来完成特定网络应用。应用层协议定义的是应用进程间通信和交互的规则。这里的进程指主机中正在运行的程序。 在互联网中应用层协议很多:如域名系统 DNS、HTTPS 协议、支持电子邮件的 SMTP 协议等。 我们把应用层交互的数据单元称为报文(message)
传输层 (transport layer)
运输层的任务是负责两台主机中进程之间的通信提供通用的数据传输服务。应用进程利用该服务传送应用层报文。 运输层主要有两种协议
传输控制协议 TCP (Transmission Control Protocol)
提供面向连接的、可靠的数据传输服务,数据传输单位是报文段(segment)
用户数据报协议 UDP (User Datagram Protocol)
提供无连接的、尽最大努力(best-effort)的数据传输服务(不保证数据传输的可靠性),数据传输单位是用户数据报
网络层 (network layer)
网络层负责为分组交换网上的不同主机提供通信服务。在发送数据时,网络层把运输层产生的报文段或用户数据报封装成分组或包进行传送。 在 TCP/IP 体系中,由于网络层使用 IP 协议,因此分组也叫IP 数据报,或简称数据报。(注意区别用户数据报协议 UDP) 无论在哪一层传送的数据单元,都可笼统地用**“分组**”表示 网络层的另一个任务就是要选择合适的路由,使源主机运输层所传下来的分组,能够通过网络中的路由器找到目的主机。 互联网使用的网络层协议是无连接的网际协议 IP(Internet Protocol)和许多路由选择协议,因此互联网的网络层也叫做网际层或IP 层
数据链路层(data link layer)
简称链路层。在两个相邻结点之间传送数据时,数据链路层将网络层交下来的IP 数据报组装成帧(framing),在两个相邻结点间的链路上传送帧(frame)。每一帧包括数据和必要的控制信息(如同步信息、地址信息、差错控制等)。 控制信息还使接收端能检测到所收到的帧中有无差错。(数据链路层不仅要检错,而且要纠错)
物理层(physical layer)
在物理层上所传送数据的单位是比特。发送方发送 1 时,接收方应当收到 1 而不是 0。因此物理层要考虑用多大的电压代表 1 或 0,以及接收方如何识别发送方所发送的比特。 注意,传递信息所利用的一些物理媒介,如双绞线、同轴电缆、光缆、无线信道,并不在物理层协议之内而是在物理层协议下面。
TCP / IP 的体系结构
TCP/IP 四层模型包括以下 4 层:
- 应用层(Application Layer)
- 为应用程序提供服务,如 HTTP、FTP、SMTP 等协议都位于这一层。
- 传输层(Transport Layer)
- 为端到端的通信提供可靠的数据传输服务,主要协议有 TCP 和 UDP 协议。
- 网际层(Internet Layer)
- 负责设备到设备的数据传输,最主要的协议是 IP 协议。
- 网络接口层/链路层(Network Interface/Link Layer)
- 负责操作单个跳数据传输,常见协议有以太网协议、WIFI 协议等。
这 4 层按自顶向下的顺序,每一层依赖于下一层提供更底层的网络传输服务。
- 应用层定义了应用程序如何传输数据
- 传输层保证了端到端的通信可靠传输
- 网际层负责设备之间路由和寻址
- 网络接口层负责在相邻节点间传输数据
这种很像沙漏计时器形状的 TCP/IP 协议族表明: TCP/IP 协议可以为各式各样的应用提供服务(everthing over IP), 同时 TCP/IP 协议也允许 IP 协议在各式各样的网络构成的互联网上运行(IP over everything)。 从中不难看出 IP 协议在互联网中的核心作用。
TCP 和 UDP
TCP 和 UDP 都属于传输层。
🌝UDP 的特点
1. UDP 是无连接的
即发送数据前不需要建立连接,因此减少了开销和发送数据之前的时延。
2. UDP 使用尽最大努力交互(不可靠性)
即不保证可靠交付,因此主机不需要维持复杂的连接状态表。
3. UDP 是面向报文的
发送方的 UDP 对应用程序交下来的报文,在添加首部后就向下交付 IP 层。 UDP 对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。也就是说,应用层交给 UDP 多长的报文,UDP 就照样发送,UDP 一次交付一个完整的报文。因此,应用程序必须选择合适大小的报文。 若报文太长,UDP 把它交给 IP 层后,IP 层在传送时可能要进行分片,这会降低 IP 层的效率。
4. UDP 没有拥塞控制
因此网络出现的拥塞不会使源主机的发送速率降低。这对某些实时应用很重要。很多实时应用(如 IP 电话、实时视频会议等)要求源主机以恒定的速率发送数据,并且允许在网络发送拥塞时丢失一些数据,但却不允许数据有太大的时延。
5. UDP 支持一对一、一对多、多对一、多对多的交互通信
6. UDP 首部开销小
UDP 首部只有 8 个字节,比 TCP 的 20 个字节首部要短。
🌝TCP 的特点
1. TCP 是面向连接的运输层协议
应用程序在使用 TCP 协议前,必须先建立 TCP 连接。在传送数据完毕后,必须释放已经建立的 TCP 连接。
2. 每一条 TCP 连接只能有两个端点(endpoint)
每一条 TCP 连接只能是点对点的
3. TCP 提供可靠交付的服务
通过 TCP 连接传送的数据,无差错、不丢失、不重复,并且按序到达。
4. TCP 提供全双工通信
5. 面向字节流
TCP 中的流(stream)指的是流入到进程或从进程流出的字节序列。 面向字节流的含义是: 虽然应用程序和 TCP 的交互是一次一个数据块(大小不等),但 TCP 把应用程序交下来的数据仅仅看成是一连串的无结构的字节流。 TCP 和 UDP 在发送报文时所采用的方式完全不同。TCP 并不关心应用程序一次把多长的报文发送到 TCP 缓存中,而是根据对方给出的窗口值和当前网络拥塞的程度来决定一个报文段应该包含多少个字节。而 UDP 发送的报文长度是应用进程给出的。
TCP 的连接
TCP 把连接作为最基本的抽象。TCP 的许多特性都与 TCP 是面向连接的这个基本的特性有关。 每一条 TCP 连接有两个端点。那么,TCP 连接的端点是什么呢? TCP 连接的端点叫做套接字(socket)或插口
套接字 socket = (IP地址:端口号)
每一条 TCP 连接唯一地被通信两端的两个端点(即两个套接字)所确定。
TCP连接 ::= {socket1, socket2} = {(IP1 : port1) , (IP2 : port2)}
TCP 连接的端点是个很抽象的套接字,即(IP 地址 :端口号) 同一个 IP 地址可以有多个不同的 TCP 连接,同一个端口号也可以出现在多个不同的 TCP 连接中。
TCP 报文段首部格式
TCP 虽然是面向字节流的,但 TCP 传输的数据单元却是报文段。一个 TCP 报文段分为首部和数据两部分。
首部个字段意义
1.源端口和目的端口
各占 2 字节,分别写入源端口号和目的端口号。
2. 序号
占 4 字节。序号范围[0, $2^{32}$ - 1] , 共 $2^{32}$ 序号。TCP 是面向字节流的。在一个 TCP 连接中传送的字节流中的每一个字节都按顺序编号。
3. 确认号
占 4 字节,是期望收到对方下一个报文段的第一个数据字节的序号。
若确认号 = N,则表明:到序号 N-1 为止的所有数据都已正确收到。
4. 数据偏移
占 4 位,它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远。 这个字段实际上指出 TCP 报文段的首部长度。
5. 保留
占 6 位,保留为今后使用,目前置为 0。
6. 6 个控制位
1. 紧急 URG(URGent)
URG = 1, 表明紧急指针字段有效。比如中断命令(ctr+c)
2. 确认 ACK (ACKnowledgment)
当 ACK = 1 时确认号字段才有效。当 ACK = 0 时,确认号无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1.
3. 推送 PSH (PuSH)
4. 复位 RST (ReSeT)
当 RST = 1 时,表明 TCP 连接中出现严重差错,必须释放连接,然后再重新建立运输连接。RST 置 1 还用来拒绝非法报文段或拒绝打开一个连接。RST 也叫重置位。
5. 同步 SYN(SYNchronization)
在连接建立时用来同步序号。 当 SYN = 1 而 ACK = 0 时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使 SYN = 1, ACK = 1。SYN 置 1 表示这是一个连接请求或连接接受报文。
6. 终止 FIN (FINis)
用来释放一个连接。当 FIN = 1 时,表明此报文段的发送发的数据已发送完毕,并要求释放运输连接。
7. 窗口
占 2 字节。值是 [0, 2^16 - 1]之间的整数。 窗口指的是发送本报文段的一方的接受窗口(而不是自己的发送窗口)。窗口字段明确指出了现在允许对方发送的数据量。 窗口值经常动态的变化着。
8. 校验和
占 2 字节。校验和检验的范围包括首部和数据这两部分。 检测数据传输中是否有错,有错就丢弃。
9. 紧急指针
占 2 字节。紧急指针仅在 URG = 1 时才有意义。PS. 即使窗口为零时也可发送紧急数据。
10. 选项
长度可变,最长可达 40 字节。当没有使用选项时,TCP 的首部长度是 20 字节。 (1kb 是 1024 字节,也就是 1024k)
TCP 可靠传输的工作原理
我们知道,TCP 发送的报文段是交给 IP 层传送的,但 IP 层只能提供尽最大努力服务,也就是说,TCP 下面的网络所提供的是不可靠的传输。 理想的传输条件有以下两个特点
- 传输信道不产生差错
- 不管发送方以多快的速度发送数据,接受方总来得及处理收到的数据
实际网络都不具备以上两个理想条件。但我们可以使用一些可靠传输协议,当出现差错时让发送方重传出现差错的数据,同时在接收方来不及处理收到的数据时,及时告诉发送发适当降低发送数据的速度。
停止等待协议
“停止等待” 就是每发送完一个分组就停止发送,等待对方的确认。在收到对方确认后再发送下一个分组。 运输层传送的协议数据单元叫报文段,网络层传送的协议数据单元叫 IP 数据报。一般讨论问题时,可以把它们简称为分组 对于出现差错的情况,可靠传输协议是这样设计的: 发送方 A 只要超过一段时间仍没有收到确认,就认为刚才发送的分组丢失了,因而重传前面发送过的分组。这就叫超时重传。 要实现超时重传,就要在每发送完一个分组时设置一个超时计时器。
信道利用率
停止等待协议的优点是简单,但缺点是信道利用率太低。 为了提高传输效率,发送方可以不使用低效率的停止等待协议,而是采用流水线传输。如下,
流水线传输就是发送方可连续发送多个分组,不必每发完一个分组就停顿下来等待对方确认。这样可使信道上一直有数据不间断地传送。
连续 ARQ 协议
发送方维持一个发送窗口,它的意义是:位于发送窗口内的分组都可以连续发送出去,而不需要等待对方的确认。 连续 ARQ 协议规定,发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置。 接收方一般都是采用累积确认的方式。就是说,接收方不必对收到的分组逐个发送确认,而是收到几个分组后,对按序到达的最后一个分组发送确认。表示:到这个分组为止的所有分组都已正确收到了。
🤣TCP 可靠传输的实现
TCP 主要提供了
- 校验和
- 序列号和确认应答
- 超时重传
- 流量控制(滑动窗口协议)
- 拥塞控制等保证数据的可靠传输。
1. 以字节为单位的滑动窗口
TCP 的滑动窗口是以字节为单位的。现假定 A 收到了 B 发来的确认报文段,其中窗口是 20 字节,而确认号是 31(表明 B 期望收到的下一个序号是 31,序号 30 为止的数据已经收到了)。根据这两个数据,A 就构造出自己的发送窗口。
2. 超时重传时间的选择
参考 计算机网络 第 7 版 P225
3. 选择确认 SACK
如果要使用选择确认 SACK,那么在建立 TCP 连接时,就要在 TCP 首部选项中加上“允许 SACK”的选项,双方必须都事先商定好。 然后,SACK 文档并没有指明发送方应当怎样响应 SACK。因此大多数的实现还是重传所有未被确认的数据块。
TCP 的流量控制
所谓流量控制(flow control)就是让发送发的发送速率不要太快,要让接收方来得及接收。 利用滑动窗口机制可以很方便地在 TCP 连接上实现对发送方的流量控制。 这里 rwnd 表示 receiver window : 接收方的窗口。单位是字节。表明接收方目前能接收的数据容量。
TCP 的拥塞控制
TCP 进行拥塞控制的算法有四种:
- 慢开始 (slow-start)
- 拥塞避免 (congestion avoidance)
- 快重传 (fast retransmit)
- 快恢复 (fast recover)
参考 计网 7 版 P232
TCP 的运输连接管理
TCP 是面向连接的协议。运输连接时用来传送 TCP 报文的。 TCP 运输连接的建立和释放是每一次面向连接的通信中必不可少的过程。 因此,运输连接有三个阶段:
- 连接建立
- 数据传送
- 连接释放
TCP 连接的建立采用客户服务器方式。主动发起连接建立的应用进程叫做客户(client),被动等待连接建立的应用进程叫做服务器(server)。
TCP 的连接建立
TCP 建立连接的过程叫做握手,握手需要在客户和服务器之间交换三个 TCP 报文段。 计网 7 中指出:首次采用三报文握手。 P 238 以前该教程采用“三次握手”这个广为流行的译名。但其实这是在一次握手过程中交换了三个报文,而并不是进行了三次握手。 这有点像两个人见面进行一次握手时,他们的手上下摇晃了三次,但并非进行了三次握手。 RFC 973 文档 表述:three way(three message) handshake, handshake 使用的是单数,表明是一次握手。
🔥 三报文握手
- 假定 A 运行 TCP 客户程序,B 运行 TCP 服务器程序,最初两端的 TCP 进程都处于CLOSED(关闭)状态。
- A 主动打开连接,B 被动打开连接
- 一开始,B 的 TCP 服务器进程先创建传输控制块 TCB,准备接受客户进程的连接请求。B 服务器进程处于LISTEN(收听)状态,等待客户连接请求。
- A 的 TCP 客户进程也是首先创建传输控制模块 TCB。然后,在打算建立 TCP 连接时,向 B 发出连接请求报文段。
- 同步位 SYN = 1,同时选择一个初始序号 seq = x
- TCP 规定,SYN 报文段(即 SYN=1 的报文段)不能携带数据,但要消耗掉一个序号。
- 这时 TCP 客户进程进入 **SYN-SENT(同步已发送)**状态
- B 收到连接请求报文段后,如果同意建立连接,向 A 发送确认
- SYN = 1,ACK = 1,确认号 ack = x + 1, 同时也为自己选择一个初始序号 seq = y
- 这个报文也不能携带数据,但同样要消耗掉一个序号
- B 服务器进程进入 **SYN-RCVD(同步收到)**状态
- A 收到 B 的确认后,还要向 B 给出确认。
- 确认报文段的 ACK = 1,确认号 ack= y + 1, 自己的序号 seq = x + 1
- TCP 规定,ACK 报文段可以携带数据。如果不携带数据则不消耗序号,这种情况下,下一个数据报文段序号仍是 seq=x+1
- 这时,TCP 连接已经建立, A 进入 ESTABLISHED (已建立连接)状态
- B 收到 A 的确认后,也进入 ESTABLISHED (已建立连接)状态
以上连接建立的过程叫做 三报文握手,注意改名字了。three way handshake。 为什么 A 最后还要发送一次确认呢? 为了防止已失效的连接请求报文段突然又传送到了 B,因而产生错误。 正常情况: A 发送连接请求,但因连接请求报文丢失而未收到确认。A 再重传一次连接请求。后来收到确认,建立了连接。数据传输完毕后,就释放了连接。A 共发送两次连接请求报文段,第一个丢失,第二个重传到达了 B。 异常情况: A 发送的第一个连接请求报文并没有丢失,而是在某个网络结点滞留了,以致延误到连接释放的某个时间才到达 B。这本来是一个早已失效的报文段。这个时候 B 收到会误认为是 A 又发出一次新的连接请求,于是向 A 发出确认报文段,同意建立连接。如果不采用三次报文握手,只要 B 发出确认,新的连接就建立了。 然后这边 A 并没有发出建立连接请求,因此不会理睬 B 的确认。B 那边却以为新的运输连接已经建立了,并一直等待 A 发来数据。浪费 B 的资料。 三报文握手: 刚才的异常情况下,A 不会向 B 的确认发出确认。B 由于收不到确认,就知道 A 并没有要建立连接。
TCP 连接的释放
数据传输结束后,通信的双方都可释放连接。现在 A 和 B 都处于 ESTABLISHED (已建立连接)状态
🌝 四次挥手
- A 的应用进程先向其 TCP 发出连接释放报文段,并停止发送数据,主动关闭 TCP 连接。
- A 把连释放报文段首部的终止控制位 FIN 置 1,其序号 seq = u, 它等于已发送数据的最后一个字节序号+1.
- A 进入 FIN-WAIT-1 (终止等待 1)状态,等待 B 的确认
- TCP 规定,FIN 报文段即使不携带数据,也消耗掉一个序号。
- B 收到连接释放报文段后发出确认。
- 确认号 ack = u + 1, 序号 seq = v(已发送序号+1)
- B 进入 CLOSE-WAIT (关闭等待)状态
- TCP 服务器进程这时应通知高层应用进程,因而 A->B 这个方向的连接就释放了。
- 这时 TCP 处于 半关闭状态(half-close), 即 A 已经没有数据要发送了,但 B 若发送数据, A 仍要接收。也就是说,从 B -> A 这个方向的连接并未关闭,这个状态会持续一段时间
- A 收到来自 B 的确认后,进入 FIN-WAIT-2 (终止等待 2)状态,等待 B 发出的连接释放报文段
- 若 B 已经没有要向 A 发送的数据,其应用进程就通知 TCP 释放连接。
- 这时 B 发出的连接释放报文段必须使用 FIN = 1。图中,还有 ACK = 1
- 假定 B 的序号为 w (在半关闭状态 B 可能又发送了一些数据) seq = w
- B 还必须重复上次已发送过的确认号 ack = u + 1
- 这时 B 就进入 **LAST-ACK(最后确认)**状态,等待 A 确认
- A 在收到 B 的连接释放报文段后,必须对此发出确认。
- 在确认报文段中 ACK = 1, 确认号 ack = w + 1, 自己的序号 seq = u + 1(根据 TCP 标准,前面发送过的 FIN 报文段要消耗一个序号 所以,seq = u + 1)
- 然后 A 进入 TIME-WAIT (时间等待)状态。
- 此时 TCP 连接还没有释放掉。必须经过 时间等待计时器(TIME-WAIT)设置的 2MSL 后,A 才进入到CLOSED 状态
- 时间 MSL 叫最长报文段寿命(Maximum Segment Lifetime) RFC 793 建议设为 2 分钟。TCP 允许不同的实现根据具体使用清空使用更小的 MSL 值。
- 从 A 进入到 TIME-WAIT 状态后,要进过 4 分钟才能进入到 CLOSED 状态,才能开始建立下一个新连接。
- B 只要收到了 A 发出的确认,就进入 CLOSED 状态。
- 同样,B 在撤销响应的传输控制块 TCB 后,就结束了这次的 TCP 连接。
- 我们注意到,B 结束 TCP 连接的时间要比 A 早一些。
为什么 A 要再 TIME-WAIT 状态必须等到 2MSL 的时间呢。
- 为了保证 A 发送的最后一个 ACK 报文段能够到达 B
- 防止“已失效的连接请求报文段”出现在本连接中。
TCP 的有限状态机
HTTP
HTTP 属于应用层协议,是面向事务(transaction-oriented)的。 是万维网上能够可靠地交换文件的重要基础。 每个万维网网点都有一个服务器进程,它不断地监听 TCP 的端口 80,以便发现是否有浏览器向它发送连接建立请求。 HTTP 使用了面向连接的 TCP 作为运输层协议,保证了数据的可靠传输。但是HTTP 协议本身是无连接的。也就是说,虽然 HTTP 使用了 TCP 连接,但通信的双方在交换 HTTP 报文之前是不需要先建立 HTTP 连接。 HTTP 协议是无状态的。也就是说,同一个客户第二次访问同一个服务器上的页面时,服务器的相应与第一次被访问时的相同(假定服务器还没有把该页面更新),因为服务器并不记得曾经访问过的这个客户,也不记得为该客户曾经服务过多少次。 当用户在点击鼠标链接按某个 link 时,HTTP 协议首先要和服务器建立 TCP 链接。这需要使用三次报文握手。当建立 TCP 连接的三报文握手的前两部分完成后(经过了一个 RTT 时间),客户端就把 HTTP 请求报文,作为建立 TCP 连接三报文握手中的第三个报文的数据,发送给服务器。服务器收到 HTTP 请求报文后,就把所请求的文档作为响应报文返回给客户。
请求一个万维网文档所需的时间是该文档的传输时间加上两倍往返 RTT。一个 RTT 用于建立 TCP 连接,另一个 RTT 用于请求和接受相应数据。TCP 建立连接的三报文握手的第三个报文段中的数据,就是客户端对服务端的请求报文。 HTTP/1.0 的主要缺点,就是每请求一个文档就要有两倍 RTT 的开销。 HTTP/1.1 协议较好的解决了这个问题,它使用了持续连接(persistent conncetion)。所谓持续连接就是服务器再发送响应后仍然在一段时间内保持这条连接,使同一个客户(浏览器)和该服务器可以继续在这条连接上传送后续的 HTTP 请求报文和响应报文。
HTTP 报文结构
HTTP 有两类报文:
- 请求报文 -- 从客户向服务器发送请求报文。
- 响应报文 -- 从服务器到客户的回答。
请求报文
响应报文
Cookie
HTTP 是无状态的,但实际工作中,一些站点常常希望能够识别用户,跟踪用户状态。要做到这一点,可以在 HTTP 中使用 Cookie,Cookie 表示 HTTP 服务器和客户之间传递的状态信息。
cookie 的工作原理
当用户 A 浏览某个使用 Cookie 的网站时,该网站的服务器就位 A 产生一个唯一的识别码,并以此作为索引在服务器的后端数据库中产生一个项目。接着在给 A 的 HTTP 响应报文中添加一个叫做 Set-cookie 的首部行。
Set-cookie: 31d5d96e307aad66
当 A 接收到这个响应时,其浏览器就在它管理的特定 Cookie 文件中添加一行,其中包括这个服务器的主机名和Set-cookie 后面给出的识别码。当 A 继续浏览这个网站时,每发送一个 HTTP 请求报文, 其浏览器就会从其 Cookie 文件中取出这个网站的识别码,并放到 HTTP 请求报文的 Cookie 首部行中
Cookie: 31d5d96e307aad66
于是,这个网站就能跟踪用户 31d5d96e307aad66(用户 A)在该网站的活动。
参考
- 计算机网络 第 7 版 谢希仁
- 原来 TCP 为了保证可靠传输做了这么多