极客时间已完结课程限时免费阅读

08 | 工欲善其事必先利其器:学会使用各种工具

08 | 工欲善其事必先利其器:学会使用各种工具-极客时间

08 | 工欲善其事必先利其器:学会使用各种工具

讲述:冯永吉

时长10:33大小9.66M

你好,我是盛延敏,这里是网络编程实战第 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 序列号排序显示的,一并显示的,也包括 TTL(time to live),反映了两个 IP 地址之间传输的时间。最后还显示了 ping 命令的统计信息,如最小时间、平均时间等。
我们需要经常和 Linux 下的 ping 命令打交道,那么 ping 命令的原理到底是什么呢?它是基于 TCP 还是 UDP 开发的?
都不是。
其实,ping 是基于一种叫做 ICMP 的协议开发的,ICMP 又是一种基于 IP 协议的控制协议,翻译为网际控制协议,其报文格式如下图:
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
可能的结果为:
netstat 会把所有 IPv4 形态的 TCP,IPV6 形态的 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
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 打开了这个本地文件套接字:
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 的输出格式:
首先我们看到的是时间戳,之后类似 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 显示的 *.* 表示的是什么意思呢?
欢迎你在评论区写下你的思考,我会和你一起交流,也欢迎你把这篇文章分享给你的朋友或者同事,一起讨论下这几个工具。
分享给需要的人,Ta购买本课程,你将得18
生成海报并分享

赞 22

提建议

上一篇
07 | What? 还有本地套接字?
下一篇
09丨答疑篇:学习网络编程前,需要准备哪些东西?
unpreview
 写留言

