飞道的博客

网络常识(上)

317人阅读  评论(0)

文章目录

一、HTTP基础

  常见的HTTP相关问题可以分为5大类:

  1. HTTP 基本概念
  2. Get 与 Post
  3. HTTP 特性
  4. HTTPS 与 HTTP
  5. HTTP/1.1、HTTP/2、HTTP/3 演变

  用图表示的话:

1.1 HTTP 基本概念

  HTTP 是超文本传输协议,也就是HyperText Transfer Protocol。它可以拆成三个部分:超文本、传输、协议。

  HTTP协议是一个用在计算机世界里的协议。它使用计算机能够理解的语言确立了一种计算机之间交流通信的规范(两个以上的参与者),以及相关的各种控制和错误处理方式(行为约定和规范)。
  HTTP 协议是一个双向协议。
  HTTP 是一个在计算机世界里专门用来在两点之间传输数据的约定和规范。
  HTTP 传输的内容是「超文本」。超文本是文字、图片、视频等的混合体,最关键有超链接,能从一个超文本跳转到另外一个超文本。HTML 就是最常见的超文本了,它本身只是纯文字文件,但内部用很多标签定义了图片、视频等的链接,再经过浏览器的解释,呈现给我们的就是一个文字、有画面的网页了。
  综合以上特点:HTTP 是一个在计算机世界里专门在「两点」之间「传输」文字、图片、音频、视频等「超文本」数据的「约定和规范」

1.1.1 状态码

  • 1xx
     1xx 类状态码属于提示信息,是协议处理中的一种中间状态,实际用到的比较少。
  • 2xx
     2xx 类状态码表示服务器成功处理了客户端的请求,也是我们最愿意看到的状态。
     「200 OK」是最常见的成功状态码,表示一切正常。如果是非 HEAD 请求,服务器返回的响应头都会有 body 数据。
     「204 No Content」也是常见的成功状态码,与 200 OK 基本相同,但响应头没有 body 数据。
     「206 Partial Content」是应用于 HTTP 分块下载或断点续传,表示响应返回的 body 数据并不是资源的全部,而是其中的一部分,也是服务器处理成功的状态。
  • 3xx
     3xx 类状态码表示客户端请求的资源发送了变动,需要客户端用新的 URL 重新发送请求获取资源,也就是重定向。
     「301 Moved Permanently」表示永久重定向,说明请求的资源已经不存在了,需改用新的 URL 再次访问。
     「302 Found」表示临时重定向,说明请求的资源还在,但暂时需要用另一个 URL 来访问。
     301 和 302 都会在响应头里使用字段 Location ,指明后续要跳转的 URL,浏览器会自动重定向新的URL。
     「304 Not Modified」不具有跳转的含义,表示资源未修改,重定向已存在的缓冲文件,也称缓存重定向,用于缓存控制。
  • 4xx
     4xx 类状态码表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义。
     「400 Bad Request」表示客户端请求的报文有错误,但只是个笼统的错误。
     「403 Forbidden」表示服务器禁止访问资源,并不是客户端的请求出错。
     「404 Not Found」表示请求的资源在服务器上不存在或未找到,所以无法提供给客户端。
  • 5xx
     5xx 类状态码表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码。
     「500 Internal Server Error」与 400 类型,是个笼统通用的错误码,服务器发生了什么错误,我们并不知道。
     「501 Not Implemented」表示客户端请求的功能还不支持,类似“即将开业,敬请期待”的意思。
     「502 Bad Gateway」通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务器发生了错误。
     「503 Service Unavailable」表示服务器当前很忙,暂时无法响应服务器,类似“网络服务正忙,请稍后重试”的意思。

1.1.2 http 常见字段

  • 1、Host 字段
     客户端发送请求时,用来指定服务器的域名。

     有了 Host 字段,就可以将请求发往「同一台」服务器上的不同网站。
  • 2、Content-Length 字段
     服务器在返回数据时,会有 Content-Length 字段,表明本次回应的数据长度。

     该字段的意思:本次服务器回应的数据长度是 1000 个字节,后面的字节就属于下一个回应。
  • 3、Connection 字段
     Connection 字段最常用于客户端要求服务器使用 TCP 持久连接,以便其他请求复用。

     HTTP/1.1 版本的默认连接都是持久连接,但为了兼容老版本的 HTTP,需要指定 Connection 首部字段的值为 Keep-Alive 。
  • 4、Content-Type 字段
     Content-Type 字段用于服务器回应时,告诉客户端,本次数据是什么格式。

     上面的类型表明,发送的是网页,而且编码是UTF-8。客户端请求的时候,可以使用 Accept 字段声明自己可以接受哪些数据格式。
  • 5、Content-Encoding 字段
     Content-Encoding 字段说明数据的压缩方法。表示服务器返回的数据使用了什么压缩格式。

     上面表示服务器返回的数据采用了 gzip 方式压缩,告知客户端需要用此方式解压。客户端在请求时,用 Accept-Encoding 字段说明自己可以接受哪些压缩方法。

1.2 GET 与 POST

  Get 方法的含义是请求从服务器获取资源,这个资源可以是静态的文本、页面、图片视频等。
  POST 方法则是相反操作,它向 URI 指定的资源提交数据,数据就放在报文的 body 里。
  GET 和 POST 方法都是安全和幂等的吗?

在 HTTP 协议里,所谓的「安全」是指请求方法不会「破坏」服务器上的资源。
所谓的「幂等」,意思是多次执行相同的操作,结果都是「相同」的。

  GET 方法就是安全且幂等的,因为它是「只读」操作,无论操作多少次,服务器上的数据都是安全的,且每次的结果都是相同的。
  POST 因为是「新增或提交数据」的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以不是幂等的。

1.3 HTTP 特性

1.3.1 优点

  HTTP 最凸出的优点是「简单、灵活和易于扩展、应用广泛和跨平台」。

  • 1、简单
     HTTP 基本的报文格式就是 header + body ,头部信息也是 key-value 简单文本的形式,易于理解,降低了学习和使用的门槛。
  • 2、灵活和易于扩展
     HTTP协议里的各类请求方法、URI/URL、状态码、头字段等每个组成要求都没有被固定死,都允许开发人员自定义和扩充。
     同时 HTTP 由于是工作在应用层( OSI 第七层),则它下层可以随意变化。
     HTTPS 也就是在 HTTP 与 TCP 层之间增加了 SSL/TLS 安全传输层,HTTP/3 甚至把 TCP 层换成了基于 UDP 的 QUIC。
  • 3、应用广泛和跨平台

1.3.2 缺点

  HTTP 协议里有优缺点一体的双刃剑,分别是「无状态、明文传输」,同时还有一大缺点「不安全」。
  无状态的好处,因为服务器不会去记忆 HTTP 的状态,所以不需要额外的资源来记录状态信息,这能减轻服务器的负担,能够把更多的 CPU 和内存用来对外提供服务。
  无状态的坏处,既然服务器没有记忆能力,它在完成有关联性的操作时会非常麻烦。例如登录->添加购物车->下单->结算->支付,这系列操作都要知道用户的身份才行。但服务器不知道这些请求是有关联的,每次都要问一遍身份信息。
  对于无状态的问题,解法方案有很多种,其中比较简单的方式用 Cookie 技术。

Cookie 通过在请求和响应报文中写入 Cookie 信息来控制客户端的状态。


  HTTP 比较严重的缺点就是不安全:

1、通信使用明文(不加密),内容可能会被窃听。比如,账号信息容易泄漏。
2、不验证通信方的身份,因此有可能遭遇伪装。比如,访问假的淘宝、拼多多。
3、无法证明报文的完整性,所以有可能已遭篡改。比如,网页上植入垃圾广告,视觉污染。

  HTTP 的安全问题,可以用 HTTPS 的方式解决,也就是通过引入 SSL/TLS 层,使得在安全上达到了极致。

1.3.3 HTTP/1.1 的性能

  HTTP 协议是基于 TCP/IP,并且使用了「请求 - 应答」的通信模式。
  HTTP/1.1 提出了长连接的通信方式,也叫持久连接。这种方式的好处在于减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载。

持久连接的特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。


  HTTP/1.1 采用了长连接的方式,这使得管道(pipeline)网络传输成为了可能。即可在同一个 TCP 连接里面,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。
  举例来说,客户端需要请求两个资源。以前的做法是,在同一个TCP连接里面,先发送 A 请求,然后等待服务器做出回应,收到后再发出 B 请求。管道机制则是允许浏览器同时发出 A 请求和 B 请求。
  但是服务器还是按照顺序,先回应 A 请求,完成后再回应 B 请求。要是前面的回应特别慢,后面就会有许多请求排队等着。这称为「队头堵塞」。
  「请求 - 应答」的模式加剧了 HTTP 的性能问题。因为当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一同被阻塞了,会招致客户端一直请求不到数据,这也就是「队头阻塞」。
  因此,HTTP/1.1 的性能一般般,后续的 HTTP/2 和 HTTP/3 就是在优化 HTTP 的性能。

1.4 HTTP 与 HTTPS

  HTTP 与 HTTPS 有哪些区别?

  1. HTTP 是超文本传输协议,信息是明文传输,存在安全风险的问题。HTTPS 则解决 HTTP 不安全的缺陷,在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。
  2. HTTP 连接建立相对简单, TCP 三次握手之后便可进行 HTTP 的报文传输。而 HTTPS 在 TCP三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。
  3. HTTP 的端口号是 80,HTTPS 的端口号是 443。
  4. HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的。

  HTTP 由于是明文传输,所以安全上存在以下三个风险:

  1. 窃听风险,比如通信链路上可以获取通信内容。
  2. 篡改风险,比如强制植入垃圾广告,视觉污染。
  3. 冒充风险,比如冒充淘宝网站,用户钱容易没。

  HTTP 与 HTTPS两者分层区别:

  HTTPS 在 HTTP 与 TCP 层之间加入了 SSL/TLS 协议,可以很好的解决了上述的风险:

  1. 信息加密:交互信息无法被窃取。
  2. 校验机制:无法篡改通信内容,篡改了就不能正常显示。
  3. 身份证书:证明淘宝是真的淘宝网。

  说的专业些:

  1. 混合加密的方式实现信息的机密性,解决了窃听的风险。
  2. 摘要算法的方式来实现完整性,它能够为数据生成独一无二的「指纹」,指纹用于校验数据的完整性,解决了篡改的风险。
  3. 将服务器公钥放入到数字证书中,解决了冒充的风险。

1.4.1 混合加密

  通过混合加密的方式可以保证信息的机密性,解决了窃听的风险。

  HTTPS 采用的是对称加密和非对称加密结合的「混合加密」方式:

1、在通信建立前采用非对称加密的方式交换「会话秘钥」,后续就不再使用非对称加密。
2、在通信过程中全部使用对称加密的「会话秘钥」的方式加密明文数据。

  采用「混合加密」的方式的原因:

1、对称加密只使用一个密钥,运算速度快,密钥必须保密,无法做到安全的密钥交换。
2、非对称加密使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但速度慢。

1.4.2 摘要算法

  摘要算法用来实现完整性,能够为数据生成独一无二的「指纹」,用于校验数据的完整性,解决了篡改的风险。

  客户端在发送明文之前会通过摘要算法算出明文的「指纹」,发送的时候把「指纹 + 明文」一同加密成密文后,发送给服务器,服务器解密后,用相同的摘要算法算出发送过来的明文,通过比较客户端携带的「指纹」和当前算出的「指纹」做比较,若「指纹」相同,说明数据是完整的。

1.4.3 数字证书

  客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密。
  这就存在些问题,如何保证公钥不被篡改和信任度?所以这里就需要借助第三方权威机构 CA (数字证书认证机构),将服务器公钥放在数字证书(由数字证书认证机构颁发)中,只要证书是可信的,公钥就是可信的。

1.4.4 HTTPS 是如何建立连接的?其间交互了什么?

  SSL/TLS 协议基本流程:

客户端向服务器索要并验证服务器的公钥。
双方协商生产「会话秘钥」。
双方采用「会话秘钥」进行加密通信。

  前两步也就是 SSL/TLS 的建立过程,也就是握手阶段。SSL/TLS 的「握手阶段」涉及四次通信:

  SSL/TLS 协议建立的详细流程:

  • 1、ClientHello
     首先,由客户端向服务器发起加密通信请求,也就是 ClientHello 请求。在这一步,客户端主要向服务器发送以下信息:

(1)客户端支持的 SSL/TLS 协议版本,如 TLS 1.2 版本。
(2)客户端生产的随机数( Client Random ),后面用于生产「会话秘钥」。
(3)客户端支持的密码套件列表,如 RSA 加密算法。

  • 2、SeverHello
     服务器收到客户端请求后,向客户端发出响应,也就是 SeverHello 。服务器回应的内容有如下内容:

