TCP/IP
TCP/IP 意味着 TCP 和 IP 在一起协同工作。
TCP 负责应用软件(比如你的浏览器)和网络软件之间的通信。
IP 负责计算机之间的通信。
TCP 负责将数据分割并装入 IP 包,然后在它们到达的时候重新组合它们。
IP 负责将包发送至接受者。
一、网络层中的 IP 协议
- IP(IPv4、IPv6)相当于 OSI 参考模型中的第3层——网络层。网络层的主要作用是“实现终端节点之间的通信”。这种终端节点之间的通信也叫“点对点通信”。
- 网络的下一层——数据链路层的主要作用是在互连同一种数据链路的节点之间进行包传递。而一旦跨越多种数据链路,就需要借助网络层。网络层可以跨越不同的数据链路,即使是在不同的数据链路上也能实现两端节点之间的数据包传输。
- IP 大致分为三大作用模块,它们是 IP 寻址、路由(最终节点为止的转发)以及 IP 分包与组包。
1. IP 地址
1.1 IP 地址概述
- 在计算机通信中,为了识别通信对端,必须要有一个类似于地址的识别码进行标识。在数据链路中的 MAC 地址正是用来标识同一个链路中不同计算机的一种识别码。
- 作为网络层的 IP ,也有这种地址信息,一般叫做 IP 地址。IP 地址用于在“连接到网络中的所有主机中识别出进行通信的目标地址”。因此,在 TCP/IP 通信中所有主机或路由器必须设定自己的 IP 地址。
- 不论一台主机与哪种数据链路连接,其 IP 地址的形式都保持不变。
- IP 地址(IPv4 地址)由32位正整数来表示。IP 地址在计算机内部以二进制方式被处理。然而,由于我们并不习惯于采用二进制方式,我们将32位的 IP 地址以每8位为一组,分成4组,每组以 “.” 隔开,再将每组数转换成十进制数。如下:
1.2 IP 地址由网络和主机两部分标识组成
- 如下图,网络标识在数据链路的每个段配置不同的值。网络标识必须保证相互连接的每个段的地址不相重复。而相同段内相连的主机必须有相同的网络地址。IP 地址的“主机标识”则不允许在同一个网段内重复出现。由此,可以通过设置网络地址和主机地址,在相互连接的整个网络中保证每台主机的 IP 地址都不会相互重叠。即 IP 地址具有了唯一性。
IP地址的主机标识
- 如下图,IP 包被转发到途中某个路由器时,正是利用目标 IP 地址的网络标识进行路由。因为即使不看主机标识,只要一见到网络标识就能判断出是否为该网段内的主机。
IP地址的网络标识
1.3 IP 地址的分类
- IP 地址分为四个级别,分别为A类、B类、C类、D类。它根据 IP 地址中从第 1 位到第 4 位的比特列对其网络标识和主机标识进行区分。
- A 类 IP 地址是首位以 “0” 开头的地址。从第 1 位到第 8 位是它的网络标识。用十进制表示的话,0.0.0.0~127.0.0.0 是 A 类的网络地址。A 类地址的后 24 位相当于主机标识。因此,一个网段内可容纳的主机地址上限为16,777,214个。
- B 类 IP 地址是前两位 “10” 的地址。从第 1 位到第 16 位是它的网络标识。用十进制表示的话,128.0.0.0~191.255.0.0 是 B 类的网络地址。B 类地址的后 16 位相当于主机标识。因此,一个网段内可容纳的主机地址上限为65,534个。
- C 类 IP 地址是前三位为 “110” 的地址。从第 1 位到第 24 位是它的网络标识。用十进制表示的话,192.0.0.0~223.255.255.0 是 C 类的网络地址。C 类地址的后 8 位相当于主机标识。因此,一个网段内可容纳的主机地址上限为254个。
- D 类 IP 地址是前四位为 “1110” 的地址。从第 1 位到第 32 位是它的网络标识。用十进制表示的话,224.0.0.0~239.255.255.255 是 D 类的网络地址。D 类地址没有主机标识,常用于多播。
- 在分配 IP 地址时关于主机标识有一点需要注意。即要用比特位表示主机地址时,不可以全部为 0 或全部为 1。因为全部为 0 只有在表示对应的网络地址或 IP 地址不可以获知的情况下才使用。而全部为 1 的主机通常作为广播地址。因此,在分配过程中,应该去掉这两种情况。这也是为什么 C 类地址每个网段最多只能有 254( 28 - 2 = 254)个主机地址的原因。
1.4 广播地址
- 广播地址用于在同一个链路中相互连接的主机之间发送数据包。将 IP 地址中的主机地址部分全部设置为 1,就成了广播地址。
- 广播分为本地广播和直接广播两种。在本网络内的广播叫做本地广播;在不同网络之间的广播叫做直接广播。
1.5 IP 多播
- 多播用于将包发送给特定组内的所有主机。由于其直接使用 IP 地址,因此也不存在可靠传输。
- 相比于广播,多播既可以穿透路由器,又可以实现只给那些必要的组发送数据包。请看下图:
- IP 多播
- 多播使用 D 类地址。因此,如果从首位开始到第 4 位是 “1110”,就可以认为是多播地址。而剩下的 28 位可以成为多播的组编号。
- 此外, 对于多播,所有的主机(路由器以外的主机和终端主机)必须属于 224.0.0.1 的组,所有的路由器必须属于 224.0.0.2 的组。
1.6 子网掩码
- 现在一个 IP 地址的网络标识和主机标识已不再受限于该地址的类别,而是由一个叫做“子网掩码”的识别码通过子网网络地址细分出比 A 类、B 类、C 类更小粒度的网络。这种方式实际上就是将原来 A 类、B 类、C 类等分类中的主机地址部分用作子网地址,可以将原网络分为多个物理网络的一种机制。
- 子网掩码用二进制方式表示的话,也是一个 32 位的数字。它对应 IP 地址网络标识部分的位全部为 “1”,对应 IP 地址主机标识的部分则全部为 “0”。由此,一个 IP 地址可以不再受限于自己的类别,而是可以用这样的子网掩码*地定位自己的网络标识长度。当然,子网掩码必须是 IP 地址的首位开始连续的 “1”。
- 对于子网掩码,目前有两种表示方式。***种是,将 IP 地址与子网掩码的地址分别用两行来表示。以 172.20.100.52 的前 26 位是网络地址的情况为例,如下:
- 第二种表示方式是,在每个 IP 地址后面追加网络地址的位数用 “/ ” 隔开,如下:
2. 路由
- 发送数据包时所使用的地址是网络层的地址,即 IP 地址。然而仅仅有 IP 地址还不足以实现将数据包发送到对端目标地址,在数据发送过程中还需要类似于“指明路由器或主机”的信息,以便真正发往目标地址。保存这种信息的就是路由控制表。
- 该路由控制表的形成方式有两种:一种是管理员手动设置,另一种是路由器与其他路由器相互交换信息时自动刷新。前者也叫做静态路由控制,而后者叫做动态路由控制。
- IP 协议始终认为路由表是正确的。然后,IP 本身并没有定义制作路由控制表的协议。即 IP 没有制作路由控制表的机制。该表示由一个叫做“路由协议”的协议制作而成。
2.1 IP 地址与路由控制
- IP 地址的网络地址部分用于进行路由控制。
- 路由控制表中记录着网络地址与下一步应该发送至路由器的地址。
- 在发送 IP 包时,首先要确定 IP 包首部中的目标地址,再从路由控制表中找到与该地址具有相同网络地址的记录,根据该记录将 IP 包转发给相应的下一个路由器。如果路由控制表中存在多条相同网络地址的记录,就选择一个最为吻合的网络地址。
3. IP 分包与组包
- 每种数据链路的***传输单元(MTU)都不尽相同,因为每个不同类型的数据链路的使用目的不同。使用目的不同,可承载的 MTU 也就不同。
- 任何一台主机都有必要对 IP 分片进行相应的处理。分片往往在网络上遇到比较大的报文无法一下子发送出去时才会进行处理。
- 经过分片之后的 IP 数据报在被重组的时候,只能由目标主机进行。路由器虽然做分片但不会进行重组。
3.1 路径 MTU 发现
- 分片机制也有它的不足。如路由器的处理负荷加重之类。因此,只要允许,是不希望由路由器进行 IP 数据包的分片处理的。
- 为了应对分片机制的不足,“路径 MTU 发现” 技术应运而生。路径 MTU 指的是,从发送端主机到接收端主机之间不需要分片是*** MTU 的大小。即路径中存在的所有数据链路中最小的 MTU 。
- 进行路径 MTU 发现,就可以避免在中途的路由器上进行分片处理,也可以在 TCP 中发送更大的包。
4. IPv6
- IPv6(IP version 6)是为了根本解决 IPv4 地址耗尽的问题而被标准化的网际协议。IPv4 的地址长度为 4 个 8 位字节,即 32 比特。而 IPv6 的地址长度则是原来的 4 倍,即 128 比特,一般写成 8 个 16 位字节。
4.1 IPv6 的特点
- IP 得知的扩大与路由控制表的聚合。
- 性能提升。包首部长度采用固定的值(40字节),不再采用首部检验码。简化首部结构,减轻路由器负担。路由器不再做分片处理。
- 支持即插即用功能。即使没有DHCP服务器也可以实现自动分配 IP 地址。
- 采用认证与加密功能。应对伪造 IP 地址的网络安全功能以及防止线路窃听的功能。
- 多播、Mobile IP 成为扩展功能。
4.2 IPv6 中 IP 地址的标记方法
- 一般人们将 128 比特 IP 地址以每 16 比特为一组,每组用冒号(“:”)隔开进行标记。
- 而且如果出现连续的 0 时还可以将这些 0 省略,并用两个冒号(“::”)隔开。但是,一个 IP 地址中只允许出现一次两个连续的冒号。
4.3 IPv6 地址的结构
- IPv6 类似 IPv4,也是通过 IP 地址的前几位标识 IP 地址的种类。
- 在互联网通信中,使用一种全局的单播地址。它是互联网中唯一的一个地址,不需要正式分配 IP 地址。
4.4 全局单播地址
- 全局单播地址是指世界上唯一的一个地址。它是互联网通信以及各个域内部通信中最为常用的一个 IPv6 地址。
- 格式如下图所示,现在 IPv6 的网络中所使用的格式为,n = 48,m = 16 以及 128 - n - m = 64。即前 64 比特为网络标识,后 64 比特为主机标识。
全局单播地址
4.5 链路本地单播地址
- 链路本地单播地址是指在同一个数据链路内唯一的地址。它用于不经过路由器,在同一个链路中的通信。通常接口 ID 保存 64 比特版的 MAC 地址。
链路本地单播地址
4.6 唯一本地地址
- 唯一本地地址是不进行互联网通信时所用的地址。
- 唯一本地地址虽然不会与互联网连接,但是也会尽可能地随机生成一个唯一的全局 ID。
- L 通常被置为 1
- 全局 ID 的值随机决定
- 子网 ID 是指该域子网地址
- 接口 ID 即为接口的 ID
唯一本地地址
4.7 IPv6 分段处理
- IPv6 的分片处理只在作为起点的发送端主机上进行,路由器不参与分片。
- IPv6 中最小 MTU 为 1280 字节,因此,在嵌入式系统中对于那些有一定系统资源限制的设备来说,不需要进行“路径 MTU 发现”,而是在发送 IP 包时直接以 1280 字节为单位分片送出。
二、IP 协议
**IP(Internet Protocol,因特网协议)**是网络层的核心协议,规定了网络层的封装规范,将上层(传输层)传递下来的数据段附加上 IP 首部封装成 IP 数据包,又称数据报文,IP 数据包同样由包首部和数据两部分组成,这是数据部分实际为传输层的数据段。
数据包首部长度为 20 字节,数据部分最大长度为 65515 字节,一个 IP 数据包总长达 65535 字节。而下层数据帧的数据部分最长为 1500 个字节,所以如果 IP 数据包的实际长度超过了 1500,就需要对 IP 数据包进行分片处理,由多个数据帧发送。
1、IP 数据包首部
- Version 版本号:标识 IP 地址的版本,目前有 IPv4 和 IPv6 两个版本
- Header Length 首部长度:标识 IP 首部长度
- Type of Service 服务类型:前 3 位标识优先级,4-7 位分别标识时延、吞吐量、可靠性、开销(一个数据包只能有一位生效)
- Total Length 总长度:标识 IP 数据包总长。 (指整个 I P数据报的长度,以字节为单位。利用首部长度字段和总长度字段,就可以知道 IP数据报中数据内容的起始位置和长度。 )
- Identification 标识字段 :IP 数据包的唯一标识,如果数据包被分片传输,接收方会根据分片中的该字段值来判断哪些分片属于同一个数据报,以此进行分片重组。通常的每发送一份数据包该值就会被加 1。
- IP Flags 标志位:标识 IP 数据包是否被分片。
- Fragment Offset 偏移量:在接收方进行分片重组时,标识分片的顺序,指明了分片起始点相对于数据包起始点的偏移量。因为 IP 协议是无连接的非可靠传输协议,所以需要该字段来应对分片传输错序的情况。
- TTL 生存时间:标识生存时长,指明了数据报可以经过的路由器数量,防止丢失数据包的无休止传播。初始值由源主机设置(通常为32或64),每经过一个路由器,该字段值就会减 1。如果数据包的 TTL 减至 0,那么路由器会丢弃该数据包并发送一个 ICMP 超时消息给数据包的源主机 IP 地址。
- Protocol 协议:标识数据包数据部分来自于哪个协议(ICMP/IGMP/TCP/UDP/GRE/ESP)。
- Header Checksum 首部校验和:根据 IP 数据包首部计算出来的校验和
- Option 选项:一个可变长的可选信息
- Source Addres 源地址:IP 数据包源主机的 IP 地址
- Destination Address 目标地址:IP 数据包的目标主机 IP 地址,是网关路由的关键依据。
2、IP 地址
因为 MAC 地址无法满足复杂的网络环境需求,所以 IP 协议制定了一套逻辑上的网络地址(IP/子网掩码)。IP 地址有 IPv4 和 IPv6 两个版本,通过子网掩码将 IP 地址分为网络号、主机号两个部分,前者标识了一个子网,后者标识了子网中的主机。如果两台主机处在同一子网,那么它们的网络好必须是相同的。IP 地址解决了 “如何轻易的进行子网划分” 的问题。
此外,还有7个特殊的I P地址,如下图所示。 在这个图中, 0表示所有的比特位全为 0; - 1表示所有的比特位全为 1;
netid、 subnetid和hostid分别表示不为全0或全1的对应字段。子网号栏为空表示该地址没有进行子网划分。
3、ARP协议
引入 IP 地址后,接入网络的主机至少拥有一个 IP 地址和 MAC 地址。需要强调的是,IP 地址本质上是一个逻辑地址,为了解决上层复杂的子网架构问题而生,并不能作为最终定位主机的依据,这仍要依靠 MAC 地址来完成。因此,网络层还需要找到一种方法来完成 IP to MAC 的映射,ARP 协议应运而生。
**ARP(Address Resolution Protocol,地址解析协议)**提供了根据 IP 地址来获取 MAC 地址的能力,地址解析之名由此而来。
地址解析原理:主机间的通信时,ARP 协议首先会发起一个请求 IP 数据包,IP 数据包的首部包含有目标主机 IP 地址,这个数据包经由数据链路层、物理介质层,最终广播到子网内所有主机,主机接受到该 IP 数据包后解析出其首部所包含的目标主机 IP 地址,再和自身 IP 地址进行比对。若相同,则根据数据包首部包含的源主机 IP 地址将 MAC 地址返回;若不相同,则丢弃该数据包。同时 ARP 协议还会缓存一份地址映射表在本地,表记录为返回的 MAC 地址和目标主机 IP 地址。下次再需要通信时,ARP 会直接查询地址映射表以提高效率。
4、路由协议
通过 ARP 协议的地址解析原理不难发现,ARP 协议同样具有子网局限性。为了突破这一限制实现跨子网通信,网络层实现了路由协议。
路由协议提供了异构网(子网间)互联的能力,可以将一个子网的 IP 数据包发送到另一个子网。所谓 “路由” 即指导数据包转发的路径信息。路由协议是运行在路由器上的协议,在错综复杂的网络世界中,路由器充当了交通枢纽的角色,它会视实际的网络环境来选择最佳路径进行数据包转发,路由器是网络层的代表设备。
IP 数据包路由原理:主机间的通信时,首先会通过 IP 协议来判断两台主机是否处在同一子网。若是,直接交给 ARP 协议和以太网协议来完成子网广播。若不是,以太网协议则会将 IP 数据包发送到子网网关(一般为路由器)进行路由决策,再经过若干次网关路由转发之后,IP 数据包就进入到目标主机 IP 地址所处的子网中,最后还是通过子网广播完成主机定位。
可见,路由器和路由协议解决了 “如何隔离子网之间的广播信号” 和 “子网之间的计算机依旧能够通信” 的问题。
5、域名
用于 TCP/IP 地址的名字被称为域名。www.baidu.com就是一个域名。
当你键入一个像https://www.baidu.com/这样的域名,域名会被一种 DNS 程序翻译为数字。
在全世界,数量庞大的 DNS 服务器被连入因特网。DNS 服务器负责将域名翻译为 TCP/IP 地址,同时负责使用新的域名信息更新彼此的系统。
当一个新的域名连同其 TCP/IP 地址一同注册后,全世界的 DNS 服务器都会对此信息进行更新。
三、传输层中的 TCP 和 UDP
TCP/IP 中有两个具有代表性的传输层协议,分别是 TCP 和 UDP。
- TCP 是面向连接的、可靠的流协议。流就是指不间断的数据结构,当应用程序采用 TCP 发送消息时,虽然可以保证发送的顺序,但还是犹如没有任何间隔的数据流发送给接收端。TCP 为提供可靠性传输,实行“顺序控制”或“重发控制”机制。此外还具备“流控制(流量控制)”、“拥塞控制”、提高网络利用率等众多功能。
- UDP 是不具有可靠性的数据报协议。细微的处理它会交给上层的应用去完成。在 UDP 的情况下,虽然可以确保发送消息的大小,却不能保证消息一定会到达。因此,应用有时会根据自己的需要进行重发处理。
- TCP 和 UDP 的优缺点无法简单地、绝对地去做比较:TCP 用于在传输层有必要实现可靠传输的情况;而在一方面,UDP 主要用于那些对高速传输和实时性有较高要求的通信或广播通信。TCP 和 UDP 应该根据应用的目的按需使用。
1. 端口号
数据链路和 IP 中的地址,分别指的是 MAC 地址和 IP 地址。前者用来识别同一链路中不同的计算机,后者用来识别 TCP/IP 网络中互连的主机和路由器。在传输层也有这种类似于地址的概念,那就是端口号。端口号用来识别同一台计算机中进行通信的不同应用程序。因此,它也被称为程序地址。
1.1 根据端口号识别应用
一台计算机上同时可以运行多个程序。传输层协议正是利用这些端口号识别本机中正在进行通信的应用程序,并准确地将数据传输。
通过端口号识别应用
1.2 通过 IP 地址、端口号、协议号进行通信识别
- 仅凭目标端口号识别某一个通信是远远不够的。
通过端口号、IP地址、协议号进行通信识别
① 和② 的通信是在两台计算机上进行的。它们的目标端口号相同,都是80。这里可以根据源端口号加以区分。
③ 和 ① 的目标端口号和源端口号完全相同,但它们各自的源 IP 地址不同。
此外,当 IP 地址和端口号全都一样时,我们还可以通过协议号来区分(TCP 和 UDP)。
1.3 端口号的确定
- 标准既定的端口号:这种方法也叫静态方法。它是指每个应用程序都有其指定的端口号。但并不是说可以随意使用任何一个端口号。例如 HTTP、FTP、TELNET 等广为使用的应用协议中所使用的端口号就是固定的。这些端口号被称为知名端口号,分布在 0~1023 之间;除知名端口号之外,还有一些端口号被正式注册,它们分布在 1024~49151 之间,不过这些端口号可用于任何通信用途。
- 时序分配法:服务器有必要确定监听端口号,但是接受服务的客户端没必要确定端口号。在这种方法下,客户端应用程序完全可以不用自己设置端口号,而全权交给操作系统进行分配。动态分配的端口号范围在 49152~65535 之间。
1.4 端口号与协议
- 端口号由其使用的传输层协议决定。因此,不同的传输层协议可以使用相同的端口号。
- 此外,那些知名端口号与传输层协议并无关系。只要端口一致都将分配同一种应用程序进行处理。
2. UDP
- UDP 不提供复杂的控制机制,利用 IP 提供面向无连接的通信服务。
- 并且它是将应用程序发来的数据在收到的那一刻,立即按照原样发送到网络上的一种机制。即使是出现网络拥堵的情况,UDP 也无法进行流量控制等避免网络拥塞行为。
- 此外,传输途中出现丢包,UDP 也不负责重发。
- 甚至当包的到达顺序出现乱序时也没有纠正的功能。
- 如果需要以上的细节控制,不得不交由采用 UDP 的应用程序去处理。
- UDP 常用于一下几个方面:1.包总量较少的通信(DNS、SNMP等);2.视频、音频等多媒体通信(即时通信);3.限定于 LAN 等特定网络中的应用通信;4.广播通信(广播、多播)。
3. TCP
- TCP 与 UDP 的区别相当大。它充分地实现了数据传输时各种控制功能,可以进行丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。而这些在 UDP 中都没有。
- 此外,TCP 作为一种面向有连接的协议,只有在确认通信对端存在时才会发送数据,从而可以控制通信流量的浪费。
- 根据 TCP 的这些机制,在 IP 这种无连接的网络上也能够实现高可靠性的通信( 主要通过检验和、序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现)。
3.1 三次握手(重点)
- TCP 提供面向有连接的通信传输。面向有连接是指在数据通信开始之前先做好两端之间的准备工作。
- 所谓三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发。
下面来看看三次握手的流程图:
三次握手
- ***次握手:客户端将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给服务器端,客户端进入SYN_SENT状态,等待服务器端确认。
- 第二次握手:服务器端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器端将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给客户端以确认连接请求,服务器端进入SYN_RCVD状态。
- 第三次握手:客户端收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给服务器端,服务器端检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。
3.2 四次挥手(重点)
- 四次挥手即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发。
- 由于TCP连接是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。
下面来看看四次挥手的流程图:
四次挥手
- 中断连接端可以是客户端,也可以是服务器端。
- ***次挥手:客户端发送一个FIN=M,用来关闭客户端到服务器端的数据传送,客户端进入FIN_WAIT_1状态。意思是说"我客户端没有数据要发给你了",但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。
- 第二次挥手:服务器端收到FIN后,先发送ack=M+1,告诉客户端,你的请求我收到了,但是我还没准备好,请继续你等我的消息。这个时候客户端就进入FIN_WAIT_2 状态,继续等待服务器端的FIN报文。
- 第三次挥手:当服务器端确定数据已发送完成,则向客户端发送FIN=N报文,告诉客户端,好了,我这边数据发完了,准备好关闭连接了。服务器端进入LAST_ACK状态。
- 第四次挥手:客户端收到FIN=N报文后,就知道可以关闭连接了,但是他还是不相信网络,怕服务器端不知道要关闭,所以发送ack=N+1后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。服务器端收到ACK后,就知道可以断开连接了。客户端等待了2MSL后依然没有收到回复,则证明服务器端已正常关闭,那好,我客户端也可以关闭连接了。最终完成了四次握手。
上面是一方主动关闭,另一方被动关闭的情况,实际中还会出现同时发起主动关闭的情况。
具体流程如下图:
同时挥手
3.3 通过序列号与确认应答提高可靠性
- 在 TCP 中,当发送端的数据到达接收主机时,接收端主机会返回一个已收到消息的通知。这个消息叫做确认应答(ACK)。当发送端将数据发出之后会等待对端的确认应答。如果有确认应答,说明数据已经成功到达对端。反之,则数据丢失的可能性很大。
- 在一定时间内没有等待到确认应答,发送端就可以认为数据已经丢失,并进行重发。由此,即使产生了丢包,仍然能够保证数据能够到达对端,实现可靠传输。
- 未收到确认应答并不意味着数据一定丢失。也有可能是数据对方已经收到,只是返回的确认应答在途中丢失。这种情况也会导致发送端误以为数据没有到达目的地而重发数据。
- 此外,也有可能因为一些其他原因导致确认应答延迟到达,在源主机重发数据以后才到达的情况也屡见不鲜。此时,源主机只要按照机制重发数据即可。
- 对于目标主机来说,反复收到相同的数据是不可取的。为了对上层应用提供可靠的传输,目标主机必须放弃重复的数据包。为此我们引入了序列号。
- 序列号是按照顺序给发送数据的每一个字节(8位字节)都标上号码的编号。接收端查询接收数据 TCP 首部中的序列号和数据的长度,将自己下一步应该接收的序列号作为确认应答返送回去。通过序列号和确认应答号,TCP 能够识别是否已经接收数据,又能够判断是否需要接收,从而实现可靠传输。
序列号和确认应答
3.4 重发超时的确定
- 重发超时是指在重发数据之前,等待确认应答到来的那个特定时间间隔。如果超过这个时间仍未收到确认应答,发送端将进行数据重发。最理想的是,找到一个最小时间,它能保证“确认应答一定能在这个时间内返回”。
- TCP 要求不论处在何种网络环境下都要提供高性能通信,并且无论网络拥堵情况发生何种变化,都必须保持这一特性。为此,它在每次发包时都会计算往返时间及其偏差。将这个往返时间和偏差时间相加,重发超时的时间就是比这个总和要稍大一点的值。
- 在 BSD 的 Unix 以及 Windows 系统中,超时都以0.5秒为单位进行控制,因此重发超时都是0.5秒的整数倍。不过,最初其重发超时的默认值一般设置为6秒左右。
- 数据被重发之后若还是收不到确认应答,则进行再次发送。此时,等待确认应答的时间将会以2倍、4倍的指数函数延长。
- 此外,数据也不会被***、反复地重发。达到一定重发次数之后,如果仍没有任何确认应答返回,就会判断为网络或对端主机发生了异常,强制关闭连接。并且通知应用通信异常强行终止。
3.5 以段为单位发送数据
- 在建立 TCP 连接的同时,也可以确定发送数据包的单位,我们也可以称其为“***消息长度”(MSS)。最理想的情况是,***消息长度正好是 IP 中不会被分片处理的***数据长度。
- TCP 在传送大量数据时,是以 MSS 的大小将数据进行分割发送。进行重发时也是以 MSS 为单位。
- MSS 在三次握手的时候,在两端主机之间被计算得出。两端的主机在发出建立连接的请求时,会在 TCP 首部中写入 MSS 选项,告诉对方自己的接口能够适应的 MSS 的大小。然后会在两者之间选择一个较小的值投入使用。
3.6 利用窗口控制提高速度
- TCP 以1个段为单位,每发送一个段进行一次确认应答的处理。这样的传输方式有一个缺点,就是包的往返时间越长通信性能就越低。
- 为解决这个问题,TCP 引入了窗口这个概念。确认应答不再是以每个分段,而是以更大的单位进行确认,转发时间将会被大幅地缩短。也就是说,发送端主机,在发送了一个段以后不必要一直等待确认应答,而是继续发送。如下图所示:
- 窗口控制
- 窗口大小就是指无需等待确认应答而可以继续发送数据的***值。上图中窗口大小为4个段。这个机制实现了使用大量的缓冲区,通过对多个段同时进行确认应答的功能。
3.7 滑动窗口控制
滑动窗口
- 上图中的窗口内的数据即便没有收到确认应答也可以被发送出去。不过,在整个窗口的确认应答没有到达之前,如果其中部分数据出现丢包,那么发送端仍然要负责重传。为此,发送端主机需要设置缓存保留这些待被重传的数据,直到收到他们的确认应答。
- 在滑动窗口以外的部分包括未发送的数据以及已经确认对端已收到的数据。当数据发出后若如期收到确认应答就可以不用再进行重发,此时数据就可以从缓存区清除。
- 收到确认应答的情况下,将窗口滑动到确认应答中的序列号的位置。这样可以顺序地将多个段同时发送提高通信性能。这种机制也别称为滑动窗口控制。
3.8 窗口控制中的重发控制
在使用窗口控制中, 出现丢包一般分为两种情况:
① 确认应答未能返回的情况。在这种情况下,数据已经到达对端,是不需要再进行重发的,如下图:
部分确认应答丢失
② 某个报文段丢失的情况。接收主机如果收到一个自己应该接收的序列号以外的数据时,会针对当前为止收到数据返回确认应答。如下图所示,当某一报文段丢失后,发送端会一直收到序号为1001的确认应答,因此,在窗口比较大,又出现报文段丢失的情况下,同一个序列号的确认应答将会被重复不断地返回。而发送端主机如果连续3次收到同一个确认应答,就会将其对应的数据进行重发。这种机制比之前提到的超时管理更加高效,因此也被称为高速重发控制。
高速重发控制
4、TCP与UDP
面向报文
面向报文的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。因此,应用程序必须选择合适大小的报文。若报文太长,则IP层需要分片,降低效率。若太短,会是IP太小。
面向字节流
面向字节流的话,虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序看成是一连串的无结构的字节流。TCP有一个缓冲,当应用程序传送的数据块太长,TCP就可以把它划分短一些再传送。
关于拥塞控制,流量控制,是TCP的重点,后面讲解。
TCP和UDP协议的一些应用
什么时候应该使用TCP?
当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。
什么时候应该使用UDP?
当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。
四、TCP 协议
TCP(Transmission Control Protocol,传输控制协议),是一种面向连接的可靠传输协议,提供可靠(无差错、不丢失、不重复、按顺序)的字节流数据传输服务。在传输效率和可靠性之间选择了后者,所以有开销大、传输速度慢的缺点。
- 面向连接:在使用 TCP 通信之前,需要进行 “三次握手” 建立发收双方连接,通信结束后还要进行 “四次挥手” 确保断开连接。
- 点对点:一条 TCP 连接只能连接两个端点。
- 全双工通信:允许通信双方任意时刻双向发送数据,发送方设有发送缓存,接收方设有接收缓存。
- 字节流传输:TCP 会将数据当作字节流进行处理,不尝试理解所传输的数据含义,仅把数据看作一连串的字节序列。
TCP 的可靠性传输具有非常复杂的实现细节,包括但不限于:
- ACK 确定机制:当接收方接收到数据段后,会返回 ACK 确认
- 定时重发:方发送方发送数据段后,会启动定时器,超时未接收到 ACK 确认,会重发该数据段
- 数据校验:接收方会对数据段进行数据校验,如果发现数据段有差错,会将该数据段丢弃,等待超时重传
- 顺序传输:TCP 字节流会为每个字节排序,确保数据传输顺序的正确性
- 滑动窗口:TCP 数据段长度可根据收发双方的缓存、网络等状态而调整。接收方只允许发送方发送接收缓冲区所能接纳的数据,防止缓冲区溢出
1、数据段首部:
- Sequence Number 序列号:字节流中的每个字节都要按序编号,该字段值为本数据段数据部分的第一个字节的序号
- Acknowledgment Number 确认号:确认序列号
- Offset 偏移量:数据段首部的长度,字段值为首部长度除以 4
- Reserved 预留:保留位,供今后使用
- TCP flags 标签:标识数据段性质。
- Window 窗口:标识发送者接收窗口的大小
- CheckSum 校验值:用于检查数据段在传输过程中是否出现差错
- Urgent Pointer 紧急指针:当字段值为 1 时生效,标识本数据段具有紧急数据
其中的 TCP Flags 字段,是非常重要的功能标识,占 8 位,分别为:
- C(CWR)、E(ECE):用于支持 ECN(显示阻塞通告)
- U(URGENT):当值为 1 时,标识此数据段有紧急数据(比如紧急关闭),应优先传送,要与紧急指针字段配合使用
- A(ACK):仅当字段值为 1 时才有效,建立 TCP 连接后,所有数据段都必须把 ACK 字段值置为 1
- P(PUSH):若 TCP 连接的一端希望另一端立即响应,PSH 字段便可以 “催促” 对方,不再等到缓存区填满才发送返回
- R(RESET):若 TCP 连接出现严重差错,该字段的值置为 1,表示先断开 TCP 连接,再重连
- S(SYN,Synchronize Sequence Numbers):用于建立和释放连接,当字段值为 1 时,表示建立连接。
- F(FIN):用于释放连接,当字段值为 1 时,表明发送方已经发送完毕,要求释放 TCP 连接
2、TCP连接的建立与终止
下面以三次握手和四次挥手为例,看看数据段首部是怎么进行传输标识并以此来保存可靠性的。
三次握手:
- 建立连接时,客户端发送 (SYN=1,seq=x) 数据段到服务器,然后进入 SYN_SENT 状态并等待服务器确认;
- 服务器收到 (SYN=1,seq=x) 数据段后,返回 (ACK=1, ack=x+1, SYN=1, seq=y) 数据段,服务器进入 SYN_RECV 状态;
- 客户端收到服务器的 ACK 确认后,也会向服务器发送 ACK 确认 (ACK=1, ack=y+1),至此客户端和服务器都进入了 ESTABLISHED 状态。三次握手完成,TCP 连接建立成功。
为什么要使用三次握手来保证数据传输的可靠性?
“握手” 的行为实际上为了告知收发双方自己的 ISN(Initial Sequence Number,初始化序号),如上图的 seq=x 或 seq=y,这个数值会被作为建立连接之后进行顺序数据传输的依据。从数据段首部的 Sequence Number 和 Acknowledgment Number 都占 32 位可知,seq 和 ack 的取值范围均为 [0, 2^32-1],顺序循环使用。需要注意的是,seq 并未每次都是从 0 开始的,TCP 协议会以 4μs 一次的频率进行 ISN+=1 操作,以此来避免 TCP 重连时会出现同一条连接中存在两个及以上 seq 相同的包,最终导致顺序错乱。
四次挥手:
- 断开连接时,客户端发送 (FIN=1, seq=m) 数据段,其中 m 的数值为客户端最后一次向服务器发送的数据段的最后一个字节的序号再加上 1,客户端进入 FIN-WAIT-1 状态。
- 服务器接收到 (FIN=1, seq=m) 之后,返回 (ACK=1, ack=m+1, seq=n) 确认数据段,服务器进入 CLOSE-WAIT 状态。
- 服务器再向客户端发送 (FIN=1, seq=u, ACK=1, ack=m+1) 数据段,服务器进入 LAST-ACK 状态。
- 客户端接收到 (FIN=1, seq=u, ACK=1, ack=m+1) 数据段后,返回 (ACK=1, ack=u+1, seq=m+1) 确认数据段,服务器和客户端都进入 CLOSED 状态。四次挥手完成,TCP 连接就此断开。
为什么建立连接是三次握手,而关闭连接却是四次挥手呢?
这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。
为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
原因有二:
一、保证TCP协议的全双工连接能够可靠关闭
二、保证这次连接的重复数据段从网络中消失
先说第一点,如果Client直接CLOSED了,那么由于IP协议的不可靠性或者是其它网络原因,导致Server没有收到Client最后回复的ACK。那么Server就会在超时之后继续发送FIN,此时由于Client已经CLOSED了,就找不到与重发的FIN对应的连接,最后Server就会收到RST而不是ACK,Server就会以为是连接错误把问题报告给高层。这样的情况虽然不会造成数据丢失,但是却导致TCP协议不符合可靠连接的要求。所以,Client不是直接进入CLOSED,而是要保持TIME_WAIT,当再次收到FIN的时候,能够保证对方收到ACK,最后正确的关闭连接。
再说第二点,如果Client直接CLOSED,然后又再向Server发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题,但是还是有特殊情况出现:假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达Server,由于新连接和老连接的端口号是一样的,又因为TCP协议判断不同连接的依据是socket pair,于是,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2倍MSL,这样可以保证本次连接的所有数据都从网络中消失。
3、TCP的状态变迁图
五、UDP协议
UDP(User Datagram Protocol,用户数据报协议),是一种无连接的非可靠传输层协议。UDP 不提供数据包分组、组装,不能对数据段进行排序,所以 UDP 数据段的首部非常简单。换句话说,当数据段发送出去之后,发送方是无法得知其是否完整且安全的到达了接收方的。这样的传输机制决定了它的最大优点就是快,同时也决定了它最大的缺点不可靠、不稳定。
1、UDP 数据段首部:
- Source Port 源应用程序端口:发送方的应用程序端口
- Destination Port 目的应用程序端口:接收方的应用程序端口
- Length 长度:数据段的长度
- Checksum 校验值:用于检查数据段在传输过程中是否出现差错
2、IP分片
3、ICMP不可达差错(需要分片)
六、参考资料
1、https://www.cnblogs.com/jmilkfan-fanguiju/p/10589771.html
2、https://developer.51cto.com/art/201906/597961.htm
3、https://blog.csdn.net/mumubumaopao/article/details/107860818
4、https://blog.csdn.net/bjweimengshu/article/details/79214572
5、https://blog.csdn.net/bjweimengshu/article/details/79214572
6、https://blog.csdn.net/weixin_44198965/article/details/90083126