Golang的GUI库-Fyne使用案例-网卡速度监测

2023-05-16

本文的重点在于实现widget.Select中的回调函数逻辑,网卡抓包采用了google的gopacket模块。

运行效果如下:
在这里插入图片描述
通过切换Select选择对应网卡,网卡名右侧显示当前网卡对应的IP,下方显示网卡的上下行速度。

代码目录结构组织如下:
在这里插入图片描述

源码及其说明如下:

  • app.go
    主要是界面的处理逻辑。
package ui

import (
	"FyneNet/internal"
	"context"
	"fmt"
	"fyne.io/fyne"
	"fyne.io/fyne/app"
	"fyne.io/fyne/layout"
	"fyne.io/fyne/widget"
	"time"
)

type AppMainWindow struct {
	ctx           context.Context
	playBtn       *widget.Button // 开始监控
	stopBtn       *widget.Button // 停止监控
	ethCards      *widget.Select // 网卡选择
	ipLabel       *widget.Label  // IP地址栏
	macLabel      *widget.Label  // MAC地址栏
	upLoadSpeed   *widget.Label  // 上行速度
	downLoadSpeed *widget.Label  // 下行速度
	anl           *internal.Analyzer
	// 要绘制图表
}

func (mw *AppMainWindow) Run() {
	a := app.New()

	ncs, _ := internal.GetNetCardsWithIPv4Addr()
	ethCards := make([]string, 0, 4)
	for _, nc := range ncs {
		ethCards = append(ethCards, nc.GetName())
	}

	mw.anl = &internal.Analyzer{}
	mw.anl.Init()

	mw.ethCards = widget.NewSelect(ethCards, func(s string) {
		var ncard internal.NetCard
		if mw.anl.Running {
			// 如果没能停止,Stop()会阻塞
			mw.anl.Stop()
		}
		validCard := false
		for _, nc := range ncs {
			if nc.GetName() == s {
				mw.ipLabel.SetText(nc.GetIPv4Addr())
				ncard = nc
				validCard = true
			}
		}

		// 如果是有效网卡,则进行捕捉
		if validCard {
			mw.anl.Nc = &ncard
			go mw.anl.Capture()
		}

	})

	//mw.ethCards.SetSelected(ethCards[0])
	mw.upLoadSpeed = widget.NewLabel("---")
	mw.downLoadSpeed = widget.NewLabel("----")

	go mw.UpdateSpeed()

	mw.ipLabel = widget.NewLabel("---")



	w := a.NewWindow("FyneNet")
	w.SetTitle("FyneNet")
	w.SetContent(fyne.NewContainerWithLayout(layout.NewGridLayout(1),
		fyne.NewContainerWithLayout(layout.NewGridLayout(2), mw.ethCards, mw.ipLabel),
		fyne.NewContainerWithLayout(layout.NewGridLayout(2), mw.upLoadSpeed, mw.downLoadSpeed),
	))
	w.ShowAndRun()
}


func (mw *AppMainWindow) UpdateSpeed() {
	for  {
		upspeed := fmt.Sprintf("Down:%.2fkb/s", mw.anl.GetDownSpeed())
		downspeed := fmt.Sprintf("Up:%.2fkb/s", mw.anl.GetUpSpeed())
		mw.upLoadSpeed.SetText(upspeed)
		mw.downLoadSpeed.SetText(downspeed)
		time.Sleep(1 * time.Second)
	}
}
  • basic.go
    获取网卡信息的方法都在这里。
package internal

import (
	"errors"
	"fmt"
	"github.com/google/gopacket/pcap"
	"net"
)


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

// 根据网卡的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() {
					fmt.Println("found one")
					fmt.Println(i.HardwareAddr.String())
					return i.HardwareAddr.String(), nil
				}
			}
		}
	}
	return "", errors.New(fmt.Sprintf("no device has given ip: %s", ip))
}


// 得到所有具有IPv4地址的网卡
func GetNetCardsWithIPv4Addr() ([]NetCard, error) {
	ncs := make([]NetCard, 0, 4)
	devices, err := pcap.FindAllDevs()
	if err != nil {
		return nil, errors.New("find device error")
	}
	for _, d := range devices {
		var nc NetCard
		ip, e := findDeviceIpv4(d)
		if e != nil {
			continue
		}
		mac, e := findMacAddrByIp(ip)
		if e != nil {
			continue
		}
		nc.ipv4 = ip
		nc.mac = mac
		nc.name = d.Name
		ncs = append(ncs, nc)
	}
	return ncs, nil
}


  • netcard.go
    网卡结构体,包含:名称、IPv4地址和MAC地址