(1)确认 SSL/ TLS 协议版本,如果浏览器不支持,则关闭加密通信。
(2)服务器生产的随机数( Server Random ),后面用于生产「会话秘钥」。
(3)确认的密码套件列表,如 RSA 加密算法。
(4)服务器的数字证书。

  • 3、客户端回应
     客户端收到服务器的回应之后,首先通过浏览器或者操作系统中的 CA 公钥,确认服务器的数字证书的真实性。如果证书没有问题,客户端会从数字证书中取出服务器的公钥,然后使用它加密报文,向服务器发送如下信息:

(1)一个随机数( pre-master key )。该随机数会被服务器公钥加密。
(2)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
(3)客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供服务端校验。

  上面第一项的随机数是整个握手阶段的第三个随机数,这样服务器和客户端就同时有三个随机数,接着就用双方协商的加密算法,各自生成本次通信的「会话秘钥」。

  • 4、服务器的最后回应
     服务器收到客户端的第三个随机数( pre-master key )之后,通过协商的加密算法,计算出本次通信的「会话秘钥」。然后,向客户端发生最后的信息:

(1)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供客户端校验。

 至此,整个 SSL/TLS 的握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的 HTTP 协议,只不过用「会话秘钥」加密内容。

1.5 HTTP/1.1、HTTP/2、HTTP/3

1.5.1 HTTP/1.1

  HTTP/1.1 相比 HTTP/1.0 性能上的改进:

1、使用 TCP 长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。
2、支持管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。

  HTTP/1.1 还是有性能瓶颈:

1、请求 / 响应头部(Header)未经压缩就发送,首部信息越多延迟越大。只能压缩 Body 的部分;
2、发送冗长的首部。每次互相发送相同的首部造成的浪费较多;
3、服务器是按请求的顺序响应的,如果服务器响应慢,会招致客户端一直请求不到数据,也就是队头阻塞;
4、没有请求优先级控制;
5、请求只能从客户端开始,服务器只能被动响应。

1.5.2 HTTP/2

  HTTP/2 协议是基于 HTTPS 的,HTTP/2 相比 HTTP/1.1 性能上的改进:

  • 1、头部压缩
     HTTP/2 会压缩头(Header)如果你同时发出多个请求,他们的头是一样的或是相似的,那么,协议会帮你消除重复的部分。这就是所谓的 HPACK 算法:在客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。
  • 2、二进制格式
     HTTP/2 不再像 HTTP/1.1 里的纯文本形式的报文,而是全面采用了二进制格式,头信息和数据体都是二进制,并且统称为帧(frame):头信息帧和数据帧。

     这样虽然对人不友好,但是对计算机非常友好,因为计算机只懂二进制,那么收到报文后,无需再将明文的报文转成二进制,而是直接解析二进制报文,这增加了数据传输的效率。
  • 3、数据流
     HTTP/2 的数据包不是按顺序发送的,同一个连接里面连续的数据包,可能属于不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。每个请求或回应的所有数据包,称为一个数据流( Stream )。每个数据流都标记着一个独一无二的编号,其中规定客户端发出的数据流编号为奇数, 服务器发出的数据流编号为偶数。
     客户端还可以指定数据流的优先级。优先级高的请求,服务器就先响应该请求。
  • 4、多路复用
     HTTP/2 是可以在一个连接中并发多个请求或回应,而不用按照顺序一一对应。移除了 HTTP/1.1 中的串行请求,不需要排队等待,也就不会再出现「队头阻塞」问题,降低了延迟,大幅度提高了连接的利用率。
     举例来说,在一个 TCP 连接里,服务器收到了客户端 A 和 B 的两个请求,如果发现 A 处理过程非常耗时,于是就回应 A 请求已经处理好的部分,接着回应 B 请求,完成后,再回应 A 请求剩下的部分。
  • 5、 服务器推送
     HTTP/2 还在一定程度上改善了传统的「请求 - 应答」工作模式,服务不再是被动地响应,也可以主动向客户端发送消息。
     举例来说,在浏览器刚请求 HTML 的时候,就提前把可能会用到的 JS、CSS 文件等静态资源主动发给客户端,减少延时的等待,也就是服务器推送(Server Push,也叫 Cache Push)。

1.5.3 HTTP/3

  HTTP/2 主要的问题在于,多个 HTTP 请求在复用一个 TCP 连接,下层的 TCP 协议是不知道有多少个HTTP 请求的。所以一旦发生了丢包现象,就会触发 TCP 的重传机制,这样在一个 TCP 连接中的所有的 HTTP 请求都必须等待这个丢了的包被重传回来。

1、HTTP/1.1 中的管道( pipeline)传输中如果有一个请求阻塞了,那么队列后请求也统统被阻塞住了。
2、HTTP/2 多个请求复用一个TCP连接,一旦发生丢包,就会阻塞住所有的 HTTP 请求。

  这都是基于 TCP 传输层的问题,所以 HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP。

  UDP 发生是不管顺序,也不管丢包的,所以不会出现 HTTP/1.1 的队头阻塞 和 HTTP/2 的一个丢包全部重传问题。
  大家都知道 UDP 是不可靠传输的,但基于 UDP 的 QUIC 协议 可以实现类似 TCP 的可靠性传输。

1、QUIC 有自己的一套机制可以保证传输的可靠性的。当某个流发生丢包时,只会阻塞这个流,其他流不会受到影响。
2、TLS3 升级成了最新的 1.3 版本,头部压缩算法也升级成了 QPack 。
3、HTTPS 要建立一个连接,要花费 6 次交互,先是建立三次握手,然后是 TLS/1.3 的三次握手。QUIC 直接把以往的 TCP 和 TLS/1.3 的 6 次交互合并成了 3 次,减少了交互次数。


  QUIC 是一个在 UDP 之上的伪 TCP + TLS + HTTP/2 的多路复用的协议。
  QUIC 是新协议,对于很多网络设备,根本不知道什么是 QUIC,只会当做 UDP,这样会出现新的问题,所以 HTTP/3 现在普及的进度非常的缓慢。

二、IP

2.1 IP初识

  IP 在 TCP/IP 参考模型中处于第三层,也就是网络层。网络层的主要作用是:实现主机与主机之间的通信,也叫点对点(end to end)通信。

  IP 的作用是主机之间通信用的,而 MAC 的作用则是实现「直连」
的两个设备之间通信,而 IP 则负责在「没有直连」的两个网络之间进行通信传输。

2.2 IP基础

  在 TCP/IP 网络通信时,为了保证能正常通信,每个设备都需要配置正确的 IP 地址,否则无法实现正常的通信。
  IP 地址(IPv4 地址)由 32 位正整数来表示,IP 地址在计算机是以二进制的方式处理的。
  为了方便记忆采用了点分十进制的标记方式,也就是将 32 位 IP 地址以每 8 位为组,共分为 4组,每组以" . "隔开,再将每组转换成十进制。

  IP 地址最大值也就是232,也就说,最大允许 43 亿左右台计算机连接到网络。
  实际上,IP 地址并不是根据主机台数来配置的,而是以网卡。像服务器、路由器等设备都是有 2 个以上的网卡,也就是它们会有 2 个以上的 IP 地址。

  因此,让 43 亿台计算机全部连网其实是不可能的,更何况 IP 地址是由「网络标识」和「主机标识」这两个部分组成的,所以实际能够连接到网络的计算机个数更是少了很多。
  目前的手机、IPad 等电子设备之和肯定超过了43亿,是怎么能够支持这么多 IP 的呢?答案是根据一种可以更换 IP 地址的技术 NAT ,使得可连接计算机数超过 43 亿台。

2.2.1 IP 地址的分类

  IP 地址分类成了 5 种类型,分别是 A 类、B 类、C 类、D 类、E 类。

  上图中黄色部分为分类号,用以区分 IP 地址类别。
  对于 A、B、C 类主要分为两个部分,分别是网络号和主机号。这很好理解,好比我是 A 小区 1栋 101 号,你是 B 小区 1 栋 101 号。
  A、B、C 分类对应的地址范围、最大主机个数:

  • 1、A、B、C 分类地址最大主机个数是如何计算的呢?
      最大主机个数,就是要看主机号的位数,如 C 类地址的主机号占 8 位,那么 C 类地址的最大主机个数:28-2=254。
      为什么要减 2 呢?因为在 IP 地址中,有两个 IP 是特殊的,分别是主机号全为 1 和 全为 0 地址。

主机号全为 1 指定某个网络下的所有主机,用于广播。
主机号全为 0 指定某个网络。

  因此,在分配过程中,应该去掉这两种情况。

  • 2、广播地址用于什么?
      广播地址用于在同一个链路中相互连接的主机之间发送数据包。
      在准备上课的时候,通常班长会喊:“上课, 全体起立!”,班里的同学听到这句话全部都站起来,这个句话就有广播的含义。
      当主机号全为 1 时,就表示该网络的广播地址。例如把 172.20.0.0/16 用二进制表示如下:

10101100.00010100.00000000.00000000

  将这个地址的主机部分全部改为 1,则形成广播地址:

10101100.00010100. 11111111.11111111

  再将这个地址用十进制表示,则为 172.20.255.255 。
  广播地址可以分为本地广播和直接广播两种:

  1. 在本网络内广播的叫做本地广播。例如网络地址为 192.168.0.0/24 的情况下,广播地址是192.168.0.255 。因为这个广播地址的 IP 包会被路由器屏蔽,所以不会到达 192.168.0.0/24 以外的其他链路上。
  2. 在不同网络之间的广播叫做直接广播。例如网络地址为 192.168.0.0/24 的主机向192.168.1.255/24 的目标地址发送 IP 包。收到这个包的路由器,将数据转发给 192.168.1.0/24,从而使得所有 192.168.1.1~192.168.1.254 的主机都能收到这个包(由于直接广播有一定的安全问题,多数情况下会在路由器上设置为不转发。) 。

  • 3、什么是 D、E 类地址?
      而 D 类和 E 类地址是没有主机号的,所以不可用于主机 IP,D 类常被用于多播,E 类是预留的分类,暂时未使用。
  • 4、多播地址用于什么?
      多播用于将包发送给特定组内的所有主机。

      多播使用的 D 类地址,其前四位是 1110 就表示是多播地址,而剩下的 28 位是多播的组编号。从 224.0.0.0 ~ 239.255.255.255 都是多播的可用范围,其划分为以下三类:

1、224.0.0.0 ~ 224.0.0.255 为预留的组播地址,只能在局域网中,路由器是不会进行转发的。
2、224.0.1.0 ~ 238.255.255.255 为用户可用的组播地址,可以用于 Internet 上。
3、239.0.0.0 ~ 239.255.255.255 为本地管理组播地址,可供内部网在内部使用,仅在特定的本地范围内有效。

  • 5、IP 分类的优点
      不管是路由器还是主机解析到一个 IP 地址时候,我们判断其 IP 地址的首位是否为 0,为 0 则为 A 类地址,那么就能很快的找出网络地址和主机地址。

第一位是0则为A类地址,否则继续判断;
第二位是0则为B类地址,否则继续判断;
第三位是0则为C类地址,否则继续判断;
第四位是0则为D类地址,否则为E类地址。

  所以,这种分类地址的优点就是简单明了、选路(基于网络地址)简单。

  • 6、IP 分类的缺点
      缺点一:同一网络下没有地址层次,比如一个公司里用了 B 类地址,但是可能需要根据生产环境、测试环境、开发环境来划分地址层次,而这种 IP 分类是没有地址层次划分的功能,所以这就缺少地址的灵活性。
      缺点二:A、B、C类有个尴尬处境,就是不能很好的与现实网络匹配。

1)C 类地址能包含的最大主机数量实在太少了,只有 254 个,估计一个网吧都不够用。
2)而 B 类地址能包含的最大主机数量又太多了,6 万多台机器放在一个网络下面,一般的企业基本达不到这个规模,闲着的地址就是浪费。

  这两个缺点,都可以在 CIDR 无分类地址解决。

2.2.2 无分类地址 CIDR

  无分类地址的方案,即 CIDR。这种方式不再有分类地址的概念,32 比特的 IP 地址被划分为两部分,前面是网络号,后面是主机号。

  • 1、怎么划分网络号和主机号的呢?
     形式 a.b.c.d/x ,其中 /x 表示前 x 位属于网络号, x 的范围是 0 ~ 32 ,这就使得 IP 地址更加具有灵活性。
     比如 10.100.122.2/24,这种地址表示形式就是 CIDR,/24 表示前 24 位是网络号,剩余的 8 位是主机号。

     还有另一种划分网络号与主机号形式,那就是子网掩码,掩码的意思就是掩盖掉主机号,剩余的就是网络号。
     将子网掩码和 IP 地址按位计算 AND,就可得到网络号。
  • 2、为什么要分离网络号和主机号?
     因为两台计算机要通讯,首先要判断是否处于同一个广播域内,即网络地址是否相同。如果网络地址相同,表明接受方在本网络上,那么可以把数据包直接发送到目标主机。
     路由器寻址工作中,也就是通过这样的方式来找到对应的网络号的,进而把数据包转发给对应的网络内。
  • 3、怎么进行子网划分?
     我们知道可以通过子网掩码划分出网络号和主机号,那实际上子网掩码还有一个作用,那就是划分子网。
     子网划分实际上是将主机地址分为两个部分:子网网络地址和子网主机地址。形式如下:

