通过 UPnP 协议监测路由器的流量

2025-02-07#UPnP#Go

UPnP 即“通用即插即用”(Universal Plug and Play),能让网络设备自动发现和配置,实现互联互通。基于 UPnP,路由器能与设备更好交互,为监测路由器流量提供便利,助用户管理网络。对于支持 UPnP 且开启了 UPnP 的路由器,可通过特定接口访问路由器的状态信息。

开源的智能家居自动化平台 Home Assistant 内置了对 UPnP 设备的支持,在添加了 UPnP/IGD 设备后,即可看到局域网内的路由器,并且获取路由器的上传和下载速率。下文以简单的代码示例,介绍通过 Golang 代码获取路由器的流量数据的方法。

UPnP库 🔗

huin/goupnp 是一个 Go语言的 UPnP 客户端库。

实现思路 🔗

基于当前的 goupnp 库,想要获取路由器的流量数据,包括如下几步:

  1. 通过 UPnP 协议,发现当前网络中的服务实例。
  2. 针对每个实例,获取当前的已接收和已发送的字节数。已接收即下载,已发送即上传。
  3. 等待一定时间,针对每个实例,再次获取获取当前的已接收和已发送的字节数。
  4. 根据两次获取到的字节数和间隔时间,计算下载和上传速度。

代码示例 🔗

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/huin/goupnp/dcps/internetgateway1"
)

func main() {
	if err := invoke(); err != nil {
		log.Fatalln(err)
	}
}

type Speed struct {
	Device   string
	Upload   float64
	Download float64
}

func invoke() error {
	clients, errors, err := internetgateway1.NewWANCommonInterfaceConfig1ClientsCtx(context.Background())
	if err != nil {
		return fmt.Errorf("Error discovering service with UPnP: %v", err)
	}

	if len(errors) > 0 {
		return fmt.Errorf("Error discovering services: %v", errors)
	}

	interval := 2 * time.Second

	for _, client := range clients {
		oldRecv, err := client.GetTotalBytesReceivedCtx(context.Background())
		if err != nil {
			return fmt.Errorf("Error requesting bytes received: %v", err)
		}
		oldSent, err := client.GetTotalBytesSentCtx(context.Background())
		if err != nil {
			return fmt.Errorf("Error requesting bytes sent: %v", err)
		}

		time.Sleep(interval)

		newRecv, err := client.GetTotalBytesReceived()
		if err != nil {
			return fmt.Errorf("Error requesting bytes received: %v", err)
		}
		newSent, err := client.GetTotalBytesSent()
		if err != nil {
			return fmt.Errorf("Error requesting bytes sent: %v", err)
		}

		downloadSpeed := float64(newRecv-oldRecv) / interval.Seconds()
		uploadSpeed := float64(newSent-oldSent) / interval.Seconds()

		fmt.Printf("%s\tDownload:%.2f KB/s\tUpload:%.2f KB/s\n", client.RootDevice.Device.FriendlyName, downloadSpeed/1000, uploadSpeed/1000)
	}

	return nil
}

其他 🔗

如果调试上面的代码,可以发现上述代码访问了 IGD 接口、IFC 接口等。我的路由器地址为 192.168.1.1,它们的地址分别是 http://192.168.1.1:1900/igd.xmlhttp://192.168.1.1:1900/ifc.xml


加载中...