package internal

type NetCard struct {
	name string
	ipv4 string
	mac  string
}

func (nc *NetCard)GetName() string {
	return nc.name
}

func (nc *NetCard)GetIPv4Addr() string {
	return nc.ipv4
}

func (nc *NetCard)GetMacAddr() string {
	return nc.mac
}
  • analyzer.go
    监测的主要逻辑在这里实现。
package internal

import (
	"context"
	"fmt"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"os"
	"time"
)

type Analyzer struct {
	Nc                 *NetCard  // 网卡
	Running            bool      // 是否在运行
	stop               chan bool // 停止信号
	allCanceled        chan bool // 所有goroutines是否中止
	downStreamDataSize int       // 单位时间内下行的总字节数
	upStreamDataSize   int       // 单位时间内上行的总字节数
	upSpeed            float32   // 转化后的上行速度
	downSpeed          float32   // 转化后的下行速度
}

func (anl *Analyzer) Init() {
	anl.stop = make(chan bool)
	anl.allCanceled = make(chan bool)
	anl.Running = false
}

func (anl *Analyzer) Capture() {
	anl.Running = true
	handler, err := pcap.OpenLive(anl.Nc.name, 1024, true, 30*time.Second)
	if err != nil {
		panic(err)
	}
	defer handler.Close()
	ctx, cancel := context.WithCancel(context.Background())
	// 开启子线程,每一秒计算一次该秒内的数据包大小平均值,并将下载、上传总量置零
	go anl.monitor(ctx)

	// 开始抓包
	packetSource := gopacket.NewPacketSource(handler, handler.LinkType())
	// 这种方式从channel中读数据很有意思
	for packet := range packetSource.Packets() {
		// 只获取以太网帧
		ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
		if ethernetLayer != nil {
			ethernet := ethernetLayer.(*layers.Ethernet)
			// 如果封包的目的MAC是本机则表示是下行的数据包,否则为上行
			if ethernet.DstMAC.String() == anl.Nc.mac {
				anl.downStreamDataSize += len(packet.Data()) // 统计下行封包总大小
			} else {
				anl.upStreamDataSize += len(packet.Data()) // 统计上行封包总大小
			}
		}
		select {
		case <-anl.stop:
			cancel()
			return
		default:
			continue
		}
	}
}

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

		time.Sleep(1 * time.Second)
		select {
		case <-ctx.Done():
			anl.Running = false
			anl.allCanceled <- true
			return
		default:
			continue
		}
	}
}

func (anl *Analyzer) GetUpSpeed() float32 {
	return anl.upSpeed
}

func (anl *Analyzer) GetDownSpeed() float32 {
	return anl.downSpeed
}

func (anl *Analyzer) Stop() {
	anl.stop <- true
	<-anl.allCanceled
}

  • main.go
    主函数,生成对象后,直接调用Run()方法即可。
package main

import (
	"FyneNet/ui"
)