未做子网划分的 ip 地址:网络地址+主机地址。
做子网划分后的 ip 地址:网络地址+(子网网络地址+子网主机地址)。

 假设对 C 类地址进行子网划分,网络地址 192.168.1.0,使用子网掩码 255.255.255.192 对其进行子网划分。C 类地址中前 24 位是网络号,最后 8 位是主机号,根据子网掩码可知从 8 位主机号中借用 2 位作为子网号。

 由于子网网络地址被划分成 2 位,那么子网地址就有 4 个,分别是 00、01、10、11,具体划分如下:

 划分后的 4 个子网如下表格:

2.2.3 公有 IP 地址与私有 IP 地址

  在 A、B、C 分类地址,实际上有分公有 IP 地址和私有 IP 地址。

  平时我们办公室、家里、学校用的 IP 地址,一般都是私有 IP 地址。因为这些地址允许组织内部的 IT人员自己管理、自己分配,而且可以重复。因此,你学校的某个私有 IP 地址和我学校的可以是一样的。
  公有 IP 地址是有个组织统一分配的,假设你要开一个博客网站,那么你就需要去申请购买一个公有 IP,这样全世界的人才能访问。并且公有 IP 地址基本上要在整个互联网范围内保持唯一。

  • 1、公有 IP 地址由谁管理呢
     私有 IP 地址通常是内部的 IT 人员管理,公有 IP 地址是由 ICANN 组织管理,中文叫「互联网名称与数字地址分配机构」。
     IANA 是 ICANN 的其中一个机构,它负责分配互联网 IP 地址,是按洲的方式层层分配。

ARIN 北美地区
LACNIC 拉丁美洲和一些加勒比群岛
RIPE NCC 欧洲、中东和中亚
AfriNIC 非洲地区
APNIC 亚太地区

 在中国是由 CNNIC 的机构进行管理,它是中国国内唯一指定的全局 IP 地址管理的组织。

2.2.4 IP 地址与路由控制

  IP地址的网络地址这一部分是用于进行路由控制。路由控制表中记录着网络地址与下一步应该发送至路由器的地址。在主机和路由器上都会有各自的路由器控制表。
  在发送 IP 包时,首先要确定 IP 包首部中的目标地址,再从路由控制表中找到与该地址具有相同网络地址的记录,根据该记录将 IP 包转发给相应的下一个路由器。如果路由控制表中存在多条相同网络地址的记录,就选择相同位数最多的网络地址,也就是最长匹配。
  例子:

  1. 主机 A 要发送一个 IP 包,其源地址是 10.1.1.30 和目标地址是 10.1.2.10 ,由于没有在主机 A的路由表找到与目标地址 10.1.2.10 的网络地址,于是包被转发到默认路由(路由器 1 );
  2. 路由器 1 收到 IP 包后,也在路由器 1 的路由表匹配与目标地址相同的网络地址记录,发现匹配到了,于是就把 IP 数据包转发到了 10.1.0.2 这台路由器 2;
  3. 路由器 2 收到后,同样对比自身的路由表,发现匹配到了,于是把 IP 包从路由器 2 的10.1.2.1 这个接口出去,最终经过交换机把 IP 数据包转发到了目标主机。

  环回地址是在同一台计算机上的程序之间进行网络通信时所使用的一个默认地址。计算机使用一个特殊的 IP 地址 127.0.0.1 作为环回地址。与该地址具有相同意义的是一个叫做localhost 的主机名。使用这个 IP 或主机名时,数据包不会流向网络。

2.2.5 IP 分片与重组

  每种数据链路的最大传输单元 MTU 都是不相同的,如 FDDI 数据链路 MTU 4352、以太网的 MTU 是1500 字节等。每种数据链路的 MTU 之所以不同,是因为每个不同类型的数据链路的使用目的不同。使用目的不同,可承载的 MTU 也就不同。
  们最常见数据链路是以太网,它的 MTU 是 1500 字节。那么当 IP 数据包大小大于 MTU 时, IP 数据包就会被分片。经过分片之后的 IP 数据报在被重组的时候,只能由目标主机进行,路由器是不会进行重组的。假设发送方发送一个 4000 字节的大数据报,若要传输在以太网链路,则需要把数据报分片成 3 个小数据报进行传输,再交由接收方重组成大数据报。

  在分片传输中,一旦某个分片丢失,则会造成整个 IP 数据报作废,所以 TCP 引入了 MSS 也就是在TCP 层进行分片不由 IP 层分片,那么对于 UDP 我们尽量不要发送一个大于 MTU 的数据报文。

2.2.6 IPv6 基本认识

  IPv4 的地址是 32 位的,大约可以提供 42 亿个地址。 IPv6 的地址是 128 位的。 IPv6 除了有更多的地址之外,还有更好的安全性和扩展性,说简单点就是 IPv6 相比于 IPv4 能带来更好的网络体验。

  • 1、IPv6 的亮点
     IPv6 可自动配置,即使没有 DHCP 服务器也可以实现自动分配IP地址。
     IPv6 包头包首部长度采用固定的值 40 字节,去掉了包头校验和,简化了首部结构,减轻了路由器负荷,大大提高了传输的性能。
     IPv6 有应对伪造 IP 地址的网络安全功能以及防止线路窃听的功能,大大提升了安全性。
  • 2、IPv6 地址的标识方法
     IPv4 地址长度共 32 位,是以每 8 位作为一组,并用点分十进制的表示方式。
     IPv6 地址长度是 128 位,是以每 16 位作为一组,每组用冒号 「:」 隔开。

     如果出现连续的 0 时还可以将这些 0 省略,并用两个冒号 「::」隔开。但是,一个 IP 地址中只允许出现一次两个连续的冒号。示例:
  • 3、IPv6 地址的结构
     IPv6 的地址主要有以下类型地址:

单播地址,用于一对一的通信
组播地址,用于一对多的通信
任播地址,用于通信最近的节点,最近的节点是由路由协议决定
没有广播地址

  • 4、IPv6 单播地址类型
     对于一对一通信的 IPv6 地址,主要划分了三类单播地址,每类地址的有效范围都不同:

在同一链路单播通信,不经过路由器,可以使用链路本地单播地址,IPv4 没有此类型;
在内网里单播通信,可以使用唯一本地地址,相当于 IPv4 的私有 IP;
在互联网通信,可以使用全局单播地址,相当于 IPv4 的公有 IP。

2.2.7 IPv4 首部与 IPv6 首部

  IPv4 首部与 IPv6 首部的差异:

  IPv6 相比 IPv4 的首部改进:

  1. 取消了首部校验和字段。 因为在数据链路层和传输层都会校验,因此 IPv6 直接取消了 IP 的校验。
  2. 取消了分片/重新组装相关字段。 分片与重组是耗时的过程,IPv6 不允许在中间路由器进行分片与重组,这种操作只能在源与目标主机,这将大大提高了路由器转发的速度。
  3. 取消选项字段。 选项字段不再是标准 IP 首部的一部分了,但它并没有消失,而是可能出现在IPv6 首部中的「下一个首部」指出的位置上。删除该选项字段使的 IPv6 的首部成为固定长度的40 字节。

2.3 IP 协议

  与 IP 协议相关的重要且常见的技术:

DNS 域名解析
ARP 与 RARP 协议
DHCP 动态获取 IP 地址
NAT 网络地址转换
ICMP 互联网控制报文协议
IGMP 因特网组管理协

2.3.1 DNS

  DNS 可以将域名网址自动转换为具体的 IP 地址。
  DNS 中的域名都是用句点来分隔的,比如 www.server.com ,这里的句点代表了不同层次之间的界限。在域名中,越靠右的位置表示其层级越高。
  根域是在最顶层,它的下一层就是 com 顶级域,再下面是 server.com。因此,域名的层级关系类似一个树状结构:

根 DNS 服务器
顶级域 DNS 服务器(com)
权威 DNS 服务器(server.com)


  根域的 DNS 服务器信息保存在互联网中所有的 DNS 服务器中。这样一来,任何 DNS 服务器就都可以找到并访问根域 DNS 服务器了。因此,客户端只要能够找到任意一台 DNS 服务器,就可以通过它找到根域 DNS 服务器,然后再一路顺藤摸瓜找到位于下层的某台目标 DNS 服务器。

  • 域名解析的工作流程
     浏览器首先看一下自己的缓存里有没有,如果没有就向操作系统的缓存要,还没有就检查本机域名解析文件 hosts ,如果还是没有,就会 DNS 服务器进行查询,查询的过程如下:
  1. 客户端首先会发出一个 DNS 请求,问 www.server.com 的 IP 是啥,并发给本地 DNS 服务器(也就是客户端的 TCP/IP 设置中填写的 DNS 服务器地址)。
  2. 本地域名服务器收到客户端的请求后,如果缓存里的表格能找到 www.server.com,则它直接返回IP 地址。如果没有,本地 DNS 会去问它的根域名服务器:“老大, 能告诉我 www.server.com 的IP 地址吗?” 根域名服务器是最高层次的,它不直接用于域名解析,但能指明一条道路。
  3. 根 DNS 收到来自本地 DNS 的请求后,发现后置是 .com,说“www.server.com 这个域名归.com 区域管理”,我给你 .com 顶级域名服务器地址给你,你去问问它吧。”
  4. 本地 DNS 收到顶级域名服务器的地址后,发起请求问“老二, 你能告诉我 www.server.com 的 IP地址吗?”
  5. 顶级域名服务器说:“我给你负责 www.server.com 区域的权威 DNS 服务器的地址,你去问它应该能问到”。
  6. 本地 DNS 于是转向问权威 DNS 服务器:“老三,www.server.com对应的IP是啥呀?” server.com的权威 DNS 服务器,它是域名解析结果的原出处。为啥叫权威呢?就是我的域名我做主。
  7. 权威 DNS 服务器查询后将对应的 IP 地址 X.X.X.X 告诉本地 DNS。
  8. 本地 DNS 再将 IP 地址返回客户端,客户端和目标建立连接。

2.3.2 ARP

  在传输一个 IP 数据报的时候,确定了源 IP 地址和目标 IP 地址后,就会通过主机「路由表」确定 IP 数据包下一跳。然而,网络层的下一层是数据链路层,所以我们还要知道「下一跳」的 MAC 地址。
  由于主机的路由表中可以找到下一跳的 IP 地址,所以可以通过ARP 协议,求得下一跳的 MAC 地址。
  简单地说,ARP 是借助 ARP 请求与 ARP 响应两种类型的包确定 MAC 地址的。

1、主机会通过广播发送 ARP 请求,这个包中包含了想要知道的 MAC 地址的主机 IP 地址。
2、当同个链路中的所有设备收到 ARP 请求时,会去拆开 ARP 请求包里的内容,如果 ARP 请求包中的目标 IP 地址与自己的 IP 地址一致,那么这个设备就将自己的 MAC 地址塞入 ARP 响应包返回给主机。

  操作系统通常会把第一次通过 ARP 获取的 MAC 地址缓存起来,以便下次直接从缓存中找到对应 IP 地址的 MAC 地址。不过,MAC 地址的缓存是有一定期限的,超过这个期限,缓存的内容将被清除。

  • RARP 协议
      ARP 协议是已知 IP 地址求 MAC 地址,那 RARP 协议正好相反,它是已知 MAC 地址求 IP 地址。例如将打印机服务器等小型嵌入式设备接入到网络时就经常会用得到。
      通常需要架设一台 RARP 服务器,在这个服务器上注册设备的 MAC 地址及其 IP 地址。然后再将这个设备接入到网络,接着发生的:

1、该设备会发送一条「我的 MAC 地址是XXXX,请告诉我,我的IP地址应该是什么」的请求信息。
2、RARP 服务器接到这个消息后返回「MAC地址为 XXXX 的设备,IP地址为 XXXX」的信息给这个设备。

  设备就根据从 RARP 服务器所收到的应答信息设置自己的 IP 地址。

2.3.3 DHCP

  我们的电脑通常都是通过 DHCP 动态获取 IP 地址,这是DHCP常规的应用。其4个步骤:

