现代计算机程序大部分时候离不开网络,作为开发者,在日常开发网络相关的程序或者排查程序错误时经常会用抓包工具来分析网卡收发的数据,比如著名的tcpdump,Wireshark等。今天我们尝试用100行左右的Python代码在Linux系统上实现一个简单的抓包工具。
本文共分为四节,前三节分别介绍了三个基本概念,包括端序、socket、以太网帧结构,最后一节介绍具体实现,文末有完整代码。
1. 端序
试想一个16进制数0x12345678,大端序在内存中的储存顺序如下,高字节保存在内存的低地址,高字节在前:
内存地址 0 1 2 3
---------------------
字节数据 | 12 | 34 | 56 | 78 |
---------------------
而小端序高字节保存在内存的高地址,低字节在前:
内存地址 0 1 2 3
---------------------
字节数据 | 78 | 56 | 34 | 12 |
---------------------
大端序还是小端序是由硬件决定的,通常CPU和内存中的数据都是小端序,而网络传输都用大端序,因为大端序字节流解析起来更方便,但有些特殊的设备是例外。
Python中socket模块的htons,ntohs,htonl,ntohl函数用来处理网络字节顺序与本地字节顺序之间的转换。函数名中h表示host,n表示net,s表示short int(2字节),l表示long int(4字节),比如htons是将2字节长的整数从本地端序转换为网络端序。比如:
import socket
i = 0x1234 # 本地小端序
net_i = socket.htons(i) # 网络大端序
print(hex(i), hex(net_i))
# 输出 0x1234 0x3412
2. socket编程
socket是操作系统用户与TCP/IP协议族通信的接口,创建一个socket需要3个参数,地址簇,socket类型,协议。比如可以用以下代码创建一个常用的TCP套接字,用来收发TCP数据流:
import socket
# AF_INET: internet协议簇
# SOCK_STREAM: stream类型的socket
# 0: 使用该socket类型的默认协议,即TCP协议
sock = socket.socket(socket.AF_INET