golang实现D-Bus原生绑定的插件库dbus的使用

golang实现D-Bus原生绑定的插件库dbus的使用

简介

dbus是一个简单的库,实现了D-Bus消息总线系统的原生Go客户端绑定。

Build Status

特性

  • 完整原生实现D-Bus消息协议
  • Go风格的API(使用通道处理信号/异步方法调用,Goroutine安全的连接)
  • 包含帮助处理自省/属性接口的子包

安装

该包需要Go 1.20或更高版本。可以通过运行以下命令安装:

go get github.com/godbus/dbus/v5

使用示例

基础连接示例

package main

import (
	"fmt"
	"github.com/godbus/dbus/v5"
)

func main() {
	// 连接到系统总线
	conn, err := dbus.SystemBus()
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	// 获取DBus守护进程的版本
	var version string
	obj := conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
	err = obj.Call("org.freedesktop.DBus.GetId", 0).Store(&version)
	if err != nil {
		panic(err)
	}

	fmt.Println("DBus守护进程ID:", version)
}

监听信号示例

package main

import (
	"fmt"
	"github.com/godbus/dbus/v5"
)

func main() {
	// 连接到会话总线
	conn, err := dbus.SessionBus()
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	// 添加匹配规则来监听NameOwnerChanged信号
	conn.BusObject().Call(
		"org.freedesktop.DBus.AddMatch", 0,
		"type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'",
	)

	// 创建信号通道
	c := make(chan *dbus.Signal, 10)
	conn.Signal(c)

	// 处理信号
	for v := range c {
		fmt.Println("收到信号:", v)
		if v.Name == "org.freedesktop.DBus.NameOwnerChanged" {
			name := v.Body[0].(string)
			oldOwner := v.Body[1].(string)
			newOwner := v.Body[2].(string)
			fmt.Printf("名称所有者变更: %s (旧: %s, 新: %s)\n", name, oldOwner, newOwner)
		}
	}
}

导出对象和方法示例

package main

import (
	"fmt"
	"github.com/godbus/dbus/v5"
	"os"
)

type example struct{}

func (e example) Hello(name string) (string, *dbus.Error) {
	return fmt.Sprintf("Hello %s!", name), nil
}

func main() {
	conn, err := dbus.SessionBus()
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	// 请求一个总线名称
	reply, err := conn.RequestName("com.example.Test", dbus.NameFlagDoNotQueue)
	if err != nil {
		panic(err)
	}
	if reply != dbus.RequestNameReplyPrimaryOwner {
		fmt.Fprintln(os.Stderr, "名称已被占用")
		os.Exit(1)
	}

	// 导出对象
	err = conn.Export(example{}, "/com/example/Test", "com.example.Test")
	if err != nil {
		panic(err)
	}

	fmt.Println("服务已启动,按Ctrl+C退出...")
	select {}
}

使用godbus的项目

  • fyne - 受Material Design启发的跨平台Go GUI
  • fynedesk - 使用Fyne的Linux/Unix完整桌面环境
  • go-bluetooth - 通过bluez dbus API提供的蓝牙客户端
  • iwd - 互联网无线守护程序"iwd"的Go绑定
  • notify - 通过dbus提供桌面通知的库
  • playerbm - 媒体播放器的书签工具
  • rpic - 用于管理Raspberry Pi的轻量级Web应用和RESTful API

注意事项

请注意,目前API被认为是不稳定的,可能会在没有进一步通知的情况下更改。

许可证

go.dbus在简化BSD许可证下可用。


更多关于golang实现D-Bus原生绑定的插件库dbus的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现D-Bus原生绑定的插件库dbus的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang D-Bus 原生绑定插件库 dbus 使用指南

D-Bus 是 Linux 系统上广泛使用的进程间通信(IPC)机制,Go 语言可以通过 github.com/godbus/dbus 库实现原生 D-Bus 绑定。下面我将详细介绍如何使用这个库。

安装

首先安装 dbus 库:

go get github.com/godbus/dbus/v5

基本概念

D-Bus 中有几个核心概念:

  • 总线(Bus):系统总线(system bus)和会话总线(session bus)
  • 对象路径(Object Path):类似于文件路径的标识符,如 /org/freedesktop/DBus
  • 接口(Interface):定义了一组方法和信号
  • 服务名(Service Name):如 org.freedesktop.DBus

连接到 D-Bus

package main

import (
	"github.com/godbus/dbus/v5"
	"log"
)

func main() {
	// 连接到会话总线
	conn, err := dbus.SessionBus()
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	// 连接到系统总线
	// conn, err := dbus.SystemBus()
}

调用 D-Bus 方法