说明一点,DHCP 客户端进程监听的是 68 端口号,DHCP 服务端进程监听的是 67 端口号。

   4 个步骤的详细描述:

  1. 客户端首先发起 DHCP 发现报文(DHCP DISCOVER) 的 IP 数据报,由于客户端没有 IP 地址,也不知道 DHCP 服务器的地址,所以使用的是 UDP 广播通信,其使用的广播目的地址是255.255.255.255(端口 67) 并且使用 0.0.0.0(端口 68) 作为源 IP 地址。DHCP 客户端将该IP 数据报传递给链路层,链路层然后将帧广播到所有的网络中设备。
  2. DHCP 服务器收到 DHCP 发现报文时,用 DHCP 提供报文(DHCP OFFER) 向客户端做出响应。该报文仍然使用 IP 广播地址 255.255.255.255,该报文信息携带服务器提供可租约的 IP 地址、子网掩码、默认网关、DNS 服务器以及 IP 地址租用期。
  3. 客户端收到一个或多个服务器的 DHCP 提供报文后,从中选择一个服务器,并向选中的服务器发送 DHCP 请求报文(DHCP REQUEST进行响应,回显配置的参数。
  4. 最后,服务端用 DHCP ACK 报文对 DHCP 请求报文进行响应,应答所要求的参数。

  一旦客户端收到 DHCP ACK 后,交互便完成了,并且客户端能够在租用期内使用 DHCP 服务器分配的IP 地址。
  如果租约的 DHCP IP 地址快期后,客户端会向服务器发送 DHCP 请求报文:

1)服务器如果同意继续租用,则用 DHCP ACK 报文进行应答,客户端就会延长租期。
2)服务器如果不同意继续租用,则用 DHCP NACK 报文,客户端就要停止使用租约的 IP 地址。

  DHCP 交互中,全程都是使用 UDP 广播通信。

  • 如果 DHCP 服务器和客户端不是在同一个局域网内,路由器又不会转发广播包,那不是每个网络都要配一个 DHCP 服务器?
     为了解决这一问题,就出现了 DHCP 中继代理。有了 DHCP 中继代理以后,对不同网段的 IP 地址分配也可以由一个 DHCP 服务器统一进行管理。

1、DHCP 客户端会向 DHCP 中继代理发送 DHCP 请求包,而 DHCP 中继代理在收到这个广播包以后,再以单播的形式发给 DHCP 服务器。
2、服务器端收到该包以后再向 DHCP 中继代理返回应答,并由 DHCP 中继代理将此包广播给DHCP 客户端 。

 因此,DHCP 服务器即使不在同一个链路上也可以实现统一分配和管理IP地址。

2.3.4 NAT

  网络地址转换 NAT 的方法,可以缓解了 IPv4 地址耗尽的问题。简单的来说 NAT 就是同个公司、家庭、教室内的主机对外部通信时,把私有 IP 地址转换成公有 IP 地址。

  在进行转换的时候,可以把 IP 地址 + 端口号一起进行转换,这种转换技术就叫网络地址与端口转换 NAPT。示例:

  图中有两个客户端 192.168.1.10 和 192.168.1.11 同时与服务器 183.232.231.172 进行通信,并且这两个客户端的本地端口都是 1025。此时,两个私有 IP 地址都转换 IP 地址为公有地址 120.229.175.121,但是以不同的端口号作为区分。
  于是,生成一个 NAPT 路由器的转换表,就可以正确地转换地址跟端口的组合,令客户端 A、B 能同时与服务器之间进行通信。

  • 1、NAT的缺点

1、外部无法主动与 NAT 内部服务器建立连接,因为 NAPT 转换表没有转换记录。
2、转换表的生成与转换操作都会产生性能开销。
3、通信过程中,如果 NAT 路由器重启了,所有的 TCP 连接都将被重置。

  • 2、如何解决 NAT 潜在的问题呢
     第一种就是改用 IPv6。IPv6 可用范围非常大,以至于每台设备都可以配置一个公有 IP 地址。
     第二种 NAT 穿透技术。NAT 穿越技术拥有这样的功能,它能够让网络应用程序主动发现自己位于 NAT 设备之后,并且会主动获得 NAT 设备的公有 IP,并为自己建立端口映射条目,注意这些都是 NAT设备后的应用程序自动完成的。
     也就是说,在 NAT 穿透技术中,NAT设备后的应用程序处于主动地位,它已经明确地知道 NAT 设备要修改它外发的数据包,于是它主动配合 NAT 设备的操作,主动地建立好映射,这样就不像以前由 NAT设备来建立映射了。
     简单点说,就是客户端主动从 NAT 设备获取公有 IP 地址,然后自己建立端口映射条目,然后用这个条目对外通信,就不需要 NAT 设备来进行转换了。

2.3.5 ICMP

  ICMP 全称是 Internet Control Message Protocol,也就是互联网控制报文协议。
  ICMP 主要的功能包括:确认 IP 包是否成功送达目标地址、报告发送过程中 IP 包被废弃的原因和改善网络设置等。
  在 IP 通信中如果某个 IP 包因为某种原因未能达到目标地址,那么这个具体的原因将由 ICMP 负责通知。

  上图例子,主机 A 向主机 B 发送了数据包,由于某种原因,途中的路由器 2 未能发现主机B 的存在,这时,路由器 2 就会向主机 A 发送一个 ICMP 目标不可达数据包,说明发往主机B 的包未能成功。
  ICMP 的这种通知消息会使用 IP 进行发送 。
  因此,从路由器 2 返回的 ICMP 包会按照往常的路由控制先经过路由器 1 再转发给主机 A 。收到该 ICMP 包的主机 A 则分解 ICMP 的首部和数据域以后得知具体发生问题的原因。
  ICMP 大致可以分为两大类:

一类是用于诊断的查询消息,也就是「查询报文类型」;
另一类是通知出错原因的错误消息,也就是「差错报文类型」。

  分的具体些:

2.3.6 IGMP

  IGMP 是因特网组管理协议,工作在主机(组播成员)和最后一跳路由之间,如图中的蓝色部分。

  IGMP 报文向路由器申请加入和退出组播组,默认情况下路由器是不会转发组播包到连接中的主机,除非主机通过 IGMP 加入到组播组,主机申请加入到组播组时,路由器就会记录 IGMP 路由器表,路由器后续就会转发组播包到对应的主机了。
  IGMP 报文采用 IP 封装,IP 头部的协议号为 2,而且 TTL 字段值通常为 1,因为 IGMP 是工作在主机与连接的路由器之间。
  IGMP 分为了三个版本分别是,IGMPv1、IGMPv2、IGMPv3。
  以 IGMPv2 作为例子,说说常规查询与响应和离开组播组这两个工作机制。

  • 1、常规查询与响应工作机制
  1. 路由器会周期性发送目的地址为 224.0.0.1 (表示同一网段内所有主机和路由器) IGMP 常规查询报文。
  2. 主机1 和 主机 3 收到这个查询,随后会启动「报告延迟计时器」,计时器的时间是随机的,通常是 0~10 秒,计时器超时后主机就会发送 IGMP 成员关系报告报文(源 IP 地址为自己主机的 IP地址,目的 IP 地址为组播地址)。如果在定时器超时之前,收到同一个组内的其他主机发送的成员关系报告报文,则自己不再发送,这样可以减少网络中多余的 IGMP 报文数量。
  3. 路由器收到主机的成员关系报文后,就会在 IGMP 路由表中加入该组播组,后续网络中一旦该组播地址的数据到达路由器,它会把数据包转发出去。
  • 2、离开组播组工作机制
     离开组播组的情况一,网段中仍有该组播组:
  1. 主机 1 要离开组 224.1.1.1,发送 IGMPv2 离组报文,报文的目的地址是 224.0.0.2(表示发向网段内的所有路由器)
  2. 路由器 收到该报文后,以 1 秒为间隔连续发送 IGMP 特定组查询报文(共计发送 2 个),以便确认该网络是否还有 224.1.1.1 组的其他成员。
  3. 主机 3 仍然是组 224.1.1.1 的成员,因此它立即响应这个特定组查询。路由器知道该网络中仍然存在该组播组的成员,于是继续向该网络转发 224.1.1.1 的组播数据包。

 离开组播组的情况二,网段中没有该组播组:

  1. 主机 1 要离开组播组 224.1.1.1,发送 IGMP 离组报文。
  2. 路由器收到该报文后,以 1 秒为间隔连续发送 IGMP 特定组查询报文(共计发送 2 个)。此时在该网段内,组 224.1.1.1 已经没有其他成员了,因此没有主机响应这个查询。
  3. 一定时间后,路由器认为该网段中已经没有 224.1.1.1 组播组成员了,将不会再向这个网段转发该组播地址的数据包。

:假设一台机器加入组播地址,需要把IP改成组播地址吗?如果离开某个组播地址,需要 dhcp 重新请求个IP吗?
:组播地址不是用于机器 ip 地址的,因为组播地址没有网络号和主机号,所以跟 dhcp 没关系。组播地址一般是用于 udp 协议,机器发送 UDP 组播数据时,目标地址填的是组播地址,那么在组播组内的机器都能收到数据包。
是否加入组播组和离开组播组,是由 socket 一个接口实现的,主机 ip 是不用改变的。

三、Ping

  在判断与对方网络是否畅通,使用的最多的莫过于 ping 命令。

3.1 IP协议的助手 —— ICMP 协议

  ping 是基于 ICMP 协议工作的,ICMP 全称是 Internet Control Message Protocol,也就是互联网控制报文协议。
  ICMP 主要的功能包括:确认 IP 包是否成功送达目标地址、报告发送过程中 IP 包被废弃的原因和改善网络设置等。
  在 IP 通信中如果某个 IP 包因为某种原因未能达到目标地址,那么这个具体的原因将由 ICMP 负责通知。

  如上图例子,主机 A 向主机 B 发送了数据包,由于某种原因,途中的路由器 2 未能发现主机B 的存在,这时,路由器 2 就会向主机 A 发送一个 ICMP 目标不可达数据包,说明发往主机B 的包未能成功。
  ICMP 的这种通知消息会使用 IP 进行发送 。
  因此,从路由器 2 返回的 ICMP 包会按照往常的路由控制先经过路由器 1 再转发给主机 A 。收到该 ICMP 包的主机 A 则分解 ICMP 的首部和数据域以后得知具体发生问题的原因。
  ICMP 报文是封装在 IP 包里面,它工作在网络层,是 IP 协议的助手。

3.2 查询报文类型

  回送消息用于进行通信的主机或路由器之间,判断所发送的数据包是否已经成功到达对端的一种消息, ping 命令就是利用这个消息实现的。

  可以向对端主机发送回送请求的消息( ICMP Echo Request Message ,类型 8 ),也可以接收对端主机发回来的回送应答消息( ICMP Echo Reply Message ,类型 0 )。

  相比原生的 ICMP,这里多了两个字段:

标识符:用以区分是哪个应用程序发 ICMP 包,比如用进程 PID 作为标识符;
序号:序列号从 0 开始,每发送一次新的回送请求就会加 1 , 可以用来确认网络包是否有丢失。

  在选项数据中, ping 还会存放发送请求的时间值,来计算往返时间,说明路程的长短。

3.3 查询报文类型

  几个常用的 ICMP 差错报文的例子:

目标不可达消息 —— 类型 为 3
原点抑制消息 —— 类型 4
重定向消息 —— 类型 5
超时消息 —— 类型 11

  IP 路由器无法将 IP 数据包发送给目标地址时,会给发送端主机返回一个目标不可达的 ICMP 消息,并在这个消息中显示不可达的具体原因,原因记录在 ICMP 包头的代码字段。由此,根据 ICMP 不可达的具体消息,发送端主机也就可以了解此次发送不可达的具体原因。
   6 种常见的目标不可达类型的代码

  正常情况下,发送端主机发送 IP 数据报时,将 IP 首部的分片禁止标志位设置为 1 。根据这个标志位,途中的路由器遇到超过 MTU 大小的数据包时,不会进行分片,而是直接抛弃。
  随后,通过一个 ICMP 的不可达消息类型,代码为 4(原点抑制消息) 的报文,告知发送端主机。
  ICMP 原点抑制消息的目的就是为了缓和网络拥堵情况。

当路由器向低速线路发送数据时,其发送队列的缓存变为零而无法发送出去时,可以向 IP 包的源地址发送一个 ICMP 原点抑制消息。收到这个消息的主机借此了解在整个线路的某一处发生了拥堵的情况,从而增大 IP 包的传输间隔,减少网络拥堵的情况。然而,由于这种 ICMP 可能会引起不公平的网络通信,一般不被使用。

  • 1、重定向消息
     如果路由器发现发送端主机使用了「不是最优」的路径发送数据,那么它会返回一个 ICMP 重定向消息给这个主机。
     在这个消息中包含了最合适的路由信息和源数据。这主要发生在路由器持有更好的路由信息的情况下。路由器会通过这样的 ICMP 消息告知发送端,让它下次发给另外一个路由器。
  • 2、超时消息
     IP 包中有一个字段叫做 TTL ( Time To Live ,生存周期),它的值随着每经过一次路由器就会减1,直到减到 0 时该 IP 包会被丢弃。此时,路由器将会发送一个 ICMP 超时消息给发送端主机,并通知该包已被丢弃。
     设置 IP 包生存周期的主要目的,是为了在路由控制遇到问题发生循环状况时,避免 IP 包无休止地在网络上被转发。

3.4 ping

  重点来看 ping 的发送和接收过程。
  ping 命令执行的时候,源主机首先会构建一个 ICMP 回送请求消息数据包。
  ICMP 数据包内包含多个字段,最重要的是两个:

