一、网络体系结构

推荐的连接
- 《计算机网络体系结构综述(上)》
- 《计算机网络体系结构综述(下)》
七层架构主要包括:
- 物理层:
主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用 是传输比特流(就是由 1、0 转化为电流强弱来进行传输,到达目的地后在转化为 1、0,也就是我们常说的模数转换与数模转换)。这一层的数据叫做比特。
- 数据链路层:
主要将从物理层接收的数据进行 MAC 地址(网卡的地址)的封装与解封装。常把这一层的数据叫做帧。在这一层工作的设备是交换机,数据通过交换机来传输。
**
- 网络层:
主要将从下层接收到的数据进行 IP 地址(例 192.168.0.1)的封装与解封装。在这一层工 作的设备是路由器,常把这一层的数据叫做数据包。
**
- 传输层:
定义了一些传输数据的协议和端口号(WWW 端口 80 等),
如:TCP(传输控制协议, 传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议, 与 TCP 特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如 QQ 聊天数据就是通过这 种方式传输的)。 主要是将从下层接收的数据进行分段进行传输,到达目的地址后在进行重组。 常常把这一层数据叫做段。
- 会话层:
==通过传输层(端口号:传输端口与接收端口)建立数据传输的通路==。主要在你的系统之间 发起会话或或者接受会话请求(设备之间需要互相认识可以是 IP 也可以是 MAC 或者是主机名)
- 表示层:
主要是进行对接收的数据进行解释、加密与解密、压缩与解压缩等(也就是把计算机能够识别的东西转换成人能够能识别的东西(如图片、声音等))
- 应用层:
主要是一些终端的应用,比如说 FTP(各种文件下载),WEB(IE 浏览),QQ 之类的(你 就把它理解成我们在电脑屏幕上可以看到的东西.就 是终端应用)。
各层使用的数据交换设备:
- 网关:应用层、传输层。> 网关在传输层上以实现网络互连,是最复杂的网络互连设备,仅用于两个高层协议不同的网络互连。
网关的结构也和路由器类似,不同的是互连层。网关既可以用于广域网互连,也可以用于局域网互连。
- 【重点】路由器:网络层> 路由选择、存储转发
- 【重点】交换机:数据链路层、网络层> 识别数据包中的 MAC 地址信息,根据 MAC 地址进行转发,并将这些 MAC 地址与对应的端口记录在自己内部的一个地址表中。
- 网桥:数据链路层> 将两个 LAN 连起来,根据 MAC 地址来转发帧。
- 集线器(Hub):物理层> 纯硬件设备,主要用来连接计算机等网络终端。
- 中继器:物理层> 在比特级别对网络信号进行再生和重定时,从而使得它们能够在网络上传输更长的距离。
二、IP
IP 地址的分类
IP 地址是指互联网协议地址,是 IP 协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。
IP 地址编址方案将IP地址空间划分为 A、B、C、D、E 五类,其中 A、B、C 是基本类,D、E 类作为多播和保留使用,为特殊地址。
每个 IP 地址包括两个标识码(ID),即网络 ID 和主机 ID 。同一个物理网络上的所有主机都使用同一个网络 ID ,网络上的一个主机(包括网络上工作站,服务器和路由器等)有一个主机 ID 与其对应。A~E 类地址的特点如下:
- A 类地址:以 0 开头,第一个字节范围:0~127 。
- B 类地址:以 10 开头,第一个字节范围:128~191 。
- C 类地址:以 110 开头,第一个字节范围:192~223。
- D 类地址:以 1110 开头,第一个字节范围:224~239 。
- E 类地址:以 1111 开头,保留地址。
详细的,可以看看 《IP 地址分类(A类 B类 C类 D类 E类)》 文章。
IP 地址与物理地址的区别
- 物理地址(MAC 地址),是数据链路层和物理层使用的地址。
- IP 地址是网络层和以上各层使用的地址,是一种逻辑地址。
- 其中 ARP 协议用于 IP 地址与物理地址的对应。
详细的,可以看看 《即生瑜,何生亮 —— MAC 地址与 IP 地址》 文章。
网络层的 ARP 协议工作原理
网络层的 ARP 协议完成了 IP 地址与物理地址的映射。
- 首先,每台主机都会在自己的 ARP 缓冲区中建立一个 ARP 列表,以表示 IP 地址和 MAC 地址的对应关系。
- 当源主机需要将一个数据包要发送到目的主机时,会首先检查自己 ARP 列表中是否存在该 IP 地址对应的 MAC 地址:
- 如果有,就直接将数据包发送到这个 MAC 地址。
- 如果没有,就向本地网段发起一个ARP请求的广播包,查询此目的主机对应的 MAC 地址。此 ARP 请求数据包里包括源主机的 IP 地址、硬件地址、以及目的主机的 IP 地址。网络中所有的主机收到这个 ARP 请求后,会检查数据包中的目的 IP 是否和自己的 I P地址一致。
- 如果不相同,就忽略此数据包。
- 如果相同,该主机首先将发送端的 MAC 地址和 IP 地址添加到自己的 ARP 列表中(如果 ARP 表中已经存在该 IP 的信息,则将其覆盖),然后给源主机发送一个 ARP 响应数据包,告诉对方自己是它需要查找的 MAC 地址。
- 源主机收到这个 ARP 响应数据包后,将得到的目的主机的 IP 地址和 MAC 地址添加到自己的 ARP 列表中,并利用此信息开始数据的传输。
- 如果源主机一直没有收到 ARP 响应数据包,表示 ARP 查询失败。
注意,在 OSI 模型中 ARP 协议属于链路层;而在 TCP/IP 模型中,ARP 协议属于网络层。
三、TCP
TCP(Transmission Control Protocol),传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。主要特点如下:
- TCP 是面向连接的。> 就好像打电话一样,通话前需要先拨号建立连接,通话结束后要挂机释放连接
- 每一条 TCP 连接只能有两个端点,每一条TCP连接只能是点对点的(一对一)。
- TCP 提供可靠交付的服务。通过TCP连接传送的数据,无差错、不丢失、不重复、并且按序到达。
- TCP 提供全双工通信。TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接收缓存,用来临时存放双方通信的数据。
- ==面向字节流。==> TCP 中的“流”(Stream),指的是流入进程或从进程流出的字节序列。
“面向字节流”的含义是:虽然应用程序和 TCP 的交互是一次一个数据块(大小不等),但 TCP 把应用程序交下来的数据仅仅看成是一连串的无结构的字节流。
数据包说明
- 源端口号( 16 位):它(连同源主机 IP 地址)标识源主机的一个应用进程。
- 目的端口号( 16 位):==它(连同目的主机 IP 地址)标识目的主机的一个应用进程。这两个值加上 IP 报头中的源主机 IP 地址和目的主机 IP 地址唯一确定一个 TCP 连接。==
- 顺序号 seq( 32 位):用来标识从 TCP 源端向 TCP 目的端发送的数据字节流,它表示在这个报文段中的第一个数据字节的顺序号。如果将字节流看作在两个应用程序间的单向流动,则 TCP 用顺序号对每个字节进行计数。序号是 32bit 的无符号数,序号到达 2 的 32 次方 - 1 后 又从 0 开始。当建立一个新的连接时, SYN 标志变 1 ,顺序号字段包含由这个主机选择的该 连接的初始顺序号 ISN ( Initial Sequence Number )。
- 确认号 ack( 32 位):包含发送确认的一端所期望收到的下一个顺序号。因此,确认序号应当是上次已成功收到数据字节顺序号加 1 。==只有 ACK 标志为 1 时确认序号字段才有效。== TCP 为 应用层提供全双工服务,这意味数据能在两个方向上独立地进行传输。因此,连接的每一端必 须保持每个方向上的传输数据顺序号。
- TCP 报头长度( 4 位):给出报头中 32bit 字的数目,它实际上指明数据从哪里开始。需要这 个值是因为任选字段的长度是可变的。这个字段占 4bit ,因此 TCP 最多有 60 字节的首部。然 而,没有任选字段,正常的长度是 20 字节。
- 保留位( 6 位):保留给将来使用,目前必须置为 0 。
- 控制位( control flags , 6 位):在 TCP 报头中有 6 个标志比特,它们中的多个可同时被设置为 1 。依次为:
- URG :为 1 表示紧急指针有效,为 0 则忽略紧急指针值。
- ACK :为 1 表示确认号有效,为 0 表示报文中不包含确认信息,忽略确认号字段。
- PSH :为 1 表示是带有 PUSH 标志的数据,指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满。
- RST :用于复位由于主机崩溃或其他原因而出现错误的连接。它还可以用于拒绝非法的报文段和拒绝连接请求。一般情况下,如果收到一个 RST 为 1 的报文,那么一定发生了某些问题。
- SYN :同步序号,为 1 表示连接请求,用于建立连接和使顺序号同步( synchronize )。
- FIN :用于释放连接,为 1 表示发送方已经没有数据发送了,即关闭本方数据流。
- 窗口大小( 16 位):数据字节数,表示从确认号开始,本报文的源方可以接收的字节数,即源 方接收窗口大小。窗口大小是一个 16bit 字段,因而窗口大小最大为 65535 字节。
- 校验和( 16 位):此校验和是对整个的 TCP 报文段,包括 TCP 头部和 TCP 数据,以 16 位字 进行计算所得。这是一个强制性的字段,一定是由发送端计算和存储,并由接收端进行验证。
- 紧急指针(16位):只有当URG标志置1时紧急指针才有效。TCP的紧急方式是发送端向另 一端发送紧急数据的一种方式。
- 选项:最常见的可选字段是最长报文大小,又称为MSS(MaximumSegmentSize)。每个连 接方通常都在通信的第一个报文段(为建立连接而设置 SYN 标志的那个段)中指明这个选项, 它指明本端所能接收的最大长度的报文段。选项长度不一定是 32 位字的整数倍,所以要加填充 位,使得报头长度成为整字数。
- 数据: TCP 报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报 文段仅有 TCP 首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数 据。在处理超时的许多情况中,也会发送不带任何数据的报文段。
TCP 对应的应用层协议
- FTP :定义了文件传输协议,使用 21 端口。常说某某计算机开了 FTP 服务便是启动了文件传输服务。下载文件,上传主页,都要用到 FTP 服务。
- Telnet :它是一种用于远程登陆的端口,用户可以以自己的身份远程连接到计算机上,通过这种端口可以提供一种基于 DOS 模式下的通信服务。如以前的 BBS 是纯字符界面的,支持 BBS 的服务器将 23 端口打开,对外提供服务。
- 邮箱
- SMTP :定义了简单邮件传送协议,现在很多邮件服务器都用的是这个协议,用于发送邮件。如常见的免费邮件服务中用的就是这个邮件服务端口,所以在电子邮件设置-中常看到有这么 SMTP 端口设置这个栏,服务器开放的是 25 号端口。
- POP3 :它是和 SMTP 对应,POP3 用于接收邮件。通常情况下,POP3 协议所用的是 110 端口。也是说,只要你有相应的使用 POP3 协议的程序(例如 Foxmail 或 Outlook),就可以不以 Web 方式登陆进邮箱界面,直接用邮件程序就可以收到邮件(如是 163 邮箱就没有必要先进入网易网站,再进入自己的邮箱来收信)。
HTTP :从 Web 服务器传输超文本到本地浏览器的传送协议。
TCP 头部
-
【重要】TCP 三次握手
三次握手,简单来说,就是:
TCP 三次握手的漫画 发送方:我要和你建立链接?
- 接收方:你真的要和我建立链接么?
- 发送方:我真的要和你建立链接,成功。
ACK :为 1 表示确认号有效,为 0 表示报文中不包含确认信息,忽略确认号字段。 SYN :同步序号,为 1 表示连接请求,用于建立连接和使顺序号同步( synchronize )。 顺序号 seq( 32 位):用来标识从 TCP 源端向 TCP 目的端发送的数据字节流,它表示在这个报文段中的第一个数据字节的顺序号。如果将字节流看作在两个应用程序间的单向流动,则 TCP 用顺序号对每个字节进行计数。序号是 32bit 的无符号数,序号到达 2 的 32 次方 - 1 后 又从 0 开始。当建立一个新的连接时, SYN 标志变 1 ,顺序号字段包含由这个主机选择的该 连接的初始顺序号 ISN ( Initial Sequence Number )。 确认号 ack( 32 位):包含发送确认的一端所期望收到的下一个顺序号。因此,确认序号应当是上次已成功收到数据字节顺序号加 1 。只有 ACK 标志为 1 时确认序号字段才有效。 TCP 为 应用层提供全双工服务,这意味数据能在两个方向上独立地进行传输。因此,连接的每一端必 须保持每个方向上的传输数据顺序号。
- 第一次握手:Client 将标志位
SYN=1,随机产生一个值seq=J,并将该数据包发送给 Server 。此时,Client 进入SYN_SENT 状态,等待 Server 确认。 - 第二次握手:Server 收到数据包后由标志位
SYN=1知道Client请求建立连接,Server 将标志位SYN和ACK都置为 1 ,ack=J+1,随机产生一个值seq=K,并将该数据包发送给 Client 以确认连接请求,Server 进入SYN_RCVD状态。此时,Server 进入 SYC_RCVD 状态。 - 第三次握手:Client 收到确认后,检查ack是否为J+1,ACK是否为 1 。
- 如果正确,则将标志位
ACK置为 1 ,ack=K+1,并将该数据包发送给 Server 。此时,Client 进入 ESTABLISHED 状态。 - Server 检查
ack是否为K+1,ACK是否为 1 ,如果正确则连接建立成功。此时 Server 进入 ESTABLISHED 状态,完成三次握手,随后 Client 与 Server 之间可以开始传输数据了。
- 如果正确,则将标志位
- 仔细看来,Client 会发起两次数据包,分别是
SYNC和ACK;Server 会发起一次数据包,包含SYNC和ACK。也就是说,三次握手的过程中,Client 和 Server 互相做了一次SYNC和ACK。三次握手的原因
第三次握手是为了防止失效的连接请求到达服务器,让服务器错误打开连接。
客户端发送的连接请求如果在网络中滞留,那么就会隔很长一段时间才能收到服务器端发回的连接确认。客户端等待一个超时重传时间之后,就会重新请求连接。但是这个滞留的连接请求最后还是会到达服务器,如果不进行三次握手,那么服务器就会打开两个连接。如果有第三次握手,客户端会忽略服务器之后发送的对滞留连接请求的连接确认,不进行第三次握手,因此就不会再次打开连接。为什么 TCP 连接需要三次握手
为了防止已失效的连接请求报文突然又传送到了服务端,因而产生错误。客户端发出的连接请求报文并未丢失,而是在某个网络节点长时间滞留了,以致延误到链接释放以后的某个时间才到达 Server 。
- 若不采用“三次握手”,那么只要 Server 发出确认数据包,新的连接就建立了。由于 Client 此时并未发出建立连接的请求,所以其不会理睬 Server 的确认,也不与 Server 通信;而这时 Server 一直在等待 Client 的请求,这样 Server 就白白浪费了一定的资源。
- 若采用“三次握手”,在这种情况下,由于 Server 端没有收到来自客户端的确认,则就会知道 Client 并没有要求建立请求,就不会建立连接。
在 《通俗大白话来理解 TCP 协议的三次握手和四次挥手》 中,搜 “为什么要三次握手” 关键字,也有非常好的解答。
如下使用 Client 和 Server 的方式,仅仅是为了方便,也是可以从 Server 向 Client 发起。
- 第一次挥手:Client 发送一个
FIN=M,用来关闭 Client 到 Server 的数据传送。此时,Client 进入FIN_WAIT_1 状态。 - 第二次挥手,Server 收到
FIN后,发送一个ACK给 Client ,确认序号ack为M+1(与SYN相同,一个FIN占用一个序号)。此时,Server 进入 CLOSE_WAIT 状态。==注意,TCP 链接处于半关闭状态,即客户端已经没有要发送的数据了,但服务端若发送数据,则客户端仍要接收。== - 第三次挥手,Server 发送一个
FIN=N,用来关闭 Server 到 Client 的数据传送。此时 Server 进入 LAST_ACK 状态。 - 第四次挥手,Client 收到
FIN后,此时 Client 进入 TIME_WAIT 状态(此时 TCP 连接还没有释放掉,然后经过时间等待计时器设置的 2MSL 后,A 才进入到close 状态)。接着,Client 发送一个ACK给 Server ,确认序号为N+1。Server 接收到后,此时 Server 进入 CLOSED 状态,完成四次挥手。> 去向ACK消息最大存活时间(MSL) + 来向FIN消息的最大存活时间(MSL)。恰恰就是2MSL( Maximum Segment Life)。等待2MSL时间,客户端就可以放心地释放TCP占用的资源、端口号,此时可以使用该端口号连接任何服务器。
为什么要四次挥手?
TCP 协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP 是全双工模式,这就意味着:
- 当主机 1 发出
FIN报文段时,只是表示主机 1 已经没有数据要发送了,主机 1 告诉主机 2 ,它的数据已经全部发送完毕了;但是,这个时候主机 1 还是可以接受来自主机 2 的数据;==当主机 2 返回ACK报文段时,表示它已经知道主机 1 没有数据发送了,但是主机 2 还是可以发送数据到主机 1 的。== - 当主机 2 也发送了
FIN报文段时,这个时候就表示主机 2 也没有数据要发送了,就会告诉主机 1 ,我也没有数据要发送了,之后彼此就会愉快的中断这次 TCP 连接。TCP 建立连接要进行三次握手,而断开连接要进行四次。这是由于 TCP 的半关闭造成的。因为 TCP 连接是全双工的(即数据可在两个方向上同时传递)所以进行关闭时每个方向上都要单独进行关闭。这个单方向的关闭就叫半关闭。当一方完成它的数据发送任务,就发送一个 FIN 来向另一方通告将要终止这个方向的连接。
如果要正确的理解四次的原理,就需要了解四次挥手过程中的状态变化。
主动方=发送方;被动方=接收方。 状态前面的(主动方)(被动方),表示该状态属于谁。
- (主动方)FIN_WAIT_1 :其实 FIN_WAIT_1 和 FIN_WAIT_2 状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:
- FIN_WAIT_1 状态实际上是当 Socket 在 ESTABLISHED 状态时,它想主动关闭连接,向对方发送了
FIN报文,此时该 Socket 即进入到 FIN_WAIT_1 状态。 - 而当对方回应
ACK报文后,则进入到 FIN_WAIT_2 状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文。所以, FIN_WAIT_1 状态一般是比较难见到的,而 FIN_WAIT_2 状态还有时常常可以用 netstat 看到。
- FIN_WAIT_1 状态实际上是当 Socket 在 ESTABLISHED 状态时,它想主动关闭连接,向对方发送了
- (主动方)FIN_WAIT_2 :上面已经详细解释了这种状态,实际上 FIN_WAIT_2 状态下的 Socket,表示半连接,也即有一方要求 close 连接,但另外还告诉对方,我暂时还有点数据需要传送给你(
ACK信息),稍后再关闭连接。 - (被动方)CLOSE_WAIT :这种状态的含义其实是表示在等待关闭。即当对方 close 一个 Socket 后发送
FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到 CLOSE_WAIT 状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以 close 这个 Socket ,发送FIN报文给对方,也即关闭连接。所以你在 CLOSE_WAIT 状态下,需要完成的事情是等待你去关闭连接。 - (被动方)LAST_ACK :这个状态还是比较容易好理解的,它是被动关闭一方在发送
FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到 CLOSED 可用状态了。 - (主动方)TIME_WAIT :表示收到了对方的
FIN报文,并发送出了ACK报文,就等 2MSL 后即可回到 CLOSED 可用状态了。如果 FIN_WAIT_1 状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到 TIME_WAIT 状态,而无须经过 FIN_WAIT_2 状态。> 为何一定要等 2MSL ?如果不等,释放的端口可能会重连刚断开的服务器端口,这样依然存活在网络里的老的 TCP 报文可能与新 TCP 连接报文冲突,造成数据冲突,为避免此种情况,需要耐心等待网络老的 TCP 连接的活跃报文全部死翘翘,2MSL 时间可以满足这个需求(尽管非常保守)!更多,可以看看知乎 《为什么 TCP 4 次挥手时等待为 2MSL?》 的讨论。
- CLOSED :表示连接中断。
另外,关于 TIME_WAIT 和 CLOSE_WAIT 状态的区别《TIME_WAIT 和 CLOSE_WAIT 状态区别》 。
【重要】TCP 数据如何传输?
- 上图给出了主机 A 分 2 次(分 2 个数据包)向主机 B 传递 200 字节的过程。
- 首先,主机 A 通过 1 个数据包发送 100 个字节的数据,数据包的Seq号设置为 1200 。主机 B 为了确认这一点,向主机 A 发送ACK包,并将Ack号设置为 1301 。
- 为了保证数据准确到达,目标机器在收到数据包(包括
SYN包、FIN包、普通数据包等)包后必须立即回传ACK包,这样发送方才能确认数据传输成功。 - 此时Ack号为 1301 而不是 1201,==原因在于Ack号的增量为传输的数据字节数。假设每次Ack号不加传输的字节数,这样虽然可以确认数据包的传输,但无法明确 100 字节全部正确传递还是丢失了一部分,比如只传递了 80 字节。因此按如下的公式确认Ack号:Ack =Seq号 + 传递的字节数 + 1 。==
- 与三次握手协议相同,最后加 1 是为了告诉对方要传递的
Seq号。
- 与三次握手协议相同,最后加 1 是为了告诉对方要传递的
- 为了保证数据准确到达,目标机器在收到数据包(包括
TCP 数据传输丢失怎么办(TCP重传,通过定时器实现)
也可以改成提问,什么是 TCP 重传。
因为各种原因,TCP 数据包可能存在丢失的情况,TCP 会进行数据重传。如下图所示:
- 上图表示通过
Seq1301 数据包向主机 B 传递 100 字节的数据,但中间发生了错误,主机 B 未收到。经过一段时间后,主机 A 仍未收到对于Seq1301 的ACK确认,因此尝试重传数据。==为了完成数据包的重传,TCP 套接字每次发送数据包时都会启动定时器,如果在一定时间内没有收到目标机器传回的ACK包,那么定时器超时,数据包会重传。==上图演示的是数据包丢失的情况,也会有ACK包丢失的情况,一样会重传。 - 重传超时时间(RTO,Retransmission Time Out)> 这个值太大了会导致不必要的等待,太小会导致不必要的重传,理论上最好是网络 RTT 时间,但又受制于网络距离与瞬态时延变化,所以实际上使用自适应的动态算法(例如 Jacobson 算法和 Karn 算法等)来确定超时时间。
==往返时间(RTT,Round-Trip Time)表示从发送端发送数据开始,到发送端收到来自接收端的
ACK确认包(接收端收到数据后便立即确认),总共经历的时延。==
- 重传次数> TCP 数据包重传次数,根据系统设置的不同而有所区别。有些系统,一个数据包只会被重传 3 次,如果重传 3 次后还未收到该数据包的
ACK确认,就不再尝试重传。但有些要求很高的业务系统,会不断地重传丢失的数据包,以尽最大可能保证业务数据的正常交互。最后需要说明的是,==发送端只有在收到对方的
ACK确认包后,才会清空输出缓冲区中的数据==。
ps:TCP 数据传输的过程,和 MQ Broker 投递消息给 Consumer 是一样的,只有在 Consumer Ack 确认消息已经消费,该消息才不会再被投递给 Consumer 。
另外,也推荐阅读 《网络基本功(九):细说TCP重传》 。
【重要】TCP 滑动窗口
在看 TCP 滑动窗口的概念之前,我们先来看看它出现的背景?
将 TCP 与 UDP 这样的简单传输协议区分开来的是,它传输数据的质量。TCP 对于发送数据进行跟踪,这种数据管理需要协议有以下两大关键功能:
- 可靠性:保证数据确实到达目的地。如果未到达,能够发现并重传。
- 数据流控:管理数据的发送速率,以使接收设备不致于过载。
要完成这些任务,整个协议操作是围绕==滑动窗口 + 确认机制==来进行的。因此,理解了滑动窗口,也就是理解了 TCP 。
那么,到底什么是 TCP 滑动窗口呢?
滑动窗口协议,是传输层进行流控的一种措施,接收方通过通告发送方自己的窗口大小,从而控制发送方的发送速度,从而达到防止发送方发送速度过快而导致自己被淹没的目的。 TCP 的滑动窗口解决了端到端的流量控制问题,允许接受方对传输进行限制,直到它拥有足够的缓冲空间来容纳更多的数据。 ==TCP将独立的字节数据当作流来处理。一次发送一个字节并接收一次确认显然是不可行的。即使重叠传输(即不等待确认就发送下一个数据),速度也还是会非常缓慢。==
TCP消息确认机制如上图所示,首先,每一条消息都有一个识别编号,每一条消息都能够被独立地确认,因此同一时刻可以发送多条信息。设备B定期发送给A一条发送限制参数,制约设备A一次能发送的消息最大数量。设备B可以对该参数进行调整,以控制设备A的数据流。 为了提高速度,TCP并没有按照字节单个发送而是将数据流划分为片段。片段内所有字节都是一起发送和接收的,因此也是一起确认的。确认机制没有采用message ID字段,而是使用的片段内最后一个字节的sequence number。因此一次可以处理不同的字节数,这一数量即为片段内的sequence number。 ==TCP数据流的概念划分类别== 假设A和B之间新建立了一条TCP连接。设备A需要传送一长串数据流,但设备B无法一次全部接收,所以它限制设备A每次发送分段指定数量的字节数,直到分段中已发送的字节数得到确认。之后,设备A可以继续发送更多字节。每一个设备都对发送,接收及确认数据进行追踪。 如果我们在任一时间点对于这一过程做一个“快照”,那么我们可以将TCP buffer中的数据分为以下四类,并把它们看作一个时间轴: 1. 已发送已确认 数据流中最早的字节已经发送并得到确认。这些数据是站在发送设备的角度来看的。如下图所示,31个字节已经发送并确认。 2. 已发送但尚未确认 已发送但尚未得到确认的字节。发送方在确认之前,不认为这些数据已经被处理。下图所示14字节为第2类。 3. 未发送而接收方已Ready 设备尚未将数据发出,但接收方根据最近一次关于发送方一次要发送多少字节确认自己有足够空间。发送方会立即尝试发送。如图,第3类有6字节。 4. 未发送而接收方Not Ready 由于接收方not ready,还不允许将这部分数据发出。
接收方采用类似的机制来区分已接收并已确认,尚未接受但准备好接收,以及尚未接收并尚未准备好接收的数据。实际上,收发双方各自维护一套独立的变量,来监控发送和接收的数据流落在哪一类。
滑动窗口的原理
TCP是一个可靠的传输协议,所以需要对数据进行确认。**TCP协议里窗口机制有2种:一种是固定的窗口大小;一种是滑动的窗口。**这个窗口大小就是我们一次传输几个数据,==对所有数据帧按顺序赋予编号,发送方在发送过程中始终保持着一个发送窗口,只有落在发送窗口内的帧才允许被发送;同时接收方也维持着一个接收窗口,只有落在接收窗口内的帧才允许接收。==这样通过调整发送方窗口和接收方窗口的大小可以实现流量控制。<br /> ==TCP滑动窗口技术通过动态改变窗口大小来调节两台主机间数据传输。==每个TCP/IP主机支持全双工数据传输,因此**TCP有两个滑动窗口:一个用于接收数据,另一个用于发送数据。**TCP使用肯定确认技术,==其确认号指的是下一个所期待的字节。==
假定发送方设备以每一次三个数据包的方式发送数据,也就是说,窗口大小为3。发送方发送序列号为1、2、3的三个数据包,接收方设备成功接收数据包,用序列号4(窗口大小+1)确认。发送方设备收到确认,继续以窗口大小3发送数据。当接收方设备要求降低或者增大网络流量时,可以对窗口大小进行减小或者增加,本例降低窗口大小为2,每一次发送两个数据包。==当接收方设备要求窗口大小为0,表明接收方已经接收了全部数据,或者接收方应用程序没有时间读取数据,要求暂停发送。==发送方接收到携带窗口号为0的确认,停止这一方向的数据传输。
注意:这里应该是ack而不是ACK,ACK是控制位中的一位,为 1 表示确认号有效,为 0 表示报文中不包含确认信息,忽略确认号字段。
这里我们可以看到假设窗口的大小是1,**也是就每次只能发送一个数据只有接受方对这个数据进行确认了以后才能发送第2个数据。**我们可以看到发送方每发送一个数据,接受方就要给发送方一个ACK对这个数据进行确认。==只有接受到了这个确认数据以后发送方才能传输下个数据。== 这样我们考虑一下如果说窗口过小,那么当传输比较大的数据的时候需要不停的对数据进行确认,这个时候就会造成很大的延迟。如果说窗口的大小定义的过大。我们假设发送方一次发送100个数据。但是接收方只能处理50个数据。这样每次都会只对这50个数据进行确认。发送方下一次还是发送100个数据,但是接受方还是只能处理50个数据。这样就避免了不必要的数据来拥塞我们的链路。所以我们就引入了滑动窗口机制,窗口的大小并不是固定的而是根据我们之间的链路的带宽的大小,这个时候链路是否拥护塞。接受方是否能处理这么多数据了。<br />结合下图看看滑动窗口是如何工作的 [](https://camo.githubusercontent.com/50eda864c5722624340c1019d19c715857e0b41d/68747470733a2f2f6c6561726e696e67706963732e6f73732d636e2d7368656e7a68656e2e616c6979756e63732e636f6d2f696d616765732f3832313237362d32303135313031363230343031343336362d35343732333538382e706e67)<br /> 首先是**第一次发送数据这个时候的窗口大小是根据链路带宽的大小来决定的**。我们假设这个时候窗口的大小是3。这个时候接受方收到数据以后会对数据进行确认告诉发送方我下次希望手到的是数据是多少。这里我们看到**接收方发送的ACK=3(这是发送方发送序列2的回答确认,下一次接收方期望接收到的是3序列信号)**。这个时候发送方收到这个数据以后就知道我第一次发送的3个数据对方**只收到了2个**。就知道第3个数据对方没有收到。下次在发送的时候就从第3个数据开始发。这个时候**窗口大小就变成了2** 。[](https://camo.githubusercontent.com/d27deb10debb40672ad180feba2c62365b2fb36e/68747470733a2f2f6c6561726e696e67706963732e6f73732d636e2d7368656e7a68656e2e616c6979756e63732e636f6d2f696d616765732f3832313237362d32303135313031363230343034383131362d3431363434353331342e706e67)<br />这个时候发送方发送2个数据。<br />[](https://camo.githubusercontent.com/b5a11b53a1eeff8f96022268c7e4edc879e6c27b/68747470733a2f2f6c6561726e696e67706963732e6f73732d636e2d7368656e7a68656e2e616c6979756e63732e636f6d2f696d616765732f3832313237362d32303135313031363230343134383336362d313839363638373331322e706e67)<br /> 看到接收方发送的ACK是5就表示他下一次希望收到的数据是5,发送方就知道我刚才发送的2个数据对方收了这个时候开始发送第5个数据。<br /> 这就是滑动窗口的工作机制,当链路变好了或者变差了这个窗口还会发生变化,并不是第一次协商好了以后就永远不变了。
滑动窗口协议,是TCP使用的一种流量控制方法。==该协议允许发送方在停止并等待确认前可以连续发送多个分组。==由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输。 只有在接收窗口向前滑动时(与此同时也发送了确认),发送窗口才有可能向前滑动。 收发两端的窗口按照以上规律不断地向前滑动,因此这种协议又称为滑动窗口协议。**当发送窗口和接收窗口的大小都等于1时,就是停止等待协议。**
参照下面:
- 《TCP 滑动窗口控制流量的原理》
- 《网络基本功(八):细说 TCP 滑动窗口》
-
TCP 拥堵
计算机网络中的带宽、交换结点中的缓存及处理机等都是网络的资源。在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就会变坏,这种情况就叫做拥塞。
🦅 怎么解决 TCP 拥堵?
通过拥塞控制来解决。拥堵控制,就是防止过多的数据注入网络中,这样可以使网络中的路由器或链路不致过载。注意,拥塞控制和流量控制不同,前者是一个全局性的过程,而后者指点对点通信量的控制。
拥塞控制的方法主要有以下四种: 1、慢开始。
- 2、拥塞避免。
- 3、快重传。
- 4、快恢复。
1)慢开始
不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小。
2)拥塞避免
拥塞避免算法,让拥塞窗口缓慢增长,即每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1 ,而不是加倍,这样拥塞窗口按线性规律缓慢增长。
3)快重传
快重传,要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方),而不要等到自己发送数据时捎带确认。
快重传算法规定,发送方只要一连收到三个重复确认,就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。
4)快恢复
快重传配合使用的还有快恢复算法,当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把 ssthresh 门限减半。
- 但是接下去并不执行慢开始算法:因为如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。
- 所以此时不执行慢开始算法,而是将 cwnd 设置为 ssthresh 的大小,然后执行拥塞避免算法。
四、UDP
UDP(User Data Protocol,用户数据报协议),是与 TCP 相对应的协议。它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去。
主要特点如下:
- UDP 是无连接的。
- UDP 使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的链接状态(这里面有许多参数)。
- UDP 是面向报文的。
- UDP 没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低。> ==对实时应用很有用,如 直播,实时视频会议等==
- UDP 支持一对一、一对多、多对一和多对多的交互通信。
UDP 的首部开销小,只有 8 个字节,比 TCP 的 20 个字节的首部要短。
UDP在应用层协议中的应用
DNS :用于域名解析服务,将域名地址转换为 IP 地址。DNS 用的是 53 号端口。
- SNMP :简单网络管理协议,使用 161 号端口,是用来管理网络设备的。由于网络设备很多,无连接的服务就体现出其优势。
TFTP(Trivial File Transfer Protocol):简单文件传输协议,该协议在熟知端口 69 上使用 UDP 服务。
【重要】TCP 与 UDP 的区别
TCP(Transmission Control Protocol)和 UDP(User Datagram Protocol) 协议属于传输层协议,它们之间的区别包括:

