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.

274 lines
14 KiB
Markdown

2 years ago
# 08 | 工欲善其事必先利其器:学会使用各种工具
你好我是盛延敏这里是网络编程实战第8讲欢迎回来。
上一讲我们讲到了本地套接字加上前面介绍的TCP、UDP套接字你会发现我们已经比较全面地接触了套接字。
其实在平常使用套接字开发和测试过程中我们总会碰到这样或那样的问题。学会对这些问题进行诊断和分析其实需要不断地积累经验。而Linux平台下提供的各种网络工具则为我们进行诊断分析提供了很好的帮助。在这一讲里我将会选择几个重点的工具逐一介绍。
## 必备工具: ping
这个命令我想大家都不陌生“ping”这个命名来自于声呐探测在网络上用来完成对网络连通性的探测这个命名可以说是恰如其分了。
```
$ ping www.sina.com.cn
PING www.sina.com.cn (202.102.94.124) 56(84) bytes of data.
64 bytes from www.sina.com.cn (202.102.94.124): icmp_seq=1 ttl=63 time=8.64 ms
64 bytes from www.sina.com.cn (202.102.94.124): icmp_seq=2 ttl=63 time=11.3 ms
64 bytes from www.sina.com.cn (202.102.94.124): icmp_seq=3 ttl=63 time=8.66 ms
64 bytes from www.sina.com.cn (202.102.94.124): icmp_seq=4 ttl=63 time=13.7 ms
64 bytes from www.sina.com.cn (202.102.94.124): icmp_seq=5 ttl=63 time=8.22 ms
64 bytes from www.sina.com.cn (202.102.94.124): icmp_seq=6 ttl=63 time=7.99 ms
^C
--- www.sina.com.cn ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 5006ms
rtt min/avg/max/mdev = 7.997/9.782/13.795/2.112 ms
```
在上面的例子中我使用ping命令探测了和新浪网的网络连通性。可以看到每次显示是按照sequence序列号排序显示的一并显示的也包括TTLtime to live反映了两个IP地址之间传输的时间。最后还显示了ping命令的统计信息如最小时间、平均时间等。
我们需要经常和Linux下的ping命令打交道那么ping命令的原理到底是什么呢它是基于TCP还是UDP开发的
都不是。
其实ping是基于一种叫做ICMP的协议开发的ICMP又是一种基于IP协议的控制协议翻译为网际控制协议其报文格式如下图
![](https://static001.geekbang.org/resource/image/15/38/1555df944c00bdba5c2a4ea3c55cf338.png)
ICMP在IP报文后加入了新的内容这些内容包括
* 类型即ICMP的类型, 其中ping的请求类型为8应答为0。
* 代码进一步划分ICMP的类型, 用来查找产生错误的原因。
* 校验和:用于检查错误的数据。
* 标识符通过标识符来确认是谁发送的控制协议可以是进程ID。
* 序列号唯一确定的一个报文前面ping名字执行后显示的icmp\_seq就是这个值。
当我们发起ping命令时ping程序实际上会组装成如图的一个IP报文。报文的目的地址为ping的目标地址源地址就是发送ping命令时的主机地址同时按照ICMP报文格式填上数据在可选数据上可以填上发送时的时间戳。
IP报文通过ARP协议源地址和目的地址被翻译成MAC地址经过数据链路层后报文被传输出去。当报文到达目的地址之后目的地址所在的主机也按照ICMP协议进行应答。之所以叫做协议是因为双方都会遵守这个报文格式并且也会按照格式进行发送-应答。
应答数据到达源地址之后ping命令可以通过再次解析ICMP报文对比序列号计算时间戳等来完成每个发送-应答的显示,最终显示的格式就像前面的例子中展示的一样。
可以说ICMP协议为我们侦测网络问题提供了非常好的支持。另外一种对路由的检测命令Traceroute也是通过ICMP协议来完成的这里就不展开讲了。
## 基本命令: ifconfig
很多熟悉Windows的同学都知道Windows有一个ipconfig命令用来显示当前的网络设备列表。事实上Linux有一个对应的命令叫做ifconfig也用来显示当前系统中的所有网络设备通俗一点的说就是网卡列表。
```
vagrant@ubuntu-xenial-01:~$ ifconfig
cni0 Link encap:Ethernet HWaddr 0a:58:0a:f4:00:01
inet addr:10.244.0.1 Bcast:0.0.0.0 Mask:255.255.255.0
inet6 addr: fe80::401:b4ff:fe51:bcf9/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1
RX packets:2133 errors:0 dropped:0 overruns:0 frame:0
TX packets:2216 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:139381 (139.3 KB) TX bytes:853302 (853.3 KB)
docker0 Link encap:Ethernet HWaddr 02:42:93:0f:f7:11
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:93ff:fe0f:f711/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:653 errors:0 dropped:0 overruns:0 frame:0
TX packets:685 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:49542 (49.5 KB) TX bytes:430826 (430.8 KB)
enp0s3 Link encap:Ethernet HWaddr 02:54:ad:ea:60:2e
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet6 addr: fe80::54:adff:feea:602e/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:7951 errors:0 dropped:0 overruns:0 frame:0
TX packets:4123 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:5081047 (5.0 MB) TX bytes:385600 (385.6 KB)
```
我稍微解释一下这里面显示的数据。
```
Link encap:Ethernet HWaddr 02:54:ad:ea:60:2e
```
上面这段表明这是一个以太网设备MAC地址为02:54:ad:ea:60:2e。
```
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet6 addr: fe80::54:adff:feea:602e/64 Scope:Link
```
这里显示的是网卡的IPv4和IPv6地址其中IPv4还显示了该网络的子网掩码以及广播地址。
在每个IPv4子网中有一个特殊地址被保留作为子网广播地址比如这里的10.0.2.255就是这个子网的广播地址。当向这个地址发送请求时,就会向以太网网络上的一组主机发送请求。
通常来说这种被称作广播broadcast的技术是用UDP来实现的。
```
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
```
这里显示的是网卡的状态MTU是最大传输单元的意思表示的是链路层包的大小。1500表示的是字节大小。
Metric大家可能不知道是干啥用的这里解释下Linux在一台主机上可以有多个网卡设备很可能有这么一种情况多个网卡可以路由到目的地。一个简单的例子是在同时有无线网卡和有线网卡的情况下网络连接是从哪一个网卡设备上出去的Metric就是用来确定多块网卡的优先级的数值越小优先级越高1为最高级。
```
RX packets:7951 errors:0 dropped:0 overruns:0 frame:0
TX packets:4123 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:5081047 (5.0 MB) TX bytes:385600 (385.6 KB)
```
## netstat和lsof对网络状况了如指掌
在平时的工作中我们最常碰到的问题就是某某进程对应的网络状况如何是不是连接被打爆了还是有大量的TIME\_WAIT连接
netstat可以帮助我们了解当前的网络连接状况比如我想知道当前所有的连接详情就可以使用下面这行命令
```
netstat -alepn
```
可能的结果为:
![](https://static001.geekbang.org/resource/image/34/df/34084af982a4c4223e0a78ed01c662df.jpg)
netstat会把所有IPv4形态的TCPIPV6形态的TCP、UDP以及UNIX域的套接字都显示出来。
对于TCP类型来说最大的好处是可以清楚地看到一条TCP连接的四元组源地址、源端口、目的地地址和目的端口
例如这里的一条信息:
```
tcp 0 0 127.0.0.1:2379 127.0.0.1:52464 ESTABLISHED 0 27710 3496/etcd
```
它表达的意思是本地127.0.0.1的端口52464连上本地127.0.0.1的端口2379状态为ESTABLISHED本地进程为etcd进程为3496。
这在实战分析的时候非常有用比如你可以很方便地知道在某个时候是不是有很多TIME\_WAIT的TCP连接导致端口号被占用光以致新的连接分配不了。
当然我们也可以只对UNIX套接字进行筛查。
```
netstat Socket -x -alepn
```
![](https://static001.geekbang.org/resource/image/a1/13/a1aeca1245b6b8cabaa0f22ce02d4813.jpg)
UNIX套接字的结果稍有不同最关键的信息是Path这个信息显示了本地套接字监听的文件路径比如这条
```
unix 3 [ ] STREAM CONNECTED 23209 1400/dockerd /var/run/docker.sock
```
这其实就是大名鼎鼎的Docker在本地套接字的监听路径。/var/run/docker.sock是本地套接字监听地址dockerd是进程名称1400是进程号。
netstat命令可以选择的参数非常之多这里只关注了几个简单的场景你可以通过帮助命令或者查阅文档获得更多的信息。
lsof的常见用途之一是帮助我们找出在指定的IP地址或者端口上打开套接字的进程而netstat则告诉我们IP地址和端口使用的情况以及各个TCP连接的状态。Isof和netstst可以结合起来一起使用。
比如说我们可以通过lsof查看到底是谁打开了这个文件
```
lsof /var/run/docker.sock
```
下面这张图显示了是dockerd打开了这个本地文件套接字
![](https://static001.geekbang.org/resource/image/ac/28/acebeb7d0bbe26b469a200456c299d28.jpg)
lsof还有一个非常常见的用途。如果我们启动了一个服务器程序发现这个服务器需要绑定的端口地址已经被占用内核报出“该地址已在使用”的出错信息我们可以使用lsof找出正在使用该端口的那个进程。比如下面这个代码就帮我们找到了使用8080端口的那个进程从而帮助我们定位问题。
```
lsof -i :8080
```
## 抓包利器: tcpdump
tcpdump这样的抓包工具对于网络编程而言是非常有用的特别是在一些“山重水复疑无路”的情形下通过tcpdump这样的抓包工具往往可以达到“柳暗花明又一村”的效果。
tcpdump具有非常强大的过滤和匹配功能。
比如说指定网卡:
```
tcpdump -i eth0
```
再比如说指定来源:
```
tcpdump src host hostname
```
我们再来一个复杂一点的例子。这里抓的包是TCP且端口是80包来自IP地址为192.168.1.25的主机地址。
```
tcpdump 'tcp and port 80 and src host 192.168.1.25'
```
如果我们对TCP协议非常熟悉还可以写出这样的tcpdump命令
```
tcpdump 'tcp and port 80 and tcp[13:1]&2 != 0'
```
这里tcp\[13:1\]表示的是TCP头部开始处偏移为13的字节如果这个值为2说明设置了SYN分节当然我们也可以设置成其他值来获取希望类型的分节。注意这里的偏移是从0开始算起的tcp\[13\]其实是报文里的第14个字节。
tcpdump在开启抓包的时候会自动创建一个类型为AF\_PACKET的网络套接口并向系统内核注册。当网卡接收到一个网络报文之后它会遍历系统中所有已经被注册的网络协议包括其中已经注册了的AF\_PACKET网络协议。系统内核接下来就会将网卡收到的报文发送给该协议的回调函数进行一次处理回调函数可以把接收到的报文完完整整地复制一份假装是自己接收到的报文然后交给tcpdump程序进行各种条件的过滤和判断再对报文进行解析输出。
下面这张图显示的是tcpdump的输出格式
![](https://static001.geekbang.org/resource/image/43/c3/43a9e4ea08bc872c2646453ce06ed3c3.jpg)
首先我们看到的是时间戳之后类似192.168.33.11.41388 > 192.168.33.11.6443这样的显示的是源地址192.168.33.11.41388到目的地址192.168.33.11.6443然后Flags \[ \]是包的标志,\[P\]表示是数据推送,比较常见的包格式如下:
* \[S\]SYN表示开始连接
* \[.\]:没有标记,一般是确认
* \[P\]PSH表示数据推送
* \[F\]FIN表示结束连接
* \[R\] RST表示重启连接
我们可以看到最后有几个数据,它们代表的含义如下:
* seq包序号就是TCP的确认分组
* cksum校验码
* win滑动窗口大小
* length承载的数据payload长度length如果没有数据则为0
此外tcpdump还可以对每条TCP报文的细节进行显示让我们可以看到每条报文的详细字节信息。这在对报文进行排查的时候很有用。
## 小结
本章我讲述了一些常见的网络诊断工具,这些工具需要你了解之后活学活用。用好它们,对加深网络编程的理解,以及对问题情况进行排查等都有非常大的帮助。
我再来总结一下这几个命令的作用:
* ping可以用来帮助我们进行网络连通性的探测。
* ifconfig用来显示当前系统中的所有网络设备。
* netstat和lsof可以查看活动的连接状况。
* tcpdump可以对各种奇怪的环境进行抓包进而帮我们了解报文排查问题。
## 思考题
最后给大家留两个思考题。
本章我讲到了强大的抓包工具tcpdump你知道tcpdump这个工具还可以对UDP包进行抓包处理吗你不妨尝试一下。
另外netstat输出时监听状态的套接字所对应的Foreign Address显示的\*.\*表示的是什么意思呢?
欢迎你在评论区写下你的思考,我会和你一起交流,也欢迎你把这篇文章分享给你的朋友或者同事,一起讨论下这几个工具。