Web 3D地球实时统计访问来源
前言
这篇文章介绍一个Web 3D地球实时统计访问来源的开源项目,效果如下,当服务器有http流量进来时,web通过3D地球+飞线实时绘制客户端的来源
![]()
实现原理
要在服务器上捕获http流量,也就是抓包,抓包有两种方案,一是使用类似pcap的库从内核捕获网络数据包,另一种比较复杂是使用ebpf拦截系统的函数点,以检测流量,本项目是使用pcap实现。
要通过代码把流量抓下来,其实逻辑比较简单,大概如下
-
打开设备 (Open Device) :
首先得告诉程序我们要监听哪个网卡(比如 eth0 还是 wlan0)。这里通常需要开启混杂模式 ,否则网卡默认只会接收发给它自己的包,而忽略广播或其他流量。 -
设置过滤器 (Set BPF Filter) :
这一步很重要,服务器上的流量太杂了,SSH、数据库、等数据包。我们只想看 HTTP 流量,所以需要设置一个过滤规则,比如 tcp port 80 or tcp port 443。这用的就是 BPF (Berkeley Packet Filter) 语法,效率极高,直接在内核层就把不相关的包扔掉了。 -
循环抓包 (Loop) :
前两步配置好后,就是在一个死循环里不断地从句柄中读取数据包(Packet)。
伪代码大概长这样:
// 1. 打开网卡 eth0,65535是最大捕获长度,1是开启混杂模式
handle = pcap_open_live("eth0", 65535, 1, 1000, errbuf);
// 2. 编译并设置过滤规则
pcap_compile(handle, &fp, "tcp port 80", 0, net);
pcap_setfilter(handle, &fp);
// 3. 每抓到一个包就回调 process_packet 函数
pcap_loop(handle, -1, process_packet, NULL);
pcap_loop 捕获到数据时,拿到手的是一堆原始的二进制字节,截获到的数据包,需要解析出以太网帧头,以太头由 14 字节固定长度构成,用于指明目标与源 MAC 地址,以及数据使用的上层协议类型,如下。
![]()
其中数据类型表示以太网帧中载荷(Payload)是什么协议的数据。
| 协议类型 | 十六进制 | 说明 |
|---|---|---|
| IPv4 | 0x0800 | 表示数据部分是 IPv4 数据包 |
| ARP | 0x0806 | 地址解析协议 |
| IPv6 | 0x86DD | IPv6 数据包 |
在项目中,解析时直接跳过14字节去解析ip报文即可,因为用不到以太数据,解析到ip头后就可以获取客户端的地址了,ip头格式如下。
![]()
获取ip包信息代码如下
void process(u_char *d, struct pcap_pkthdr *h, u_char *p) {
struct ip *ip4_pkt = (struct ip *) (p + link_offset);
uint32_t ip_hl = ip4_pkt->ip_hl * 4;
uint8_t ip_proto = ip4_pkt->ip_p;
char ip_src[INET_ADDRSTRLEN];
char ip_dst[INET_ADDRSTRLEN];
unsigned char *data;
uint32_t len = h->caplen;
inet_ntop(AF_INET, (const void *) &ip4_pkt->ip_src, ip_src, sizeof(ip_src));
inet_ntop(AF_INET, (const void *) &ip4_pkt->ip_dst, ip_dst, sizeof(ip_dst));
}
接下来拿到客户端的ip后,解析出ip的经纬度通过websocket发送到前端即可,经纬度有两种办法可以获取。
-
使用maxminddb
他是一种离线的经纬度查询系统,但是测试后不太准确,优点是速度极快。
-
在线服务
寻找大量的在线ip转经纬度服务,找到他的api接口,在程序中使用负载均衡(因为免费服务都要分钟内次数限制)
但是,目前广泛的做法是nginx做web监听,使用https 443端口,虽然抓包获取ip来源没问题,但是有时候我们想通过http请求头中的字段获取ip,比如真实的用户IP往往藏在HTTP请求头的 X-Forwarded-For 或者 X-Real-IP 类似的字段里,这就需要解析tcp数据包,从tcp的Payload中解析出http请求头信息。
GET /api/v1/stats HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0...
X-Forwarded-For: 203.0.113.195
...
项目直接调用github.com/nodejs/http… 这个库去解析http头,他是被广泛验证的高性能 C 解析库(这也是 Node.js 早期底层使用的解析器)。