You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

447 lines
30 KiB
Markdown

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 01 | 网络模型和工具:网络为什么要分层?
你好,我是胜辉。
今天是咱们的第一节正课,就像我在开篇词里介绍的,在预习篇这里,我们的目标是搞清楚网络分层的概念,还有初步学习抓包分析。所以接下来,我会先从一些基础的网络知识说起,为你重点讲解网络分层模型以及各层之间的区别和联系。
因为咱们是以案例实战为导向的课程,所以我除了会在网络的每一层,给你介绍相关的技术细节以外,还会带你认识相应的排查工具。学完这节课,哪怕你原本是网络方面的小白,你也可以在网络排查方面“一试身手”了,是不是有点期待了呢?好,让我们开始吧。
## 网络是七层、五层还是四层?
学习网络排查,可能首先要搞清楚的,就是网络的分层模型了。工作中,我们也时常会听到这些术语,比如三层交换机、七层规则等等。网络分层的概念,可谓深入人心。
可是你有没有想过网络为什么要分层呢难道是非分不可吗回答这个问题之前我们先做个有趣的假设这会儿是在网络诞生的前夜什么IP协议、TCP协议都还不存在而你是网络的缔造者面临设计网络这个伟大的任务。面对这么好的机会你会选择做怎样的设计呢
你大体上有这么两种选择:
* **应用程序包办一切。**程序把应用层的数据,按某种编码转化为二进制数据,然后程序去操控网卡,把二进制数据发送到网络上。这期间,通信的连接方式、传输的可靠性、速度和效率的保证等等,都需要这个程序去实现。然后下次开发另外一个应用的时候,就把上面这些活,再干一遍。
* **应用程序、操作系统、网络设备等环节各自分工。**应用程序只负责实现应用层的业务逻辑,操作系统负责连接的建立、处理网络拥塞和丢包乱序、优化网络读写速度等等,然后把数据交给网卡,后者和交换机等设备做好联动,负责二进制数据在物理线路上的传送和接收。
那么显然,第一种大包大揽的方式,实现难度太大、耦合度太高,怎么看都是一个“反面典型”。所以,我们应该选择第二种,也就是分层的方式去实现。
你有没有发现,其实这个思路,跟编程的思想是类似的。在编程中,我们需要把一些逻辑抽象为函数或者对象,以实现更好的解耦和复用。在网络世界里也是如此,每一层干好自己的分内事,那么所有的层次配合起来工作的时候,就显得有条不紊了。
说到具体的分层模型,你应该会想到两种比较有名的方案。对,它们就是**OSI的七层模型**,和**TCP/IP的四层/五层模型**。这两种模型的最大区别,就是前者在传输层和应用层之间,还有会话层和表示层,而后者没有。
我们来看一下示意图:
![图片](https://static001.geekbang.org/resource/image/83/73/83bde3e930ea80c4cf1a3ae30868f973.jpg?wh=1920x1080)
那在这里,你可能还会想:这两种模型哪种用得最多,或者说,哪种更合理呢?
其实我觉得倒不用过于纠结在“谁比谁更好”这个点上,如果我们理解了每一层的作用,那么就不会被表象上的层级所束缚了。事实上,两种分法都有可取之处。
一般来说七层模型在我们工作当中谈论得更多些。比如我的同事会找过来说“你帮我建一个七层规则吧”。这里的七层就是指应用层他说的“七层规则”呢可能是HTTP路由规则比如把符合某种条件的HTTP请求分流到某个特定的后端集群。
还有一些场景也是比较适合用七层模型来解释的。比如TLS虽然在TCP之上按TCP/IP模型就要被归入应用层。但事实上在HTTPS的场景下HTTP协议就是运行在TLS协议之上的那么是不是把HTTP和TLS分到不同的层次更合适呢正好在七层模型里第五层和第六层可以分别代表TLS的会话保持功能和数据加解密这种表示层的功能。
![图片](https://static001.geekbang.org/resource/image/e9/d5/e9f4b3258ec7c621b780db18be32d7d5.jpg?wh=1920x1080)
不过会话层和表示层的协议确实比较少。从控制模型复杂度的角度来看如果把这两层都合并到应用层那么模型倒是比较简单也适合入门学习的。所以从这一点上看TCP/IP模型也有可取之处。
这里你可能稍有疑问为什么TCP/IP还有四层和五层模型这两种说法呢其实五层模型就是OSI的前四层加上一个应用层。这样的话这个五层模型跟OSI七层模型差异就比四层模型又缩小了一点。
所以,你现在应该明白了,**两种分层模型的最大差异,其实还是在会话层和表示层上面。**第一到第四层,已经基本统一了。而它们的最高层,虽然一个叫第七层,一个叫第四层或者第五层,表面上虽然并不一致,但实际上都可以用“应用层”来代替。这样既避免了可能的误解,也更加准确地表示了这一层的具体用途。
## 什么是TCP流
在一些技术文档特别是Wireshark相关的文档中“TCP流”是一个很常见的词汇。它是什么意思呢为什么叫“流”难道跟水有关吗
其实这里的TCP流就是英文的TCP Stream。Stream这个词有“流”的意思也有“连续的事件”这样一个含义所以它是有前后、有顺序的这也正对应了TCP的特性。
跟Stream相对的一个词是Datagram它是指没有前后关系的数据单元比如UDP和IP都属于Datagram。在Linux网络编程里面TCP对应的socket类型是SOCK\_STREAM而UDP对应的就是SOCK\_DGRAM了。显然DGRAM就是Datagram的简写。
在具体的网络报文层面一个TCP流对应的就是一个五元组**传输协议类型、源IP、源端口、目的IP、目的端口**。比如今天你访问了极客时间网站那么你这次的TCP流就可能就是这样一个五元组
```java
(TCP, your_ip, your_port, geekbang_ip, 443)
```
一个IP报文包含了所有这五个元素所以Wireshark在解析抓包文件时自然就能通过五元组知道每个报文所属的TCP流了。这也是为什么我们可以在Wireshark里用Follow TCP Stream的方法找到报文所在的TCP流。
不过有时候也会有四元组的说法。其实它跟五元组大体上是一致的只是四元组没有区分传输层协议类型TCP或者UDP。但是如果我们都清楚地知道应用类型比如知道应用是HTTP协议的那它的传输层协议默认就是TCP这一元是否算在里面已经不重要了。
## 报文、帧、分组、段、数据包,这些术语是同一个东西吗?
**报文packet**是一种相对宽泛和通用的说法基本上每一层都可以用。比如在应用层你可以说“HTTP报文”在传输层你可以说“TCP报文”同样的在网络层当然就是“IP报文”了。事实上网络层也是“报文”一词被使用最多的场景了。**数据包**也是类似的,可以在很多场景下通用。
我们再稍微考究一下语法。packet这个词的后缀是et。而在英文中以et结尾的很多词表示某一个小小的东西。比如功能完备的一小段代码叫code snippet一小段内嵌在HTML中的Java前端代码叫applet。自然的packet就是一个小的pack包裹
然而,另外几个术语在用的时候,就需要讲究一点了,因为它们并不是通用词,而是特定层的专有词汇。
**frame**是二层也就是数据链路层的概念代表了二层报文它包含帧头、载荷、帧尾。注意帧是有尾部的而其他像IP、TCP、HTTP等层级的报文都没有尾部。我们不可以说“TCP帧”或者“IP帧”虽然也许对方也明白你的意思但我们都想做得专业一点不是嘛。这里还有个小知识点HTTP/2实现了多路复用其中也有帧的概念不过那个帧跟这里网络二层的帧除了名称相同以外就没有别的联系了。
**分组**是IP层报文也就是狭义的packet。
**段特指TCP segment**也就是TCP报文。既然segment是“部分”的意思那这个“整体”又是什么呢它就是在应用层交付给传输层的消息message。当message被交付给传输层时如果这个message的原始尺寸超出了传输层数据单元的限制比如超出了TCP的MSS它就会被划分为多个segment。这个过程就是**分段**segmentation也是TCP层的一个很重要的职责。
说到segmentation你可能也会想到fragmentation分片。这俩是同一个东西吗这方面的知识点也不少我在这里就不具体展开了。不过别着急我会在第8讲里帮你把这两个东西梳理清楚。
另外这里还要提一下Datagram的中文叫**“数据报”**但不是“数据包”。读音类似但意思并不完全相同。前面说过“数据包”是一个通用词所以用“UDP数据包”指代“UDP数据报”并没有问题。但反过来非UDP协议的数据包比如TCP段就不能叫“TCP数据报”了因为TCP不是Datagram。
最后,你可以再来看下这张层级和术语对应关系的示意图:
![图片](https://static001.geekbang.org/resource/image/21/06/210167875fb87016a6c4a52fbafc0006.jpg?wh=1920x1080)
## 网络各层都有哪些排查工具呢?
通过上面的内容,你应该对于网络为什么要做分层、为什么那样做分层,已经有了比较清晰地认识了,我也带你探讨了每个层级的名词概念。所谓“名不正则言不顺”,咱们把这些术语搞清楚了,是不是感觉自己的技术“格调”也有那么点提升了呢?
接下来,我们进入干货部分,也就是每个层级的排查工具,用大白话说就是:“这可是我们吃饭的家伙儿”。
### 应用层
应用层的排查工具就太多了,相信做应用的同学,对自己的应用排查,应该是比我要更加熟悉。那我这里呢,就选一个主要的应用来展开吧,我们来谈谈 **HTTP应用的排查工具**
现在主流的浏览器是Google的Chrome它本身就**内置了一个开发者工具**。在Chrome界面里按下F12或者你是苹果系统的话还可以按下组合键option + command + I启动开发者工具。
其实在其他的浏览器上,都有类似这样的工具,比如**Firefox和Edge**。而且因为Edge基于Chromium浏览器内核它的开发者工具跟Chrome的开发者工具很相似。
在更老的IE浏览器时代并没有原生的开发者工具。当时有一个叫**HttpWatch**的工具可以在IE上实现类似的功能但需要另外安装。
借助开发者工具,我们可以非常方便地做很多事,比如以下这些。
* **找到有问题的服务端IP**
比如有用户报告死活访问不了你的网站但是你很清楚这个网站的域名对应了很多IP地址你怎么知道用户连的是哪个IP呢
你可以这样做让客户启用开发者工具在Network页找到主页对象在它的Headers部分就能看到Remote address这里的IP就是当前连接的IP比如下面这样
![](https://static001.geekbang.org/resource/image/96/49/969a3674269b60593e83f623c310c749.jpg?wh=2572x1072)
不过有句成语叫“刻舟求剑”因为DNS解析的关系你很可能下次重连就不是这个IP了所以每次都应该重新确认一下这个信息。
这个技巧,在**排查公网的访问问题**的时候特别有用。要知道现在流量大一点的网站都已经上了CDN那就必然在全国乃至全球各地有少则数十个、多则数百个CDN终端节点在给访问者提供就近的服务。如果有人说他访问不了某个站点了那么请一定让他用开发者工具找到他连的远程IP然后你再根据这个信息展开排查工作。
* **辅助排查网页慢的问题**
访问页面感觉很慢,那么可以借助开发者工具的**时间统计功能**找到耗时较高的HTTP资源对象再针对性排查。比如我觉得访问[https://github.com](https://github.com)很慢那么可以先打开开发者工具然后访问站点等全部加载完成后到Network页查看这些HTTP对象的加载时间。
![](https://static001.geekbang.org/resource/image/52/ba/529e907d52d11c95d26dd8a8681428ba.jpg?wh=2668x1074)
不过,这个办法只能排查到是哪个资源对象耗时比较长,但更进一步的排查,比如“为什么这个对象的加载时间比别的对象长”这个问题,开发者工具就难以回答了。关于这个问题,我会在后续的课程里深入展开,我们会用到抓包分析这把“手术刀”,来根本性地排查这类问题。
* **解决失效Cookie带来的问题**
有时候我们的Cookie过期了导致无法正常登录站点那么可以打开开发者工具到Application页找到Storage -> Cookie把对应的条目清除。这样下次你再访问这个站点就已经“洗心革面”了。对站点来说你就是一次新的访问可以生成一次新的Cookie了。
当然,你通过删除浏览器缓存的方式,也是可以做到这一点的。但开发者工具的优点是,可以**细粒度**到这个网站级别而删除缓存的方式删除的就是所有站点的Cookie了这未必是你想要的。
### 表示层和会话层
在前面的网络分层部分我提到过其实表示层和会话层的协议并不多TLS可以归入这两个层级。为了对TLS的问题进行排查我推荐你两种工具。
**第一种,还是基于浏览器做初步的检查,主要是围绕证书本身做检查。**在浏览器的地址栏那里有一个按钮点开后就可以查看TLS证书等信息
![图片](https://static001.geekbang.org/resource/image/ff/3c/ff2324fa28934951c39b6e65b8d5833c.jpg?wh=593x408)
在上面的菜单中继续点开Connection is secure按钮进而点击Certificate is valid按钮就能查看证书了。
另外使用开发者工具的Security菜单还可以查看更为详细的TLS信息包括协议版本、密钥交换算法、证书有效期等等。
![图片](https://static001.geekbang.org/resource/image/59/88/59c05ec8a3c287036d4b286f749eb188.jpg?wh=779x676)
**第二种关于TLS握手、密钥交换、密文传输等方面的排查还是需要用tcpdump和Wireshark来做。**在Wireshark中可以更加全面地查看TLS细节。
比如我们可以直接看到TLS握手阶段里双方协商**过程中**各自展示的Cipher suite而在开发者工具里我们只能看到协商**完成后**的选择。
![图片](https://static001.geekbang.org/resource/image/a4/b3/a4153e0a5c4be0e520ab00bb44b0fab3.jpg?wh=671x611)
### 传输层
传输层毫无疑问是重中之重,工具也很多。我们就按排查场景来介绍工具。
* **路径可达性测试**
如果我们要测试TCP握手我们有**telnet、nc**这两个常规工具。比如telnet
```plain
$ telnet www.baidu.com 443
Trying 180.101.49.12...
Connected to www.a.shifen.com.
Escape character is '^]'.
```
用nc呢可以这样
```plain
$ nc -w 2 -zv www.baidu.com 443
Connection to www.baidu.com 443 port [tcp/https] succeeded!
```
* **查看当前连接状况**
**netstat** 命令是一个经典命令了很多同学都会使用它来获取当前的TCP、UDP等的连接信息比如
```plain
$ netstat -ant
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN
tcp        0    280 10.0.2.15:22            10.0.2.2:56669          ESTABLISHED
tcp6       0      0 :::22                   :::*                    LISTEN
```
* **查看当前连接的传输速率**
有时候,你的网络跑得挺繁忙的,但你却不知道哪个连接占用了大量的带宽?你可以用 **iftop**。这个工具不是系统默认自带的需要你安装一下然后执行iftop就好了。对了你需要有sudo权限也就是执行sudo iftop然后就能看到不同连接的传输速率把祸害你带宽的连接给找到。比如下面这样
![图片](https://static001.geekbang.org/resource/image/36/a2/367d7286ecc1bf97c6f8bcd5709df1a2.jpg?wh=927x227)
* **查看丢包和乱序等的统计**
其实用netstat除了可以获取实时连接状况还可以获取历史统计信息。比如你怀疑一台机器的网络很不稳定除了用ping做简单的测试你还可以用 **netstat -s** 来获取更加详细的统计信息。比如其中的TCP丢包和乱序计数值就能帮助你判断传输层的状况。下面是我截取了一次netstat -s命令的输出
```plain
$ netstat -s
......
Tcp:
    16 active connection openings
    1 passive connection openings
    8 failed connection attempts
    1 connection resets received
    1 connections established
    6254 segments received
    4035 segments sent out
    1 segments retransmitted
    0 bad segments received
    3 resets sent
......
TcpExt:
    1 ICMP packets dropped because socket was locked
    3 TCP sockets finished time wait in fast timer
    8 delayed acks sent
    4674 packet headers predicted
    10 acknowledgments not containing data payload received
    1008 predicted acknowledgments
    TCPTimeouts: 1
    TCPBacklogCoalesce: 140
    1 connections reset due to early user close
    TCPRcvCoalesce: 2187
    TCPAutoCorking: 110
    TCPSynRetrans: 1
    TCPOrigDataSent: 1041
    TCPDelivered: 1049
```
你可能会问:这些不是静态值吗,我想知道当前情况啊?这个也很好解决,你可以这样做:
```plain
watch --diff netstat -s
```
这个命令会把发生变化的数值进行高亮,方便我们查看:
![](https://static001.geekbang.org/resource/image/1c/be/1c67a7092ac84aff78360fyy9af7cabe.jpg?wh=606x418)
当然上面这个算运维“青铜”版。你也可以写一个简单的脚本在两次netstat -s命令之间执行sleep然后计算两个读数之间的差值并除以sleep的时间得到大致的变化速度。这样就又升级了一点。
如果你想做得再到位一点你可以把netstat -s的输出值写入到TSDB然后用Grafana之类的Dashboard展示这样不仅有视图也有历史值可以算运维“王者”了。
* **还有ss**
**ss** 命令是Iproute2包里的命令也是netstat的“取代者”。它提供了对socket的丰富的统计信息。比如下面这条命令我也经常用可以查看到当前连接的统计信息
```plain
$ ss -s
Total: 164
TCP:   5 (estab 1, closed 0, orphaned 0, timewait 0)
Transport Total     IP        IPv6
RAW   1         0         1
UDP   2         2         0
TCP   5         4         1
INET   8         6         2
FRAG   0         0         0
```
当然也不能完全说“ss等于netstat”因为事实上netstat命令的功能被拆分到了ss和ip这两个命令里并分别得到了丰富和加强。具体的细节我们在课程中还会陆续提到。
### 网络层
在这一层除了可以直接用ping这个非常简便的工具以外你还应该掌握另外两个命令它们能提供更为强大的排查能力它们就是**traceroute和mtr**。
* **查看网络路径状况**
下面这个是我用自己的Mac笔记本做一个简单的traceroute的典型输出
```plain
$ traceroute  www.baidu.com
traceroute to www.a.shifen.com (180.101.49.12), 64 hops max
  1   10.0.2.2  0.133ms  0.131ms  0.087ms
  2   192.168.1.1  3.048ms  1.466ms  1.574ms
  3   100.65.0.1  8.975ms  3.067ms  6.472ms
  4   61.152.53.149  5.644ms  3.691ms  4.624ms
  5   61.152.24.226  5.357ms  4.393ms  4.244ms
  6   202.97.29.122  10.171ms  10.403ms  8.755ms
  7   58.213.94.118  10.707ms  11.880ms  11.441ms
  8   58.213.94.90  9.644ms  *  *
  9   58.213.96.110  12.758ms  12.095ms  11.842ms
 10   *  *  *
 11   *  *  *
 12   *  *  *
 13   *  *  *
 14   *  *  *
 15   *  *  *
 16   *  *  *
 17   *  *  *
 18   *  *  *
 19   *  *  *
 20   *  *  *
```
等等为什么从第10跳开始就没有IP只有星号了你是不是也遇到过这种情况呢其实你稍微改一下命令也就是加上**\-I**参数I代表ICMP就可以正常跑到底了
```plain
$ traceroute  www.baidu.com -I
traceroute to www.a.shifen.com (180.101.49.12), 64 hops max
  1   10.0.2.2  0.099ms  2.363ms  0.078ms
  2   192.168.1.1  3.320ms  1.220ms  1.204ms
  3   100.65.0.1  8.737ms  4.872ms  6.403ms
  4   61.152.54.125  5.035ms  3.397ms  4.288ms
  5   *  61.152.25.110  4.176ms  *
  6   202.97.101.30  7.447ms  6.399ms  5.936ms
  7   58.213.95.110  10.488ms  *  9.014ms
  8   *  58.213.95.134  11.064ms  *
  9   58.213.96.74  10.997ms  10.042ms  10.592ms
 10   *  *  *
 11   *  *  *
 12   *  *  *
 13   180.101.49.12  11.269ms  9.518ms  8.779ms
```
背后的原理就是traceroute默认是用UDP作为探测协议的但是很多网络设备并不会对UDP作出回应。所以我们改成ICMP协议做探测后网络设备就有回应了。其实Windows上的tracert就是默认用ICMP这一点跟Linux正好是反过来的。两个操作系统真是“相爱相杀”啊。
但是traceroute也有一个明显的不足**它不能对这个路径做连续多次的探测**。
于是mtr出现了它可以说是traceroute的超集除了traceroute的功能还能实现丰富的探测报告。尤其是它对每一跳的丢包率的百分比是用来定位路径中节点问题的重要指标。所以当你在遇到**“连接状况时好时坏的问题”**的时候单纯用一次性的traceroute恐怕难以看清楚那就可以用mtr来获取更加全面和动态的链路状态信息了。
```plain
$ mtr www.baidu.com -r -c 10
Start: 2022-01-07T04:05:02+0000
HOST: victorebpf                  Loss%   Snt   Last   Avg  Best  Wrst StDev
  1.|-- _gateway                   0.0%    10    0.3   0.4   0.2   1.2   0.3
  2.|-- 192.168.1.1                0.0%    10    1.6   1.8   1.4   3.2   0.5
  3.|-- 100.65.0.1                 0.0%    10    3.8   7.0   3.8  10.3   2.0
  4.|-- 61.152.54.125              0.0%    10    4.0   4.3   3.6   5.1   0.5
  5.|-- 61.152.25.110             30.0%    10    5.0   6.8   4.4  18.9   5.4
  6.|-- 202.97.101.30             20.0%    10    7.8   6.6   5.4   7.8   0.8
  7.|-- 58.213.95.110             80.0%    10   10.0   9.8   9.6  10.0   0.3
  8.|-- ???                       100.0    10    0.0   0.0   0.0   0.0   0.0
  9.|-- 58.213.96.74               0.0%    10   10.5  12.7   9.9  24.7   4.9
 10.|-- ???                       100.0    10    0.0   0.0   0.0   0.0   0.0
 11.|-- ???                       100.0    10    0.0   0.0   0.0   0.0   0.0
 12.|-- ???                       100.0    10    0.0   0.0   0.0   0.0   0.0
 13.|-- 180.101.49.12              0.0%    10    9.4   9.1   8.3   9.7   0.5
```
* **查看路由**
命令 **route** 可以查看路由表,不过这个命令比较老一点:
```plain
# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.2.2        0.0.0.0         UG    100    0        0 enp0s3
10.0.2.0        0.0.0.0         255.255.255.0   U     0      0        0 enp0s3
10.0.2.2        0.0.0.0         255.255.255.255 UH    100    0        0 enp0s3
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
```
传输层工具里介绍的 **netstat**,其实也能帮我们查看路由,只要加上 **\-r** 参数:
```plain
$ netstat -r
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
default         _gateway        0.0.0.0         UG        0 0          0 enp0s3
10.0.2.0        0.0.0.0         255.255.255.0   U         0 0          0 enp0s3
_gateway        0.0.0.0         255.255.255.255 UH        0 0          0 enp0s3
172.17.0.0      0.0.0.0         255.255.0.0     U         0 0          0 docker0
```
我前面说过netstat是被ss和ip这两个命令替代了。所以我们同样可以用 **ip命令**查看路由。比如这样:
```plain
$ ip route
default via 10.0.2.2 dev enp0s3 proto dhcp src 10.0.2.15 metric 100
10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15
10.0.2.2 dev enp0s3 proto dhcp scope link src 10.0.2.15 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
```
### 数据链路层和物理层
这一层离应用层已经很远了一般来说是专职的网络团队在负责。如果这一层有问题就会直接体现在网络层表现上面比如IP会有丢包和延迟等现象然后会引发传输层异常如丢包、乱序、重传等。所以**一个稳定的数据链路层乃至物理层,是网络可靠性的基石。**
你可能会奇怪既然底下这两层的稳定性如此重要那上层的TCP不是号称还有传输可靠性的保障吗难道这种保障形同虚设
其实这两点并不矛盾。TCP的传输可靠性是通过序列号、确认号、重传机制等来保证的通过这种机制TCP可以在**一定程度**的网络不稳定场景下依然保证传输可靠但不等于TCP可以无限容忍底层的不稳定因为各种TCP拥塞控制算法都会因为这种问题而极大地降低传输性能。
如果你想查看这两层的状况,可以用 **ethtool** 这个工具。比如这样:
```plain
# ethtool -S enp0s3
NIC statistics:
     rx_packets: 45897
     tx_packets: 9457
     rx_bytes: 59125524
     tx_bytes: 834625
     rx_broadcast: 0
     tx_broadcast: 17
     rx_multicast: 0
     tx_multicast: 59
     rx_errors: 0
     tx_errors: 0
     tx_dropped: 0
```
它的原理是网卡驱动会到内核中注册ethtool回调函数然后我们用ethtool命令就可以查看这些信息了。由于信息是由网卡驱动提供的所以十分“接地气”。
如果你在传输层和网络层的排查工具上,已经看到明确的链路不稳定的信息,那就直接找网络团队去处理吧。
## 小结
这节课我们回顾了网络分层模型也了解了OSI模型和TCP/IP模型的区别和联系。通过“抠字眼”的方式我们把每层的术语搞清楚由此对分层模型有了更加深入的理解这个对我们开展网络排查工作有很强的指导性意义。
然后,我们逐一学习了各层的常用排查工具。我来给你再梳理一下:
1. 应用层以HTTP为例可以用**浏览器开发者工具**实现远程IP识别、耗时分析、Cookie删除等需求。
2. 会话层和表示层以TLS为主我们还是用**浏览器开发者工具**可以查看证书细节、协商后使用的Cipher suite等信息属于静态信息。然后学习了**用tcpdump和Wireshark** 查看更详细的TLS握手细节的方法。这些信息是动态的也只有用抓包分析的手段才能做到。
3. 在传输层,我们学到了 **telnet、nc、netstat、ss** 等命令,通过它们,我们可以测试连通性,也可以获取连接状况和统计信息,对于传输问题的排查都很有帮助。
4. 在网络层及以下的部分,我们学习了 **traceroute、mtr、ip** 等工具,可以检测网络路径状况。
5. 在数据链路层和物理层,我们可以做得不多,主要依靠网络层观察到的链路质量来推断这两次的情况。当然,也可以用 **ethtool** 这个工具查看这两层的详情。
最后,为了方便你复习,我也给你画了一张思维导图,让你能一目了然:
![](https://static001.geekbang.org/resource/image/17/d2/179d2da5c5cc67a9b0f07af2cbc668d2.jpg?wh=1630x1107)
如果对这些命令的更多细节或者原理很感兴趣,在实战三模块里,我也会专门讨论这些工具相关的案例和使用技巧,相信会让你的网络排查技能变得更加丰富多元。
## 思考题
感谢你认真学完了这节课的内容,不过在结束之前,给你留几道思考题:
1. traceroute默认是用UDP来做探测的那这个又是基于什么原理呢通和不通我们会收到怎样的回复
2. 有时候运行telnet后命令就挂起没有响应了这说明了什么问题呢
欢迎你把答案写到留言区,我们一起交流讨论。也欢迎你把今天的内容分享给更多的朋友,一同成长和进步。