第一个是类型,对于回送请求消息而言该字段为 8 ;
另外一个是序号,主要用于区分连续 ping 的时候发出的多个数据包。

  每发出一个请求数据包,序号会自动加 1 。为了能够计算往返时间 RTT ,它会在报文的数据部分插入发送时间。

  然后,由 ICMP 协议将这个数据包连同地址 192.168.1.2 一起交给 IP 层。IP 层将以 192.168.1.2 作为目的地址,本机 IP 地址作为源地址,协议字段设置为 1 表示是 ICMP 协议,再加上一些其他控制信息,构建一个 IP 数据包。

  接下来,需要加入 MAC 头。如果在本地 ARP 映射表中查找出 IP 地址 192.168.1.2 所对应的 MAC地址,则可以直接使用;如果没有,则需要发送 ARP 协议查询 MAC 地址,获得 MAC 地址后,由数据链路层构建一个数据帧,目的地址是 IP 层传过来的 MAC 地址,源地址则是本机的 MAC 地址;还要附加上一些控制信息,依据以太网的介质访问规则,将它们传送出去。

  主机 B 收到这个数据帧后,先检查它的目的 MAC 地址,并和本机的 MAC 地址对比,如符合,则接收,否则就丢弃。
  接收后检查该数据帧,将 IP 数据包从帧中提取出来,交给本机的 IP 层。同样,IP 层检查后,将有用的信息提取后交给 ICMP 协议。
  主机 B 会构建一个 ICMP 回送响应消息数据包,回送响应数据包的类型字段为 0 ,序号为接收到的请求数据包中的序号,然后再发送出去给主机 A。

  在规定的时候间内,源主机如果没有接到 ICMP 的应答包,则说明目标主机不可达;如果接收到了ICMP 回送响应消息,则说明目标主机可达。
  此时,源主机会检查,用当前时刻减去该数据包最初从源主机上发出的时刻,就是 ICMP 数据包的时间延迟。

  这只是最简单的,同一个局域网里面的情况。如果跨网段的话,还会涉及网关的转发、路由器的转发等等。
  但是对于 ICMP 的头来讲,是没什么影响的。会影响的是根据目标 IP 地址,选择路由的下一跳,还有每经过一个路由器到达一个新的局域网,需要换 MAC 头里面的 MAC 地址。
  说了这么多,可以看出 ping 这个程序是使用了 ICMP 里面的 ECHO REQUEST(类型为 8 ) 和ECHO REPLY (类型为 0)

3.5 traceroute

  有一款充分利用 ICMP 差错报文类型的应用叫做 traceroute (在UNIX、MacOS中是这个命令,而在Windows中对等的命令叫做 tracert )。

3.5.1 traceroute作用一

  traceroute 的第一个作用就是故意设置特殊的 TTL,来追踪去往目的地时沿途经过的路由器
  traceroute 的参数指向某个目的 IP 地址,示例:

traceroute 192.168.1.100

  traceroute的原理就是利用 IP 包的生存期限 从 1 开始按照顺序递增的同时发送 UDP 包,强制接收 ICMP 超时消息的一种方法。

 比如,将 TTL 设置 为 1 ,则遇到第一个路由器,就牺牲了,接着返回 ICMP 差错报文网络包,类型是时间超时。
 接下来将 TTL 设置为 2 ,第一个路由器过了,遇到第二个路由器也牺牲了,也同时返回了 ICMP 差错报文数据包,如此往复,直到到达目的主机。
 这样的过程,traceroute 就可以拿到了所有的路由器 IP。

  当然有的路由器根本就不会返回这个 ICMP,所以对于有的公网地址,是看不到中间经过的路由的。
  traceroute 在发送 UDP 包时,会填入一个不可能的端口号值作为 UDP 目标端口号(大于 3000)。当目的主机,收到 UDP 包后,会返回 ICMP 差错报文消息,但这个差错报文消息的类型是「端口不可达」。所以,当差错报文类型是端口不可达时,说明发送方发出的 UDP 包到达了目的主机。

3.5.2 traceroute作用二

  traceroute 还有一个作用是故意设置不分片,从而确定路径的 MTU。这样做的目的是为了路径MTU发现。
  因为有的时候我们并不知道路由器的 MTU 大小,以太网的数据链路上的 MTU 通常是 1500 字节,但是非以外网的 MTU 值就不一样了,所以我们要知道 MTU 的大小,从而控制发送的包大小。

  工作原理:首先在发送端主机发送 IP 数据报时,将 IP 包首部的分片禁止标志位设置为 1。根据这个标志位,途中的路由器不会对大数据包进行分片,而是将包丢弃。随后,通过一个 ICMP 的不可达消息将数据链路上 MTU 的值一起给发送主机,不可达消息的类型为「需要进行分片但设置了不分片位」。
发送主机端每次收到 ICMP 差错报文时就减少包的大小,以此来定位一个合适的 MTU 值,以便能到达目标主机。

四、从URL到网页

  图较简单的网络拓扑模型:

4.1 HTTP

  首先浏览器做的第一步工作就是要对 URL 进行解析,从而生成发送给 Web 服务器的请求信息。 URL 里的各个元素的代表什么,见下图:

  URL 实际上是请求服务器里的文件资源。当没有路径名时,就代表访问根目录下事先设置的默认文件,也就是 /index.html 或者 /default.html这些文件,这样就不会发生混乱了。
  对 URL 进行解析之后,浏览器确定了 Web 服务器和文件名,接下来就是根据这些信息来生成 HTTP请求消息了。

4.2 DNS

  通过浏览器解析 URL 并生成 HTTP 消息后,需要委托操作系统将消息发送给 Web 服务器。但在发送之前,还有一项工作需要完成,那就是查询服务器域名对应的 IP 地址,因为委托操作系统发送消息时,必须提供通信对象的 IP 地址。
  所以,有一种服务器就专门保存了 Web 服务器域名与 IP 的对应关系,它就是 DNS 服务器。
  这些知识点可以参考之前章节。

DNS 中的域名都是用句点来分隔的,比如 www.server.com ,这里的句点代表了不同层次之间的界限。在域名中,越靠右的位置表示其层级越高。

  根域是在最顶层,它的下一层就是 com 顶级域,再下面是 server.com。所以域名的层级关系类似一个树状结构:

根 DNS 服务器
顶级域 DNS 服务器(com)
权威 DNS 服务器(server.com)


  根域的 DNS 服务器信息保存在互联网中所有的 DNS 服务器中。这样一来,任何 DNS 服务器就都可以找到并访问根域 DNS 服务器了。因此,客户端只要能够找到任意一台 DNS 服务器,就可以通过它找到根域 DNS 服务器,然后再一路顺藤摸瓜找到位于下层的某台目标 DNS 服务器。
  DNS 的解析过程:

  1. 客户端首先会发出一个 DNS 请求,问 www.server.com 的 IP 是啥,并发给本地 DNS 服务器(也就是客户端的 TCP/IP 设置中填写的 DNS 服务器地址)。
  2. 本地域名服务器收到客户端的请求后,如果缓存里的表格能找到 www.server.com,则它直接返回
    IP 地址。如果没有,本地 DNS 会去问它的根域名服务器:“老大, 能告诉我 www.server.com 的IP 地址吗?” 根域名服务器是最高层次的,它不直接用于域名解析,但能指明一条道路。
  3. 根 DNS 收到来自本地 DNS 的请求后,发现后置是 .com,说:“www.server.com 这个域名归.com 区域管理”,我给你 .com 顶级域名服务器地址给你,你去问问它吧。”
  4. 本地 DNS 收到顶级域名服务器的地址后,发起请求问“老二, 你能告诉我 www.server.com 的 IP地址吗?”
  5. 顶级域名服务器说:“我给你负责 www.server.com 区域的权威 DNS 服务器的地址,你去问它应该能问到”。
  6. 本地 DNS 于是转向问权威 DNS 服务器:“老三,www.server.com对应的IP是啥呀?” server.com的权威 DNS 服务器,它是域名解析结果的原出处。为啥叫权威呢?就是我的域名我做主。
  7. 权威 DNS 服务器查询后将对应的 IP 地址 X.X.X.X 告诉本地 DNS。
  8. 本地 DNS 再将 IP 地址返回客户端,客户端和目标建立连接。

  图示:

4.3 协议栈

  通过 DNS 获取到 IP 后,就可以把 HTTP 的传输工作交给操作系统中的协议栈。
  协议栈的内部分为几个部分,分别承担不同的工作。上下关系是有一定的规则的,上面的部分会向下面的部分委托工作,下面的部分收到委托的工作并执行。

  应用程序(浏览器)通过调用 Socket 库,来委托协议栈工作。协议栈的上半部分有两块,分别是负责收发数据的 TCP 和 UDP 协议,它们两会接受应用层的委托执行收发数据的操作。
  协议栈的下面一半是用 IP 协议控制网络包收发操作,在互联网上传数据时,数据会被切分成一块块的网络包,而将网络包发送给对方的操作就是由 IP 负责的。
  此外 IP 中还包括 ICMP 协议和 ARP 协议。

ICMP 用于告知网络包传送过程中产生的错误以及各种控制信息。
ARP 用于根据 IP 地址查询相应的以太网 MAC 地址。

  IP 下面的网卡驱动程序负责控制网卡硬件,而最下面的网卡则负责完成实际的收发操作,也就是对网线中的信号执行发送和接收操作。

4.4 TCP

  HTTP 是基于 TCP 协议传输的。 TCP 报文头部的格式:

  源端口号和目标端口号是不可少的,如果没有这两个端口号,数据就不知道应该发给哪个应用。接下来有包的序号,这个是为了解决包乱序的问题。
  还有应该有的是确认号,目的是确认发出去对方是否有收到。如果没有收到就应该重新发送,直到送达,这个是为了解决不丢包的问题。
  接下来还有一些状态位。例如 SYN 是发起一个连接, ACK 是回复, RST 是重新连接, FIN 是结束连接等。TCP 是面向连接的,因而双方要维护连接的状态,这些带状态位的包的发送,会引起双方的状态变更。
  还有一个重要的就是窗口大小。TCP 要做流量控制,通信双方各声明一个窗口(缓存大小),标识自己当前能够的处理能力,别发送的太快,撑死我,也别发的太慢,饿死我。
  除了做流量控制以外,TCP还会做拥塞控制,对于真正的通路堵车不堵车,它无能为力,唯一能做的就是控制自己,也即控制发送的速度。

4.4.1 三次握手

  在 HTTP 传输数据之前,首先需要 TCP 建立连接,TCP 连接的建立,通常称为三次握手。

  1. 一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN状态。
  2. 然后客户端主动发起连接 SYN ,之后处于 SYN-SENT 状态。
  3. 服务端收到发起的连接,返回 SYN ,并且 ACK 客户端的 SYN ,之后处于 SYN-RCVD 状态。
  4. 客户端收到服务端发送的 SYN 和 ACK 之后,发送 ACK 的 ACK ,之后处于
    ESTABLISHED 状态,因为它一发一收成功了。
  5. 服务端收到 ACK 的 ACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。

  三次握手目的是保证双方都有发送和接收的能力。
  TCP 的连接状态查看,在 Linux 可以通过 netstat -napt 命令查看。

  如果 HTTP 请求消息比较长,超过了 MSS 的长度,这时 TCP 就需要把 HTTP 的数据拆解成一块块的数据发送,而不是一次性发送所有数据。

4.4.2 TCP分割数据

  如果 HTTP 请求消息比较长,超过了 MSS 的长度,这时 TCP 就需要把 HTTP 的数据拆解成一块块的数据发送,而不是一次性发送所有数据。

MTU :一个网络包的最大长度,以太网中一般为 1500 字节。
MSS :除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度。

  数据会被以 MSS 的长度为单位进行拆分,拆分出来的每一块数据都会被放进单独的网络包中。也就是在每个被拆分的数据加上 TCP 头信息,然后交给 IP 模块来发送数据。

4.4.3 TCP 报文生成

  TCP 协议里面会有两个端口,一个是浏览器监听的端口(通常是随机生成的),一个是 Web 服务器监听的端口(HTTP 默认端口号是 80 , HTTPS 默认端口号是 443 )。
  在双方建立了连接后,TCP 报文中的数据部分就是存放 HTTP 头部 + 数据,组装好 TCP 报文之后,就需交给下面的网络层处理。

4.5 IP

  TCP 模块在执行连接、收发、断开等各阶段操作时,都需要委托 IP 模块将数据封装成网络包发送给通信对象。
   IP 报文头部的格式:

  在 IP 协议里面需要有源地址 IP 和 目标地址 IP:

源地址IP,即是客户端输出的 IP 地址;
目标地址,即通过 DNS 域名解析得到的 Web 服务器 IP。

  因为 HTTP 是经过 TCP 传输的,所以在 IP 包头的协议号,要填写为 06 (十六进制),表示协议为TCP。
  假设客户端有多个网卡,就会有多个 IP 地址,那 IP 头部的源地址应该选择哪个 IP 呢?这个时候就需要根据路由表规则,来判断哪一个网卡作为源地址 IP。
  在 Linux 操作系统,我们可以使用 route -n 命令查看当前系统的路由表。

  举个例子,根据上面的路由表,我们假设 Web 服务器的目标地址是 192.168.10.200 。

  1. 首先先和第一条目的子网掩码( Genmask )进行 与运算,得到结果为 192.168.10.0 ,但是第一
    个条目的 Destination 是 192.168.3.0 ,两者不一致所以匹配失败。
  2. 再与第二条目的子网掩码进行 与运算,得到的结果为 192.168.10.0 ,与第二条目的 Destination
    192.168.10.0 匹配成功,所以将使用 eth1 网卡的 IP 地址作为 IP 包头的源地址。

  假设 Web 服务器的目标地址是 10.100.20.100 ,那么依然依照上面的路由表规则判断,判断后的结果是和第三条目匹配。第三条目比较特殊,它目标地址和子网掩码都是 0.0.0.0 ,这表示默认网关,如果其他所有条目都无法匹配,就会自动匹配这一行。并且后续就把包发给路由器, Gateway 即是路由器的 IP 地址。
  至此,网络包的报文:

4.6 MAC

  生成了 IP 头部之后,接下来网络包还需要在 IP 头部的前面加上 MAC 头部。MAC 头部是以太网使用的头部,它包含了接收方和发送方的 MAC 地址等信息。

  在 MAC 包头里需要发送方 MAC 地址和接收方目标 MAC 地址,用于两点之间的传输。一般在 TCP/IP 通信里,MAC 包头的协议类型只使用:

0800 : IP 协议
0806 : ARP 协议

  发送方的 MAC 地址获取就比较简单,MAC 地址是在网卡生产时写入到 ROM 里的,只要将这个值读取出来写入到 MAC 头部就可以了。
  接收方的 MAC 地址有点复杂,只要告诉以太网对方的 MAC 的地址,以太网就会帮我们把包发送过去,那么很显然这里应该填写对方的 MAC 地址。所以先得搞清楚应该把包发给谁,这个只要查一下路由表就知道。在路由表中找到相匹配的条目,然后把包发给 Gateway 列中的 IP 地址就可以了。
  从IP到MAC的获悉是用ARP 协议实现的。ARP 协议会在以太网中以广播的形式,对以太网所有的设备喊出:“这个 IP 地址是谁的?请把你的MAC 地址告诉我”。

操作系统会把本次查询结果放到一块叫做 ARP 缓存的内存空间留着以后用,不过缓存的时间就几分钟。

  在发包时:先查询 ARP 缓存,如果其中已经保存了对方的 MAC 地址,就不需要发送 ARP 查询,直接使用ARP 缓存中的地址。当 ARP 缓存中不存在对方 MAC 地址时,则发送 ARP 广播查询。
  在 Linux 系统中,我们可以使用 arp -a 命令来查看 ARP 缓存的内容。

  至此,网络包的报文:

4.7 网卡

  IP 生成的网络包只是存放在内存中的一串二进制数字信息,没有办法直接发送给对方。因此,我们需要将数字信息转换为电信号,才能在网线上传输,也就是说,这才是真正的数据发送过程。负责执行这一操作的是网卡,要控制网卡还需要靠网卡驱动程序。
  网卡驱动从 IP 模块获取到包之后,会将其复制到网卡内的缓存区中,接着会在其开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列。

起始帧分界符是一个用来表示包起始位置的标记;
末尾的 FCS (帧校验序列)用来检查包传输过程是否有损坏。

  最后网卡会将包转为电信号,通过网线发送出去。

4.8 交换机

  交换机的设计是将网络包原样转发到目的地。交换机工作在
MAC 层,也称为二层网络设备。

  • 1、交换机的包接收操作
     首先,电信号到达网线接口,交换机里的模块进行接收,接下来交换机里的模块将电信号转换为数字信号。然后通过包末尾的 FCS 校验错误,如果没问题则放到缓冲区。这部分操作基本和计算机的网卡相同,但交换机的工作方式和网卡不同。
     计算机的网卡本身具有 MAC 地址,并通过核对收到的包的接收方 MAC 地址判断是不是发给自己的,如果不是发给自己的则丢弃;相对地,交换机的端口不核对接收方 MAC 地址,而是直接接收所有的包并存放到缓冲区中。因此,和网卡不同,交换机的端口不具有 MAC 地址。
     将包存入缓冲区后,接下来需要查询一下这个包的接收方 MAC 地址是否已经在 MAC 地址表中有记录了。
     交换机的 MAC 地址表主要包含两个信息:

一个是设备的 MAC 地址,
另一个是该设备连接在交换机的哪个端口上。


 举个例子,如果收到的包的接收方 MAC 地址为 00-02-B3-1C-9C-F9 ,则与图中表中的第 3 行匹配,根据端口列的信息,可知这个地址位于 3 号端口上,然后就可以通过交换电路将包发送到相应的端口了。
 所以,交换机根据 MAC 地址表查找 MAC 地址,然后将信号发送到相应的端口

  • 2、当 MAC 地址表找不到指定的 MAC 地址会怎么样?
     地址表中找不到指定的 MAC 地址。这可能是因为具有该地址的设备还没有向交换机发送过包,或者这个设备一段时间没有工作导致地址被从地址表中删除了。这种情况下,交换机无法判断应该把包转发到哪个端口,只能将包转发到除了源端口之外的所有端口上,无论该设备连接在哪个端口上都能收到这个包。
     因为以太网的设计本来就是将包发送到整个网络的,然后只有相应的接收者才接收包,而其他设备则会忽略这个包。
     不用过于担心成网络拥塞的问题,因为发送了包之后目标设备会作出响应,只要返回了响应包,交换机就可以将它的地址写入 MAC 地址表,下次也就不需要把包发到所有端口了。
     此外,如果接收方 MAC 地址是一个广播地址,那么交换机会将包发送到除源端口之外的所有端口。以下两个属于广播地址:

MAC 地址中的 FF:FF:FF:FF:FF:FF
IP 地址中的 255.255.255.255

4.9 路由器

  网络包经过交换机之后,现在到达了路由器,并在此被转发到下一个路由器或目标设备。这一步转发的工作原理和交换机类似,也是通过查表判断包转发的目标。
  过在具体的操作过程上,路由器和交换机是有区别的:

1、因为路由器是基于 IP 设计的,俗称三层网络设备,路由器的各个端口都具有 MAC 地址和 IP 地址;
2、交换机是基于以太网设计的,俗称二层网络设备,交换机的端口不具有 MAC 地址。

4.9.1 路由器基本原理

  路由器的端口具有 MAC 地址,因此它就能够成为以太网的发送方和接收方;同时还具有 IP 地址,从这个意义上来说,它和计算机的网卡是一样的。
  当转发包时,首先路由器端口会接收发给自己的以太网包,然后路由表查询转发目标,再由相应的端口作为发送方将以太网包发送出去。

4.9.2 路由器的包接收操作

  首先,电信号到达网线接口部分,路由器中的模块会将电信号转成数字信号,然后通过包末尾的 FCS进行错误校验。如果没问题则检查 MAC 头部中的接收方 MAC 地址,看看是不是发给自己的包,如果是就放到接收缓冲区中,否则就丢弃这个包。总的来说,路由器的端口都具有 MAC 地址,只接收与自身地址匹配的包,遇到不匹配的包则直接丢弃。

4.9.3 查询路由表确定输出端口

  完成包接收操作之后,路由器就会去掉包开头的 MAC 头部。MAC 头部的作用就是将包送达路由器,其中的接收方 MAC 地址就是路由器端口的 MAC 地址。因此,当包到达路由器之后,MAC 头部的任务就完成了,于是 MAC 头部就会被丢弃。
  接下来,路由器会根据 MAC 头部后方的 IP 头部中的内容进行包的转发操作。
  转发操作分为几个阶段,首先是查询路由表判断转发目标。

  举个例子:假设地址为 10.10.1.101 的计算机要向地址为 192.168.1.100 的服务器发送一个包,这个包先到达图中的路由器。判断转发目标的第一步,就是根据包的接收方 IP 地址查询路由表中的目标地址栏,以找到相匹配的记录。路由匹配和前面讲的一样,每个条目的子网掩码和 192.168.1.100 IP 做 & 与运算后,得到的结果与对应条目的目标地址进行匹配,如果匹配就会作为候选转发目标,如果不匹配就继续与下个条目进行路由匹配。
  如第二条目的子网掩码 255.255.255.0 与 192.168.1.100 IP 做 & 与运算后,得到结果是192.168.1.0 ,这与第二条目的目标地址 192.168.1.0 匹配,该第二条目记录就会被作为转发目标。实在找不到匹配路由时,就会选择默认路由,路由表中子网掩码为 0.0.0.0 的记录表示「默认路由」。

4.9.4 路由器的发送操作

  接下来就会进入包的发送操作。首先,需要根据路由表的网关列判断对方的地址:

如果网关是一个 IP 地址,则这个IP 地址就是我们要转发到的目标地址,还未抵达终点,还需继续需要路由器转发。
如果网关为空,则 IP 头部中的接收方 IP 地址就是要转发到的目标地址,也是就终于找到 IP 包头里的目标地址了,说明已抵达终点。

  知道对方的 IP 地址之后,接下来需要通过 ARP 协议根据 IP 地址查询 MAC 地址,并将查询的结果作为接收方 MAC 地址。路由器也有 ARP 缓存,因此首先会在 ARP 缓存中查询,如果找不到则发送 ARP 查询请求。
  接下来是发送方 MAC 地址字段,这里填写输出端口的 MAC 地址。还有一个以太类型字段,填写0080 (十六进制)表示 IP 协议。
  网络包完成后,接下来会将其转换成电信号并通过端口发送出去。
  发送出去的网络包会通过交换机到达下一个路由器。由于接收方 MAC 地址就是下一个路由器的地址,所以交换机会根据这一地址将包传输到下一个路由器。接下来,下一个路由器会将包转发给再下一个路由器,经过层层转发之后,网络包就到达了最终的目的地。
  在网络包传输的过程中,源 IP 和目标 IP 始终是不会变的,一直变化的是 MAC 地址,因为需要 MAC 地址在以太网内进行两个设备之间的包传输。

4.10 服务器 与 客户端


  数据包抵达服务器后,服务器会先扒开数据包的 MAC 头部,查看是否和服务器自己的 MAC 地址符合,符合就将包收起来。接着继续扒开数据包的 IP 头,发现 IP 地址符合,根据 IP 头中协议项,知道自己上层是 TCP 协议。
  于是,扒开 TCP 的头,里面有序列号,需要看一看这个序列包是不是我想要的,如果是就放入缓存中然后返回一个 ACK,如果不是就丢弃。TCP头部里面还有端口号, HTTP 的服务器正在监听这个端口号。
  于是,服务器自然就知道是 HTTP 进程想要这个包,于是就将包发给 HTTP 进程。服务器的 HTTP 进程看到,原来这个请求是要访问一个页面,于是就把这个网页封装在 HTTP 响应报文里。
  HTTP 响应报文也需要穿上 TCP、IP、MAC 头部,不过这次是源地址是服务器 IP 地址,目的地址是客户端 IP 地址。穿好头部衣服后,从网卡出去,交由交换机转发到出城的路由器,路由器就把响应数据包发到了下一个路由器,就这样跳。
  最后跳到了客户端的城门把手的路由器,路由器扒开 IP 头部发现是要找城内的人,于是又把包发给了城内的交换机,再由交换机转发到客户端。客户端收到了服务器的响应数据包后,同样也非常的高兴,客户能拆快递了!于是,客户端开始扒皮,把收到的数据包的皮扒剩 HTTP 响应报文后,交给浏览器去渲染页面,一份特别的数据包快递,就这样显示出来了!最后,客户端要离开了,向服务器发起了 TCP 四次挥手,至此双方的连接就断开了。

1、在发送数据包时,如果目标主机不是本地局域网,填入的MAC地址是路由器,也就是把数据包转发给路由器,路由器一直转发下一个路由器,直到转发到目标主机的路由器,发现 IP 地址是自己局域网内的主机,就会 arp 请求获取目标主机的 MAC 地址,从而转发到这个服务器主机。
2、转发的过程中,源IP地址和目标IP地址是不会变的,源MAC地址和目标MAC地址是会变化的。

五、三次握手和四次挥手

5.1 TCP 基本认识


  序列号:在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题
  确认应答号:指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决不丢包的问题
  控制位:

ACK:该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建立连接时的 SYN包之外该位必须设置为 1 。
RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。
SYN:该位为 1 时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定。
FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。

5.1.1 TCP 工作在哪一层

  IP 层是「不可靠」的,它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的 TCP 协议来负责。

  TCP 是一个工作在传输层的可靠数据传输的服务,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。

5.1.2 什么是 TCP 连接

  TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。

  1. 面向连接:一定是「一对一」才能连接,不能像 UDP 协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;
  2. 可靠的:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端;
  3. 字节流:消息是「没有边界」的,所以无论我们消息有多大都可以进行传输。并且消息是「有序的」,当「前一个」消息没有收到的时候,即使它先收到了后面的字节,那么也不能扔给应用层去处理,同时对「重复」的报文会自动丢弃。

  TCP 连接:简单来说就是,用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接。
  因此,建立一个 TCP 连接是需要客户端与服务器端达成上述三个信息的共识。