精选留言(31)

  • xindoo
    2019-08-19
    iftop命令可以查网络io大户
    共 1 条评论
    59
  • 许童童
    2019-08-19
    tcpdump可以抓UDP,很简单 指定端口就可以了。 tcpdump还可以导出文件pcap,放到wireshark中进一步分析。 Foreign Address 显示的 *.* 表示的是什么意思呢? 这个套接字正在监听端口等待连接进来,允许任何地址、任何端口来建立连接。
    展开
    共 3 条评论
    31
  • 一步
    2019-08-20
    对于 ping 的最后统计信息中 rtt min/avg/max/mdev = 4.512/4.579/4.647/0.078 ms 中的 mdev 时间是什么意思的?

    作者回复: 这个值我也没注意过,看了介绍是说mdev 就是 Mean Deviation 的缩写,它表示这些 ICMP 包的 RTT 偏离平均值的程度,这个值越大说明你的网速越不稳定。

    共 2 条评论
    12
  • 剑衣清风
    2019-08-19
    tcpdump 可以结合 wireshark,也就是把抓到的协议包保存成 cap 格式的,然后在 windows 上点击查看更加形象 tcpdump host 10.1.11.133 and udp port 5060 -v -w gw.cap【写成 wireshark 可读取的】
    10
  • icejoywoo
    2019-11-02
    ss可以替代netstat么,netstat有时候非常慢,ss速度很快

    作者回复: 可以的,作为一个老程序员,ss是我刚刚接触到的,学习了。

    6
  • jay
    2020-03-30
    文中这句语: 即 ICMP 的类型, 其中 ping 的请求类型为 0,应答为 8。描述有误,请求类型为8,应答为0。 Type: 8 (Echo (ping) request) Type: 0 (Echo (ping) reply)

    作者回复: 感谢斧正,已经修改,待提交。

    共 2 条评论
    5
  • 明键
    2019-11-27
    IPv6的地址不是128位吗,为什么我数ifconfig的输出只有12字节?还有四字节到哪里去了呢?

    作者回复: IPV6的地址格式为X:X:X:X:X:X:X:X,一共8个X,其中每个X表示地址中的16b,以十六进制表示。 在某些情况下,一个IPv6地址中间可能包含很长的一段0,可以把连续的一段0压缩为“::”。 所以,看看你的地址里是不是有::哦。

    共 2 条评论
    5
  • 哦哟哟哟哟
    2019-08-19
    麻烦老师解答下。前面ifconfig命令关于网卡优先级的metric=1表示优先级最高、示例中三张网卡优先级都是1、那如何进行选择呢

    作者回复: 看你应用程序绑定到哪个网卡设备和Ip了

    共 2 条评论
    5
  • 2019-11-22
    小结 ping 可以用来帮助我们进行网络连通性的探测。 ifconfig,用来显示当前系统中的所有网络设备。 netstat 和 lsof 可以查看活动的连接状况。 tcpdump 可以对各种奇怪的环境进行抓包,进而帮我们了解报文,排查问题。 iftop命令可以查网络io大户 arp router ss
    展开
    共 1 条评论
    4
  • 超大红细胞
    2020-01-05
    tcpdump 抓取 1400 端口 UDP 报文并保存: tcpdump -i ens33 udp port -w ./udp.cap

    作者回复: 学习了

    共 2 条评论
    3
  • 晚风·和煦
    2020-02-11
    老师,accept是发生在三次握手之后吗?😂

    作者回复: 在程序角度来说,accept函数表示的是三次握手的过程,如果从accept返回,表示三次握手成功,连接已经建立。

    共 2 条评论
    2
  • 极客雷
    2019-10-22
    ifconfig、netstat都是比较偏老的、不再维护的命令,为啥不与时俱进讲一下iproute2系列的命令

    作者回复: 这些命令在平时的trouleshooting还是很有用处的,毕竟还不能在生产环境都换成Linux 4/5版本吧。

    2
  • null
    2021-04-09
    如果对方禁ping 了,还有其他方法能测试与对方服务器的连通性么?

    作者回复: ping都不通了,可以认为对方服务器真的下线了

    共 3 条评论
    1
  • NEVER SETTLE
    2020-10-31
    老师,我用CLion编写代码时,有明显的语法错误,结果没有提示出来(波浪线等等)

    作者回复: 正常啊,工具也不是万能的。不过很好奇,什么样的语法错误逃脱了CLion的火眼金睛。

    1
  • dll
    2022-07-29 来自北京
    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) _________ RX TX 是什么呢。我看到文里没有解释
    展开
    共 1 条评论
  • gecko
    2022-07-14
    netstat netstat -x -alepn yes netstat Socket -x -alepn no
  • null
    2021-03-19
    macbook 下,也是使用 wireshark 配合 tcpdump 文件一起么?

    作者回复: mac是可以使用这两个工具的。

  • null
    2021-03-18
    cni0(1450)、enp0s3(1500)、docker0(1500)这三者的 MTU 为啥不一样,是协议栈的不同么? 这三个网卡的 Metric 都为 1,最高级,那最终选择哪一个网卡设置呀? 谢谢老师!

    作者回复: 选择哪个网卡,有多个因素会决定,一个可能的因素是系统路由表,比如docker0的地址是172.17.0.1,走172.17.0.0/16的地址就会通过docker0。

  • Geek_8593e5
    2020-09-03
    老师说的,类型:即 ICMP 的类型, 其中 ping 的请求类型为 0,应答为 8。我实际测了下,应答为0,请求为8?

    作者回复: 你是对的,我已更改文稿。

  • 你好
    2020-04-08
    🙋,老师我有问题: 前面文章中提到过:使用 INADDR_ANY,一台机器有两块网卡,IP 地址分别是 202.61.22.55 和 192.168.1.11,那么向这两个 IP 请求的请求包都会被我们编写的应用程序处理。那如果优先级相同是走哪一个呢? 那服务器/客户端绑定端口的时候如何指定使用哪个网卡进行绑定呢?
    展开

    作者回复: 应该是随机的吧,没有什么优先级;一般我们多用ANY_ADDR来做,本身就不想限制是哪块网卡或者IP地址。