func callMethod(conn *dbus.Conn) {
	// 创建一个 D-Bus 对象
	obj := conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")

	// 调用 ListNames 方法
	var result []string
	err := obj.Call("org.freedesktop.DBus.ListNames", 0).Store(&result)
	if err != nil {
		log.Fatal(err)
	}

	log.Println("D-Bus 服务列表:")
	for _, name := range result {
		log.Println(name)
	}
}

导出 D-Bus 服务

package main

import (
	"github.com/godbus/dbus/v5"
	"log"
)

const (
	objectPath = "/com/example/Hello"
	interfaceName = "com.example.Hello"
	serviceName = "com.example.HelloService"
)

// Hello 结构体实现 D-Bus 接口
type Hello struct{}

// SayHello 是要导出的方法
func (h *Hello) SayHello(name string) (string, *dbus.Error) {
	return "Hello " + name, nil
}

func exportService(conn *dbus.Conn) {
	// 请求服务名
	reply, err := conn.RequestName(serviceName, dbus.NameFlagDoNotQueue)
	if err != nil {
		log.Fatal(err)
	}
	if reply != dbus.RequestNameReplyPrimaryOwner {
		log.Fatal("名称已被占用")
	}

	// 导出接口
	err = conn.Export(&Hello{}, objectPath, interfaceName)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("已导出服务 %s 在路径 %s", serviceName, objectPath)
}

func main() {
	conn, err := dbus.SessionBus()
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	exportService(conn)
	
	// 保持程序运行
	select {}
}

监听信号

func watchSignals(conn *dbus.Conn) {
	// 添加匹配规则来监听 NameOwnerChanged 信号
	err := conn.AddMatchSignal(
		dbus.WithMatchInterface("org.freedesktop.DBus"),
		dbus.WithMatchMember("NameOwnerChanged"),
	)
	if err != nil {
		log.Fatal(err)
	}

	c := make(chan *dbus.Signal, 10)
	conn.Signal(c)

	for signal := range c {
		if signal.Name == "org.freedesktop.DBus.NameOwnerChanged" {
			name := signal.Body[0].(string)
			oldOwner := signal.Body[1].(string)
			newOwner := signal.Body[2].(string)
			log.Printf("NameOwnerChanged: %s (old: %s, new: %s)", name, oldOwner, newOwner)
		}
	}
}

属性访问

type Properties struct {
	Value int `dbus:"value"`
}

func propertyExample(conn *dbus.Conn) {
	props := &Properties{Value: 42}
	
	// 导出属性接口
	err := conn.Export(props, "/com/example/Props", "org.freedesktop.DBus.Properties")
	if err != nil {
		log.Fatal(err)
	}

	// 客户端获取属性
	obj := conn.Object("com.example.PropsService", "/com/example/Props")
	var value int
	err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, "com.example.Props", "value").Store(&value)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("属性值: %d", value)
}

注意事项

  1. D-Bus 方法调用是同步的,如果需要异步调用,可以使用 goroutine
  2. 导出的方法必须返回 *dbus.Error 作为最后一个返回值
  3. 方法参数和返回值必须是可以被 D-Bus 类型系统表示的类型
  4. 在导出服务前应该先检查服务名是否可用

完整示例

下面是一个完整的服务端和客户端示例:

服务端:

package main

import (
	"github.com/godbus/dbus/v5"
	"log"
)

const (
	objectPath    = "/com/example/Calculator"
	interfaceName = "com.example.Calculator"
	serviceName   = "com.example.CalculatorService"
)

type Calculator struct{}

func (c *Calculator) Add(a, b int32) (int32, *dbus.Error) {
	return a + b, nil
}

func (c *Calculator) Subtract(a, b int32) (int32, *dbus.Error) {
	return a - b, nil
}

func main() {
	conn, err := dbus.SessionBus()
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	reply, err := conn.RequestName(serviceName, dbus.NameFlagDoNotQueue)
	if err != nil {
		log.Fatal(err)
	}
	if reply != dbus.RequestNameReplyPrimaryOwner {
		log.Fatal("名称已被占用")
	}

	err = conn.Export(&Calculator{}, objectPath, interfaceName)
	if err != nil {
		log.Fatal(err)
	}

	log.Println("计算器服务已启动...")
	select {}
}

客户端:

package main

import (
	"github.com/godbus/dbus/v5"
	"log"
)

func main() {
	conn, err := dbus.SessionBus()
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	obj := conn.Object("com.example.CalculatorService", "/com/example/Calculator")

	var result int32
	err = obj.Call("com.example.Calculator.Add", 0, int32(10), int32(5)).Store(&result)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("10 + 5 = %d", result)

	err = obj.Call("com.example.Calculator.Subtract", 0, int32(10), int32(5)).Store(&result)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("10 - 5 = %d", result)
}

通过以上示例,你可以实现基本的 D-Bus 通信功能。godbus/dbus 库还支持更高级的功能如属性访问、信号监听等,可以根据需要进一步探索。

回到顶部