Socket:由 IP 地址和端口号组成
序列号:用来解决乱序问题等
窗口大小:用来做流量控制

5.1.3 如何唯一确定一个 TCP 连接

  TCP 四元组可以唯一的确定一个连接,四元组包括如下:源地址、源端口、目的地址、目的端口。
  源地址和目的地址的字段(32位)是在 IP 头部中,作用是通过 IP 协议发送报文给对方主机。
  源端口和目的端口的字段(16位)是在 TCP 头部中,作用是告诉 TCP 协议应该把报文发给哪个进程。

5.1.4 TCP 的最大连接数

  服务器通常固定在某个本地端口上监听,等待客户端的连接请求。因此,客户端 IP 和 端口是可变的,其理论值计算公式如下:

  对 IPv4,客户端的 IP 数最多为 232,客户端的端口数最多为 2^ 16^,也就是服务端单机最大 TCP 连接数,约为 248
  当然,服务端最大并发 TCP 连接数远不能达到理论上限:

首先主要是文件描述符限制,Socket 都是文件,所以首先要通过 ulimit 配置文件描述符的数目;
另一个是内存限制,每个 TCP 连接都要占用一定内存,操作系统的内存是有限的。

5.1.5 UDP 和 TCP的区别

  UDP 不提供复杂的控制机制,利用 IP 提供面向「无连接」的通信服务。
  UDP 协议头部只有 8 个字节( 64 位),UDP 的头部格式如下:

目标和源端口:主要是告诉 UDP 协议应该把报文发给哪个进程。
包长度:该字段保存了 UDP 首部的长度跟数据的长度之和。
校验和:校验和是为了提供可靠的 UDP 首部和数据而设计。

  • 1、TCP 和 UDP 区别
     1)连接。TCP 是面向连接的传输层协议,传输数据前先要建立连接。UDP 是不需要连接,即刻传输数据。
     2)服务对象。TCP 是一对一的两点服务,即一条连接只有两个端点。
    UDP 支持一对一、一对多、多对多的交互通信。
     3)可靠性。TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。UDP 是尽最大努力交付,不保证可靠交付数据。
     4)拥塞控制、流量控制。TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。
     5)首部开销。TCP 首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使用了「选项」字段则会变长的。UDP 首部只有 8 个字节,并且是固定不变的,开销较小。
     6)传输方式。TCP 是流式传输,没有边界,但保证顺序和可靠。UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。
     7)分片不同
      TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
      UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层,但是如果中途丢了一个分片,则就需要重传所有的数据包,这样传输效率非常差,所以通常 UDP 的报文应该小于 MTU。
  • 2、TCP 和 UDP 应用场景
     由于 TCP 是面向连接,能保证数据的可靠性交付,因此经常用于:

FTP 文件传输;
HTTP / HTTPS。

  由于 UDP 面向无连接,它可以随时发送数据,再加上UDP本身的处理既简单又高效,因此经常用于:

包总量较少的通信,如 DNS 、 SNMP 等;
视频、音频等多媒体通信;
广播通信。

  为什么 UDP 头部没有「首部长度」字段,而 TCP 头部有「首部长度」字段呢?原因是 TCP 有可变长的「选项」字段,而 UDP 头部长度则是不会变化的,无需多一个字段去记录UDP 的首部长度。

  • 3、为什么 UDP 头部有「包长度」字段,而 TCP 头部则没有「包长度」字段呢?
      TCP 是如何计算负载数据长度:

     IP 总长度 和 IP 首部长度,在 IP 首部格式是已知的。TCP 首部长度,则是在 TCP 首部格式已知的,所以就可以求得 TCP 数据的长度。
     为了网络设备硬件设计和处理方便,首部长度需要是 4 字节的整数倍。如果去掉 UDP 「包长度」字段,那 UDP 首部长度就不是 4 字节的整数倍了,所以可能是为了补全 UDP 首部长度是 4 字节的整数倍,才补充了「包长度」字段。

5.2 TCP 连接建立

  TCP 是面向连接的协议,所以使用 TCP 前必须先建立连接,而建立连接是通过三次握手来进行的。

  一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN状态:

  客户端会随机初始化序号( client_isn ),将此序号置于 TCP 首部的「序号」字段中,同时把SYN 标志位置为 1 ,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT 状态。

  服务端收到客户端的 SYN 报文后,首先服务端也随机初始化自己的序号( server_isn ),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入 client_isn +1 , 接着把 SYN 和 ACK 标志位置为 1 。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态。

  客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部ACK 标志位置为 1 ,其次「确认应答号」字段填入 server_isn + 1 ,最后把报文发送给服务端,这次报文可以携带客户到服务器的数据,之后客户端处于 ESTABLISHED 状态。服务器收到客户端的应答报文后,也进入 ESTABLISHED 状态。

从上面的过程可以发现第三次握手是可以携带数据的,前两次握手是不可以携带数据的。

  一旦完成三次握手,双方都处于 ESTABLISHED 状态,此时连接就已建立完成,客户端和服务端就可以相互发送数据了。
  TCP 的连接状态查看,在 Linux 可以通过 netstat -napt 命令查看。

5.2.1 为什么是三次握手?不是两次、四次?

  一般来说,因为三次握手才能保证双方具有接收和发送的能力。
   TCP 连接:是用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小。
  所以,重要的是为什么三次握手才可以初始化Socket、序列号和窗口大小并建立 TCP 连接。
  三个方面分析三次握手的原因:

三次握手才可以阻止重复历史连接的初始化(主要原因);
三次握手才可以同步双方的初始序列号;
三次握手才可以避免资源浪费。

  • 1、避免历史连接
     简单来说,三次握手的首要原因是为了防止旧的重复连接初始化造成混乱。

      客户端连续发送多次 SYN 建立连接的报文,在网络拥堵情况下:

 一个「旧 SYN 报文」比「最新的 SYN 」 报文早到达了服务端;
 那么此时服务端就会回一个 SYN + ACK 报文给客户端;
 客户端收到后可以根据自身的上下文,判断这是一个历史连接(序列号过期或超时),那么客户端就会发送 RST 报文给服务端,表示中止这一次连接。

  如果是两次握手连接,就不能判断当前连接是否是历史连接,三次握手则可以在客户端(发送方)准备发送第三次报文时,客户端因有足够的上下文来判断当前连接是否是历史连接:

 如果是历史连接(序列号过期或超时),则第三次握手发送的报文是 RST 报文,以此中止历史连接;
 如果不是历史连接,则第三次发送的报文是 ACK 报文,通信双方就会成功建立连接;

  所以,TCP 使用三次握手建立连接的最主要原因是防止历史连接初始化了连接。

  • 2、同步双方初始序列号
     TCP 协议的通信双方, 都必须维护一个「序列号」, 序列号是可靠传输的一个关键因素,它的作用:

 接收方可以去除重复的数据;
 接收方可以根据数据包的序列号按序接收;
 可以标识发送出去的数据包中, 哪些是已经被对方收到的。

  可见,序列号在 TCP 连接中占据着非常重要的作用,所以当客户端发送携带「初始序列号」的 SYN报文的时候,需要服务端回一个 ACK 应答报文,表示客户端的 SYN 报文已被服务端成功接收,那当服务端发送「初始序列号」给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,才能确保双方的初始序列号能被可靠的同步。

  四次握手其实也能够可靠的同步双方的初始化序号,但由于第二步和第三步可以优化成一步,所以就成了「三次握手」。
  而两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。

  • 3、避免资源浪费
     如果只有「两次握手」,当客户端的 SYN 请求连接在网络中阻塞,客户端没有接收到 ACK 报文,就会重新发送 SYN ,由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的ACK 确认信号,所以每收到一个 SYN 就只能先主动建立一个连接,这会造成什么情况呢?如果客户端的 SYN 阻塞了,重复发送多次 SYN 报文,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。

      即两次握手会造成消息滞留情况下,服务器重复接受无用的连接请求 SYN 报文,而造成重复分配资源。
  • 小节
      TCP 建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号。序列号能够保证数据包不重复、不丢弃和按序传输。
      不使用「两次握手」和「四次握手」的原因:

「两次握手」:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号;
「四次握手」:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。

5.2.2 一些常见问题

  • 1、为什么客户端和服务端的初始序列号 ISN 是不相同的?
      如果一个已经失效的连接被重用了,但是该旧连接的历史报文还残留在网络中,如果序列号相同,那么就无法分辨出该报文是不是历史报文,如果历史报文被新的连接接收了,则会产生数据错乱。
      所以,每次建立连接前重新初始化一个序列号主要是为了通信双方能够根据序号将不属于本连接的报文段丢弃。
      另一方面是为了安全性,防止黑客伪造的相同序列号的 TCP 报文被对方接收。
  • 2、既然 IP 层会分片,为什么 TCP 层还需要 MSS 呢?
      们先来认识下 MTU 和 MSS:

MTU :一个网络包的最大长度,以太网中一般为 1500 字节;
MSS :除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度。

  如果在 TCP 的整个报文(头部 + 数据)交给 IP 层进行分片,会有什么异常呢?当 IP 层有一个超过 MTU 大小的数据(TCP 头部 + TCP 数据)要发送,那么 IP 层就要进行分片,把数据分片成若干片,保证每一个分片都小于 MTU。把一份 IP 数据报进行分片以后,由目标主机的 IP层来进行重新组装后,再交给上一层 TCP 传输层。
  这存在隐患的,那么当如果一个 IP 分片丢失,整个 IP 报文的所有分片都得重传。因为 IP 层本身没有超时重传机制,它由传输层的 TCP 来负责超时和重传。
  当接收方发现 TCP 报文(头部 + 数据)的某一片丢失后,则不会响应 ACK 给对方,那么发送方的TCP 在超时后,就会重发「整个 TCP 报文(头部 + 数据)」。因此,可以得知由 IP 层进行分片传输,是非常没有效率的。
  所以,为了达到最佳的传输效能 TCP 协议在建立连接的时候通常要协商双方的 MSS 值,当 TCP 层发现数据超过 MSS 时,则就先会进行分片,当然由它形成的 IP 包的长度也就不会大于 MTU ,自然也就不用 IP 分片了。

  经过 TCP 层分片后,如果一个 TCP 分片丢失后,进行重发时也是以 MSS 为单位,而不用重传所有的分片,大大增加了重传的效率。

5.2.3 SYN 攻击

  TCP 连接建立是需要三次握手,假设攻击者短时间伪造不同 IP 地址的 SYN 报文,服务端每接收到一个 SYN 报文,就进入 SYN_RCVD 状态,但服务端发送出去的 ACK + SYN 报文,无法得到未知 IP 主机的 ACK 应答,久而久之就会占满服务端的 SYN 接收队列(未连接队列),使得服务器不能为正常用户服务。

  • 1、避免 SYN 攻击方式一
     其中一种解决方式是通过修改 Linux 内核参数,控制队列大小和当队列满时应做什么处理。
     当网卡接收数据包的速度大于内核处理的速度时,会有一个队列保存这些数据包。控制该队列的最大值:net.core.netdev_max_backlog
     SYN_RCVD 状态连接的最大个数:net.ipv4.tcp_max_syn_backlog
     超出处理能时,对新的 SYN 直接回报 RST,丢弃连接:net.ipv4.tcp_abort_on_overflow
  • 2、避免 SYN 攻击方式二
      Linux 内核的 SYN (未完成连接建立)队列与 Accpet (已完成连接建立)队列的工作流程:

     正常流程:

当服务端接收到客户端的 SYN 报文时,会将其加入到内核的「 SYN 队列」;
接着发送 SYN + ACK 给客户端,等待客户端回应 ACK 报文;
服务端接收到 ACK 报文后,从「 SYN 队列」移除放入到「 Accept 队列」;
应用通过调用 accpet() socket 接口,从「 Accept 队列」取出连接。

 应用程序过慢,如果应用程序过慢时,就会导致「 Accept 队列」被占满:

 受到 SYN 攻击:

 如果不断受到 SYN 攻击,就会导致「 SYN 队列」被占满。
 tcp_syncookies 的方式可以应对 SYN 攻击,设置方式为:
net.ipv4.tcp_syncookies = 1。此时的流程:

 此时的流程:

当 「 SYN 队列」满之后,后续服务器收到 SYN 包,不进入「 SYN 队列」;
计算出一个 cookie 值,再以 SYN + ACK 中的「序列号」返回客户端;
服务端接收到客户端的应答报文时,服务器会检查这个 ACK 包的合法性。如果合法,直接放入到「 Accept 队列」;
最后应用通过调用 accpet() socket 接口,从「 Accept 队列」取出的连接。