TCP 是面向连接的;UDP 是无连接的。
- TCP 是可靠的;UDP 是不可靠的。
- TCP 只支持点对点通信;UDP 支持一对一、一对多、多对一、多对多的通信模式。
- TCP 是面向字节流的;UDP 是面向报文的。
- TCP 有拥塞控制机制;UDP 没有拥塞控制,适合媒体通信。
TCP 首部开销(20 个字节),比 UDP 的首部开销(8 个字节)要大。
TCP数据流模式和UDP数据报模式
所谓的“流模式”,是指TCP 发送端发送几次数据和接收端接收几次数据是没有必然联系的。
比如你通过 TCP 连接给另一端发送数据,你只调用了一次 write ,发送了 100 个字节,但是对方可以分 10 次收完,每次 10 个字节;你也可以调用 10 次 write ,每次 10 个字节,但是对方可以一次就收完。
- 原因:这是因为 TCP 是面向连接的,一个 Socket 中收到的数据都是由同一台主机发出,且有序地到达,所以每次读取多少数据都可以。
所谓的“数据报模式”,是指 UDP 发送端调用了几次 write ,接收端必须用相同次数的 read 读完。
- UDP 是基于报文的,在接收的时候,每次最多只能读取一个报文,报文和报文是不会合并的,如果缓冲区小于报文长度,则多出的部分会被丢弃。
- 原因:这是因为 UDP 是无连接的,只要知道接收端的 IP 和端口,任何主机都可以向接收端发送数据。这时候,如果一次能读取超过一个报文的数据,则会乱套。
UDP 报文的格式
-
五、DNS
域名解析,www.xxx.com 转换成 IP ,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的 IP 数串。
- DNS 协议运行在 UDP 协议之上,使用端口号 53 。
主机解析域名的顺序
- 浏览器缓存
- 找本机的 hosts 文件
- 路由缓存
- 找 DNS 服务器(本地域名、顶级域名、根域名)
- 迭代查询
- 递归查询
DNS 使用的协议
参见 《DNS使用的是 TCP 协议还是 UDP 协议》 文章。
既使用 TCP 又使用 UDP 。
- 区域传送时使用 TCP 协议。> - 辅域名服务器会定时(一般是 3 小时)向主域名服务器进行查询以便了解数据是否有变动。如有变动,则会执行一次区域传送,进行数据同步。区域传送将使用 TCP 而不是 UDP ,因为数据同步传送的数据量比一个请求和应答的数据量要多得多。
- TCP 是一种可靠的连接,保证了数据的准确性。
- 域名解析时使用 UDP 协议。> - 客户端向 DNS 服务器查询域名,一般返回的内容都不超过 512 字节,用 UDP 传输即可。> UDP 报文的最大长度为 512 字节。
- 不用经过 TCP 三次握手,这样 DNS 服务器负载更低,响应更快。虽然从理论上说,客户端也可以指定向 DNS 服务器查询的时候使用 TCP ,但事实上,很多 DNS 服务器进行配置的时候,仅支持 UDP 查询包。





