Go语言获取实时网速(gopacket实践)

本文介绍如何使用Go语言和gopacket库进行网络流量监控,包括安装配置npcap,通过抓包获取上行和下行数据包大小,以及计算每秒平均流量。

简介

gopacket是经过cgo封装的libpcap的接口,这样便于我们在go语言中使用libpcap

前提

libpcap是Linux平台的抓包框架,它也有Windows移植版,比如winpcap,但在2013年已停止维护。所以本文采用的是winpcap官方推荐的npcap

npcap的安装
  1. 首先下载npcap以及npcap-sdk;
  2. 安装npcap;
  3. 解压npcap-sdkC:\WpdPack。(gopacketcgo实现会在这个位置寻找pcap.h)

代码

package main

import (
	"errors"
	"flag"
	"fmt"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"log"
	"net"
	"os"
	"time"
)

var (
	downStreamDataSize = 0  // 单位时间内下行的总字节数
	upStreamDataSize   = 0  // 单位时间内上行的总字节数
	deviceName        = flag.String("i", "eth0", "network interface device name") // 要监控的网卡名称
)

func main() {
	flag.Parse()

	// Find all devices
	// 获取所有网卡
	devices, err := pcap.FindAllDevs()
	if err != nil {
		log.Fatal(err)
	}

	// Find exact device
	// 根据网卡名称从所有网卡中取到精确的网卡
	var device pcap.Interface
	for _, d := range devices {
		if d.Name == *deviceName {
			device = d
		}
	}

	// 根据网卡的ipv4地址获取网卡的mac地址,用于后面判断数据包的方向
	macAddr, err := findMacAddrByIp(findDeviceIpv4(device))
	if err != nil {
		panic(err)
	}

	fmt.Printf("Chosen device's IPv4: %s\n", findDeviceIpv4(device))
	fmt.Printf("Chosen device's MAC: %s\n", macAddr)

	// 获取网卡handler,可用于读取或写入数据包
	handle, err := pcap.OpenLive(*deviceName, 1024 /*每个数据包读取的最大值*/, true /*是否开启混杂模式*/, 30*time.Second /*读包超时时长*/)
	if err != nil {
		panic(err)
	}
	defer handle.Close()

	// 开启子线程,每一秒计算一次该秒内的数据包大小平均值,并将下载、上传总量置零
	go monitor() 
	
	// 开始抓包
	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	for packet := range packetSource.Packets() {
		// 只获取以太网帧
		ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
		if ethernetLayer != nil {
			ethernet := ethernetLayer.(*layers.Ethernet)
			// 如果封包的目的MAC是本机则表示是下行的数据包,否则为上行
			if ethernet.DstMAC.String() == macAddr {
				downStreamDataSize += len(packet.Data()) // 统计下行封包总大小
			} else {
				upStreamDataSize += len(packet.Data())   // 统计上行封包总大小
			}
		}
	}
}

// 获取网卡的IPv4地址
func findDeviceIpv4(device pcap.Interface) string {
	for _, addr := range device.Addresses {
		if ipv4 := addr.IP.To4(); ipv4 != nil {
			return ipv4.String()
		}
	}
	panic("device has no IPv4")
}

// 根据网卡的IPv4地址获取MAC地址
// 有此方法是因为gopacket内部未封装获取MAC地址的方法,所以这里通过找到IPv4地址相同的网卡来寻找MAC地址
func findMacAddrByIp(ip string) (string, error) {
	interfaces, err := net.Interfaces()
	if err != nil {
		panic(interfaces)
	}

	for _, i := range interfaces {
		addrs, err := i.Addrs()
		if err != nil {
			panic(err)
		}

		for _, addr := range addrs {
			if a, ok := addr.(*net.IPNet); ok {
				if ip == a.IP.String() {
					return i.HardwareAddr.String(), nil
				}
			}
		}
	}
	return "", errors.New(fmt.Sprintf("no device has given ip: %s", ip))
}

// 每一秒计算一次该秒内的数据包大小平均值,并将下载、上传总量置零
func monitor() {
	for {
		os.Stdout.WriteString(fmt.Sprintf("\rDown:%.2fkb/s \t Up:%.2fkb/s", float32(downStreamDataSize)/1024/1, float32(upStreamDataSize)/1024/1))
		downStreamDataSize = 0
		upStreamDataSize = 0
		time.Sleep(1 * time.Second)
	}
}

运行

运行的时候需要指定网卡名称,如: ./speedTest -i etc0
但是在Windows平台有些不一样,因为gopacket接口返回的deviceNameWindows平台特有Transport Name,通过getmac命令即可获得MACTransport Name的对应关系了:

C:\Users\hanzi>getmac

物理地址            传输名称
=================== ==========================================================
00-50-56-C0-00-08   \Device\Tcpip_{0849F909-8F06-475C-B0D3-81F5902DA026}
00-FF-D8-BD-B9-CF   媒体已断开连接
00-50-56-C0-00-01   \Device\Tcpip_{55A0F561-C6BF-4E15-9E97-D378BDB2B856}
36-15-2F-36-64-AE   \Device\Tcpip_{E1264F2B-388D-4992-A9DE-040165E3A4EE}
4C-CC-6A-B7-E6-D6   \Device\Tcpip_{DF0ECEC3-C9D5-4D38-946F-3E5CD67CCA2E}
02-00-4C-4F-4F-50   \Device\Tcpip_{0D1DF6B1-ADA4-485B-B0A6-B95C2EC619A5}

在笔者电脑上运行./speedTest -i \Device\NPF_{DF0ECEC3-C9D5-4D38-946F-3E5CD67CCA2E}, 输出:

Chosen device's IPv4: 192.168.0.8
Chosen device's MAC: 4c:cc:6a:b7:e6:d6
Down:48.26kb/s 	 Up:3.20kb/s

以上代码的正确运行需要网卡有IPv4,但可以很容易的扩展到IPv6(响应工信部号召)

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值