5.3 TCP 连接断开

  TCP 断开连接是通过四次挥手方式。

  流程:

 客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN报文,之后客户端进入 FIN_WAIT_1 状态。
 服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入 CLOSED_WAIT 状态。
 客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2 状态。
 等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入 LAST_ACK 状态。
 客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,之后进入 TIME_WAIT 状态。
 服务器收到了 ACK 应答报文后,就进入了 CLOSED 状态,至此服务端已经完成连接的关闭。
 客户端在经过 2MSL 一段时间后,自动进入 CLOSED 状态,至此客户端也完成连接的关闭。

  需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。

5.3.1 为什么挥手需要四次

  回顾下四次挥手双方发 FIN 包的过程:

 关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
 服务器收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接。

  ,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK 和 FIN 一般都会分开发送,从而比三次握手导致多了一次。

5.3.2 为什么 TIME_WAIT 等待的时间是 2MSL

  MSL 是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为 TCP 报文基于是 IP 协议的,而 IP 头中有一个 TTL 字段,是 IP 数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报文通知源主机。
  MSL 与 TTL 的区别: MSL 的单位是时间,而 TTL 是经过路由跳数。所以 MSL 应该要大于等于 TTL消耗为 0 的时间,以确保报文已被自然消亡。
  TIME_WAIT 等待 2 倍的 MSL,比较合理的解释是: 网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待 2 倍的时间。

比如如果被动关闭方没有收到断开连接的最后的 ACK 报文,就会触发超时重发 Fin 报文,另一方接收到 FIN 后,会重发 ACK 给被动关闭方, 一来一去正好 2 个 MSL。

  2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时。
  在 Linux 系统里 2MSL 默认是 60 秒,那么一个 MSL 也就是 30 秒。Linux 系统停留在TIME_WAIT 的时间为固定的 60 秒。其定义在 Linux 内核代码里的名称为 TCP_TIMEWAIT_LEN:

#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT
state, about 60 seconds */

  如果要修改 TIME_WAIT 的时间长度,只能修改 Linux 内核代码里 TCP_TIMEWAIT_LEN 的值,并重新编译 Linux 内核。

5.3.3 为什么需要 TIME_WAIT 状态

  主动发起关闭连接的一方,才会有 TIME-WAIT 状态。需要 TIME-WAIT 状态,主要是两个原因:

 防止具有相同「四元组」的「旧」数据包被收到;
 保证「被动关闭连接」的一方能被正确的关闭,即保证最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。

  • 1、防止旧连接的数据包
     假设 TIME-WAIT 没有等待时间或时间过短,被延迟的数据包抵达后会发生什么呢?

 如上图黄色框框服务端在关闭连接之前发送的 SEQ = 301 报文,被网络延迟了。这时有相同端口的 TCP 连接被复用后,被延迟的 SEQ = 301 抵达了客户端,那么客户端是有可能正常接收这个过期的报文,这就会产生数据错乱等严重的问题。

 所以,TCP 就设计出了这么一个机制,经过 2MSL 这个时间,足以让两个方向上的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的。

  • 2、保证连接正确关闭
     TIME-WAIT 作用是等待足够的时间以确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。
     假设 TIME-WAIT 没有等待时间或时间过短,断开连接会造成什么问题呢?

     如上图红色框框客户端四次挥手的最后一个 ACK 报文如果在网络中被丢失了,此时如果客户端TIME-WAIT 过短或没有,则就直接进入了 CLOSED 状态了,那么服务端则会一直处在LASE_ACK 状态。当客户端发起建立连接的 SYN 请求报文后,服务端会发送 RST 报文给客户端,连接建立的过程就会被终止。
     如果 TIME-WAIT 等待足够长的情况就会遇到两种情况:

1)服务端正常收到四次挥手的最后一个 ACK 报文,则服务端正常关闭连接。
2)服务端没有收到四次挥手的最后一个 ACK 报文时,则会重发 FIN 关闭连接报文并等待新的ACK 报文。

 所以客户端在 TIME-WAIT 状态等待 2MSL 时间后,就可以保证双方的连接都可以正常的关闭。

5.3.4 TIME_WAIT 过多有什么危害

  如果服务器有处于 TIME-WAIT 状态的 TCP,则说明是由服务器方主动发起的断开请求。过多的 TIME-WAIT 状态主要的危害有两种:

第一是内存资源占用;
第二是对端口资源的占用,一个 TCP 连接至少消耗一个本地端口。

  第二个危害是会造成严重的后果的,要知道,端口资源也是有限的,一般可以开启的端口为 32768~61000 ,也可以通过如下参数设置指定:net.ipv4.ip_local_port_range
  如果发起连接一方的 TIME_WAIT 状态过多,占满了所有端口资源,则会导致无法创建新连接。

1、客户端受端口资源限制:客户端TIME_WAIT过多,就会导致端口资源被占用,因为端口就65536个,被占满就会导致无法创建新的连接。
2、服务端受系统资源限制:由于一个四元组表示 TCP 连接,理论上服务端可以建立很多连接,服务端确实只监听一个端口 但是会把连接扔给处理线程,所以理论上监听的端口可以继续监听。但是线程池处理不了那么多一直不断的连接了。所以当服务端出现大量 TIME_WAIT 时,系统资源被占满时,会导致处理不过来新的连接。

5.3.5 如何优化 TIME_WAIT

  给出优化 TIME-WAIT 的几个方式,都是有利有弊:

 打开 net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_timestamps 选项;
 net.ipv4.tcp_max_tw_buckets;
 程序中使用 SO_LINGER ,应用强制使用 RST 关闭。

  • 1、net.ipv4.tcp_tw_reuse 和 tcp_timestamps
      设置net.ipv4.tcp_tw_reuse = 1Linux 内核参数开启后,则可以复用处于 TIME_WAIT 的 socket 为新的连接所用。有一点需要注意的是,tcp_tw_reuse 功能只能用客户端(连接发起方),因为开启了该功能,在调用connect() 函数时,内核会随机找一个 time_wait 状态超过 1 秒的连接给新的连接复用。
      使用这个选项,还有一个前提,需要打开对 TCP 时间戳的支持,即
    net.ipv4.tcp_timestamps=1(默认即为 1)
      这个时间戳的字段是在 TCP 头部的「选项」里,用于记录 TCP 发送方的当前时间戳和从对端接收到的最新时间戳。由于引入了时间戳,前面提到的 2MSL 问题就不复存在了,因为重复的数据包会因为时间戳过期被自然丢弃。
  • 2、net.ipv4.tcp_max_tw_buckets
      这个值默认为 18000,当系统中处于 TIME_WAIT 的连接一旦超过这个值时,系统就会将所有的TIME_WAIT 连接状态重置。这个方法过于暴力,而且治标不治本,带来的问题远比解决的问题多,不推荐使用。
  • 3、程序中使用 SO_LINGER
      我们可以通过设置 socket 选项,来设置调用 close 关闭连接行为。
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 0;
setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger,sizeof(so_linger));

  如果 l_onoff 为非 0, 且 l_linger 值为 0,那么调用 close 后,会立该发送一个 RST 标志给对端,该TCP 连接将跳过四次挥手,也就跳过了 TIME_WAIT 状态,直接关闭。但这为跨越 TIME_WAIT 状态提供了一个可能,不过是一个非常危险的行为,不值得提倡。

5.3.6 如果已经建立了连接,但是客户端突然出现故障了怎么办

  TCP 有一个机制是保活机制。这个机制的原理是这样的:

定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个探测报文,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。

  在 Linux 内核可以有对应的参数可以设置保活时间、保活探测的次数、保活探测的时间间隔,以下都为默认值:

net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75
net.ipv4.tcp_keepalive_probes=9

  tcp_keepalive_time=7200:表示保活时间是 7200 秒(2小时),也就 2 小时内如果没有任何连接相关的活动,则会启动保活机制。
  tcp_keepalive_intvl=75:表示每次检测间隔 75 秒。
  tcp_keepalive_probes=9:表示检测 9 次无响应,认为对方是不可达的,从而中断本次的连接。
  也就是说在 Linux 系统中,最少需要经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接。

  这个时间是有点长的,我们也可以根据实际的需求,对以上的保活相关的参数进行设置。如果开启了 TCP 保活,需要考虑以下几种情况:

  1. 第一种,对端程序是正常工作的。当 TCP 保活的探测报文发送给对端, 对端会正常响应,这样 TCP 保活时间会被重置,等待下一个 TCP 保活时间的到来。
  2. 第二种,对端程序崩溃并重启。当 TCP 保活的探测报文发送给对端后,对端是可以响应的,但由于没有该连接的有效信息,会产生一个 RST 报文,这样很快就会发现 TCP 连接已经被重置。
  3. 第三种,是对端程序崩溃,或对端由于其他原因导致报文不可达。当 TCP 保活的探测报文发送给对端后,石沉大海,没有响应,连续几次,达到保活探测次数后,TCP 会报告该 TCP 连接已经死亡。

5.4 Socket 编程


  文字描述:

 服务端和客户端初始化 socket ,得到文件描述符;
 服务端调用 bind ,将绑定在 IP 地址和端口;
 服务端调用 listen ,进行监听;
 服务端调用 accept ,等待客户端连接;
 客户端调用 connect ,向服务器端的地址和端口发起连接请求;
 服务端 accept 返回用于传输的 socket 的文件描述符;
 客户端调用 write 写入数据;服务端调用 read 读取数据;
 客户端断开连接时,会调用 close ,那么服务端 read 读取数据的时候,就会读取到了EOF ,待处理完数据后,服务端调用 close ,表示连接关闭。

  需要注意的是,服务端调用 accept 时,连接成功了会返回一个已完成连接的 socket,后续用来传输数据。所以,监听的 socket 和真正用来传送数据的 socket,是「两个」 socket,一个叫作监听 socket,一个叫作已完成连接 socket。
  成功连接建立之后,双方开始通过 read 和 write 函数来读写数据,就像往一个文件流里面写东西一样。

5.4.1 listen 时候参数 backlog 的意义

  Linux内核中会维护两个队列:

未完成连接队列(SYN 队列):接收到一个 SYN 建立连接请求,处于 SYN_RCVD 状态;
已完成连接队列(Accpet 队列):已完成 TCP 三次握手过程,处于 ESTABLISHED 状态。

int listen (int socketfd, int backlog)

  socketfd 为 socketfd 文件描述符;
  backlog,这参数在历史版本有一定的变化。在早期 Linux 内核 backlog 是 SYN 队列大小,也就是未完成的队列大小。在 Linux 内核 2.2 之后,backlog 变成 accept 队列,也就是已完成连接建立的队列长度,所以现在通常
认为 backlog 是 accept 队列。但是上限值是内核参数 somaxconn 的大小,也就说 accpet 队列长度 = min(backlog,somaxconn)。

5.4.2 accept 发生在三次握手的哪一步

  先看看客户端连接服务端时,发送了什么?

  1. 客户端的协议栈向服务器端发送了 SYN 包,并告诉服务器端当前发送序列号 client_isn,客户端进入 SYN_SENT 状态;
  2. 服务器端的协议栈收到这个包之后,和客户端进行 ACK 应答,应答的值为 client_isn+1,表示对SYN 包 client_isn 的确认,同时服务器也发送一个 SYN 包,告诉客户端当前我的发送序列号为server_isn,服务器端进入 SYN_RCVD 状态;
  3. 客户端协议栈收到 ACK 之后,使得应用程序从 connect 调用返回,表示客户端到服务器端的单向连接建立成功,客户端的状态为 ESTABLISHED,同时客户端协议栈也会对服务器端的 SYN 包进行应答,应答数据为 server_isn+1;
  4. 应答包到达服务器端后,服务器端协议栈使得 accept 阻塞调用返回,这个时候服务器端到客户端的单向连接也建立成功,服务器端也进入 ESTABLISHED 状态。

  们可以得知客户端 connect 成功返回是在第二次握手,服务端 accept 成功返回是在三次握手成功之后。

5.4.3 客户端调用 close 了,连接是断开的流程是什么

  1. 客户端调用 close ,表明客户端没有数据需要发送了,则此时会向服务端发送 FIN 报文,进入FIN_WAIT_1 状态;
  2. 服务端接收到了 FIN 报文,TCP 协议栈会为 FIN 包插入一个文件结束符 EOF 到接收缓冲区中,应用程序可以通过 read 调用来感知这个 FIN 包。这个 EOF 会被放在已排队等候的其他已接收的数据之后,这就意味着服务端需要处理这种异常情况,因为 EOF 表示在该连接上再无额外数据到达。此时,服务端进入 CLOSE_WAIT 状态;
  3. 接着,当处理完数据后,自然就会读到 EOF ,于是也调用 close 关闭它的套接字,这会使得客户端会发出一个 FIN 包,之后处于 LAST_ACK 状态;
  4. 客户端接收到服务端的 FIN 包,并发送 ACK 确认包给服务端,此时客户端将进入 TIME_WAIT 状态;
  5. 服务端收到 ACK 确认包后,就进入了最后的 CLOSE 状态;
  6. 客户端经过 2MSL 时间之后,也进入 CLOSE 状态。

转载:https://blog.csdn.net/m0_37741420/article/details/115693357
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场