func main() {
	ent := &ui.AppMainWindow{}
	ent.Run()
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Golang的GUI库-Fyne使用案例-网卡速度监测 的相关文章

  • tp6实现商城后台登录功能

    后台登录逻辑 1 没有登录状态 xff0c 访问后台页面都应该跳转到登录页 已经登录状态 xff1a 1 访问登录页面 xff0c 应该跳转到后台首页 解决方案 xff1a 后台登录拦截 xff1a 1 传统common base php控
  • Jenkins环境部署

    简介 Jenkins是一款开源 CI amp CD 软件 xff0c 用于自动化各种任务 xff0c 包括构建 测试和部署软件 Jenkins 支持各种运行方式 xff0c 可通过系统包 Docker 或者通过一个独立的 Java 程序 J
  • Nginx中Return指令使用

    Return指令使用 1 返回状态码 return 404 location test set name 39 user123 39 return 404 name curl i http 192 168 56 10 test 2 返回字符
  • MySQL触发器

    介绍 触发器是与表有关的数据库对象 xff0c 指在 insert update delete 之前或之后 xff0c 触发并执行触发器中定义的SQL语句集合 触发器的这种特性可以协助应用在数据库端确保数据的完整性 日志记录 数据校验等操作
  • 制作composer包提供sdk扩展

    目录 1 初始化包 2 将代码推送到github远程仓库 3 为写好扩展包打上tag标签标记当前代码版本 4 将包发布到包管理平台 初始化包 xff0c 生成 Creates a basic composer json file in cu
  • Go环境搭建与IDE开发工具配置

    安装Go语言编译器 Go语言编译器 61 编译器 61 将源代码编译为可执行程序 61 源代码 61 程序员使用高级语言所书写的代码文件 61 高级语言 61 c c 43 43 go 61 机器语言 61 0和1构成 xff0c 机器能直
  • 计算机中的速率、带宽、时延、利用率解读

    计算机网络的性能一般是指它的几个重要的性能指标 但除了这些重要的性能指标外 xff0c 还有一些非性能特征 xff08 nonperformance characteristics xff09 也对计算机网络的性能有很大的影响 那么 xff
  • 无人机原理

    文章目录 1基本运动2为何无人机螺旋桨多为偶数3机器人的运动控制4加速度计5陀螺仪6姿态角解算欧拉角的平面换算 1基本运动 为了平衡扭矩 xff0c 上图中1 3逆时针转动 xff0c 电机2 4顺时针转动进行反扭矩对消 上升 F1 61
  • 新浪微博PC端模拟登陆

    点击上方 程序人生 xff0c 选择 置顶公众号 第一时间关注程序猿 xff08 媛 xff09 身边的故事 图 xff1a Fantastic Beasts and Where to Find Them 投稿 星星在线 简介 一个从妹子图
  • win10下 docker build 报错 “Failed to fetch xxxxxxxx Temporary failure resolving 'archive.ubuntu.com'”

    错误信息 在docker中搭建禅道的时候执行docker build t zentao 命令后执行到apt get update amp amp apt get install指令的时候报错 xff0c 错误如下 xff1a W Faile
  • docker-ros-gui

    主机环境 系统 Ubuntu 16 04 1 安装docker 1 1 在线安装 物理机 默认使用当前最新版本 1 更新apt包索引 xff1a sudo apt get update 2 安装以下包以使apt可以通过HTTPS使用存储库
  • halcon opencv 图像处理面试指南

    珠海某上市公司算法总监 xff1a 1 相机标定的原理与坐标系之间的转换 xff0c 如何转换 2 激光三角原理 xff0c 如何搭建 3 测量拟合的过程中有哪些算子 xff0c 原理什么 xff0c 接着问5 xff0c 跌代多少次 xf
  • 【3D计算机视觉】从PointNet到PointNet++理论及pytorch代码

    从PointNet到PointNet 43 43 理论及代码详解 1 点云是什么1 1 三维数据的表现形式1 2 为什么使用点云1 3 点云上以往的相关工作 2 PointNet2 1 基于点云的置换不变性2 1 1 由对称函数到Point
  • 在ubuntu上安装mavlink-router

    版本说明 xff1a ubuntu 20 0 04 mavlink router 2 为了安装mavlink router 需要预先安装一些依赖库 xff1a autoconf libtool python future python3 f
  • PX4多机仿真(gazebo)

    版本说明 xff1a 操作系统 宿主机 xff08 或开发机 xff09 xff1a ubuntu server 20 0 04 thinkpad 目标机 xff08 机载计算机 xff09 xff1a ubuntu server 20 0
  • 为PX4建立J-Link Eclipse交叉调试环境

    版本说明 1 自驾仪 xff1a Holybro的Pixhawk 4 2 PX4版本 xff1a 1 12 3 宿主机 xff1a Thinkpad笔记本 43 Ubuntu 20 04 2 LTS 4 Eclipse xff1a 2022
  • 为Ubuntu增加swap空间并调整性能

    版本说明 xff1a Raspberry Pi 3B 43 Ubuntu 20 04 server 本文参考翻译自这篇文章 SWAP文件的设置 Swap是硬盘存储的一部分 xff0c 它被预留给操作系统 当RAM中不再有足够的空间来保存正在
  • PX4的中间件Fast DDS

    本文参考官网 xff1a RTPS DDS Interface PX4 Fast RTPS DDS Bridge PX4 User Guide 版本说明 xff1a PX4固件版本 xff1a 1 13 0d 自驾仪 xff1a PIXHA
  • 为PX4建立ROS2环境,x86与树梅派

    本文参考官网 xff1a a href https docs px4 io master en ros ros2 comm html title htt htt a ps docs px4 io master en ros ros2 com
  • Python读写Excel表格,就是这么简单粗暴又好用

    点击上方 程序人生 xff0c 选择 置顶公众号 第一时间关注程序猿 xff08 媛 xff09 身边的故事 图片源自unsplash 作者 丁彦军 如需转载 xff0c 请联系原作者授权 最近在做一些数据处理和计算的工作 xff0c 因为

随机推荐