TCP网络编程常用工具(转载)

TCP网络编程常用工具(转载)

ping、ifconfig、netstat、lsof、tcpdump

其实在平常使用套接字开发和测试过程中,我们总会碰到这样或那样的问题。学会对这些问题进行诊断和分析,其实需要不断地积累经验。而 Linux 平台下提供的各种网络工具,则为我们进行诊断分析提供了很好的帮助。

一、ping

这个命令我想大家都不陌生,“ping” 这个命名来自于声呐探测,在网络上用来完成对网络连通性的探测,这个命名可以说是恰如其分了。

1
2
3
4
5
6
7
8
9
10
11
12
$ 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 的请求类型为 0,应答为 8。
  • 代码:进一步划分 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,也用来显示当前系统中的所有网络设备,通俗一点的说,就是网卡列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

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)

我稍微解释一下这里面显示的数据。

1
Link encap:Ethernet  HWaddr 02:54:ad:ea:60:2e

上面这段表明这是一个以太网设备,MAC 地址为 02:54:ad:ea:60:2e。

1
2
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 来实现的。

1
UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

这里显示的是网卡的状态,MTU 是最大传输单元的意思,表示的是链路层包的大小。1500 表示的是字节大小。

Metric 大家可能不知道是干啥用的,这里解释下,Linux 在一台主机上可以有多个网卡设备,很可能有这么一种情况,多个网卡可以路由到目的地。一个简单的例子是在同时有无线网卡和有线网卡的情况下,网络连接是从哪一个网卡设备上出去的?Metric 就是用来确定多块网卡的优先级的,数值越小,优先级越高,1 为最高级。

1
2
3
4
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 可以帮助我们了解当前的网络连接状况,比如我想知道当前所有的连接详情,就可以使用下面这行命令:

1
netstat -alepn

可能的结果为:

netstat 会把所有 IPv4 形态的 TCP,IPV6 形态的 TCP、UDP 以及 UNIX 域的套接字都显示出来。

对于 TCP 类型来说,最大的好处是可以清楚地看到一条 TCP 连接的四元组(源地址、源端口、目的地地址和目的端口)。

例如这里的一条信息:

1
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 套接字进行筛查。

1
netstat Socket -x -alepn

UNIX 套接字的结果稍有不同,最关键的信息是 Path,这个信息显示了本地套接字监听的文件路径,比如这条:

1
unix  3      [ ]         STREAM     CONNECTED     23209    1400/dockerd        /var/run/docker.sock

这其实就是大名鼎鼎的 Docker 在本地套接字的监听路径。/var/run/docker.sock 是本地套接字监听地址,dockerd 是进程名称,1400 是进程号。

netstat 命令可以选择的参数非常之多,这里只关注了几个简单的场景,你可以通过帮助命令或者查阅文档获得更多的信息。

lsof 的常见用途之一是帮助我们找出在指定的 IP 地址或者端口上打开套接字的进程,而 netstat 则告诉我们 IP 地址和端口使用的情况,以
及各个 TCP 连接的状态。lsof 和 netstst 可以结合起来一起使用。

比如说,我们可以通过 lsof 查看到底是谁打开了这个文件:

1
lsof /var/run/docker.sock

下面这张图显示了是 dockerd 打开了这个本地文件套接字:

lsof 还有一个非常常见的用途。如果我们启动了一个服务器程序,发现这个服务器需要绑定的端口地址已经被占用,内核报出“该地址已在使用”的出错信息,我们可以使用 lsof 找出正在使用该端口的那个进程。比如下面这个代码,就帮我们找到了使用 8080 端口的那个进程,从而帮助我们定位问题。

1
lsof -i :8080

四、tcpdump

tcpdump 这样的抓包工具对于网络编程而言是非常有用的,特别是在一些“山重水复疑无路”的情形下,通过 tcpdump 这样的抓包工具,往往可以达到“柳暗花明又一村”的效果。

tcpdump 具有非常强大的过滤和匹配功能。

比如说指定网卡:

1
tcpdump -i eth0

再比如说指定来源:

1
tcpdump src host hostname

我们再来一个复杂一点的例子。这里抓的包是 TCP,且端口是 80,包来自 IP 地址为 192.168.1.25 的主机地址。

1
tcpdump 'tcp and port 80 and src host 192.168.1.25'

如果我们对 TCP 协议非常熟悉,还可以写出这样的 tcpdump 命令:

1
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 可以对各种奇怪的环境进行抓包,进而帮我们了解报文,排查问题。

六、问答

1、tcpdump 这个工具还可以对 UDP 包进行抓包处理吗?

  • tcpdump 可以抓 UDP,指定端口就可以。
  • tcpdump 还可以导出文件 pcap,放到 wireshark 中进一步分析。

2、Foreign Address 显示的 . 表示的是什么意思呢?

这个套接字正在监听端口等待连接进来,允许任何地址、任何端口来建立连接。

参考文章:
工欲善其事必先利其器:学会使用各种工具

评论

:D 一言句子获取中...

加载中,最新评论有1分钟缓存...