golang实现D-Bus原生绑定的插件库dbus的使用
golang实现D-Bus原生绑定的插件库dbus的使用
简介
dbus是一个简单的库,实现了D-Bus消息总线系统的原生Go客户端绑定。
特性
- 完整原生实现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)
}
注意事项
- D-Bus 方法调用是同步的,如果需要异步调用,可以使用 goroutine
- 导出的方法必须返回
*dbus.Error
作为最后一个返回值 - 方法参数和返回值必须是可以被 D-Bus 类型系统表示的类型
- 在导出服务前应该先检查服务名是否可用
完整示例
下面是一个完整的服务端和客户端示例:
服务端:
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 库还支持更高级的功能如属性访问、信号监听等,可以根据需要进一步探索。