linux - syscall.Sockaddr 类型断言

标签 linux sockets go type-assertion

我正在打开一个 Linux 数据包套接字并尝试将接收到的数据包读入一个结构中:

type msg struct {
    n, oobn, flags int
    p, oob []byte
    from syscall.Sockaddr
}

socket, err := syscall.Socket(AF_PACKET, SOCK_RAW, ETH_P_ALL)

pkt := new(msg)
pkt.p = make([]byte, 1500)
pkt.oob = make([]byte, 1500)

pkt.n, pkt.oobn, pkt.flags, pkt.from, _ = syscall.Recvmsg(socket, pkt.p, pkt.oob, 0)

根据文档 (http://golang.org/pkg/syscall/#Recvmsg) Recvmsg() 将 msghdr 作为 syscall.Sockaddr 返回,并且我在上面概述的代码片段有效。

打印出 pkt.from 结构成员,我可以在 Sockaddr 接口(interface)中看到值:

fmt.Printf("%+v\n", pkt.from)

>>> &{Protocol:8 Ifindex:3 Hatype:1 Pkttype:0 Halen:6 Addr:[0 0 36 205 126 213 0 0] raw:{Family:0 Protocol:0 Ifindex:0 Hatype:0 Pkttype:0 Halen:0 Addr:[0 0 0 0 0 0 0 0]}}

但是,如果我尝试访问它们,则会出现错误:

fmt.Println(pkt.from.Ifindex)

>>> pkt.from.Ifindex undefined (type syscall.Sockaddr has no field or method Ifindex)

通过 reflect.TypeOf(pkt.from) 我发现它是 *syscall.SockaddrLinklayer 类型。当 Recvmsg 尝试执行分配时,尝试将我的 msg 结构成员更改为该类型失败,因为它不是 syscall.Sockaddr 类型。

我能够使用类型断言:

bar := pkt.from.(*syscall.SockaddrLinklayer)
fmt.Println(bar.Ifindex)

>>> 2

我是 Go 的新手;这是我的第一门静态类型语言,所以我不明白 Recvmsg 函数是如何需要 syscall.Sockaddr 但是 返回一个 *syscall.SockaddrLinklayer?我显然遗漏了一些非常基本的东西。另外,使用类型断言是执行此操作的正确方法吗?感觉确实不太对……但我真的没有资格做出这样的判断!

最佳答案

Sockaddr 是一个接口(interface),因此 Recvmsg 可以返回满足该接口(interface)的不同类型。

有关接口(interface)的更多详细信息,请查看 Effective Go .

你应该检查断言是否有效,这样你就不会以运行时错误结束,例如:

bar, ok := pkt.from.(*syscall.SockaddrLinklayer) 
if !ok {
    //log error and break out of the loop
}
stuffWith(bar)

关于linux - syscall.Sockaddr 类型断言,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25654928/

相关文章:

android, libaums : java. io.IOException: 无法写入设备

sockets - 通过使用监听多个 UDP 套接字来避免数据包类型检查

algorithm - 找到矩阵中的最短路径总和。对于这种情况,Dijkstra 不是最优的吗?

pointers - 如何通过 Go 中的 map 指针更改/访问 map 实例的值?

mysql - MySQL CentOS 6 无法设置根密码

linux - 如何在 Debian Stretch 上安装 3.7 版的 puppet?

python - 在 GCE 上运行 selenium(chrome 驱动程序)时出错?

c - Sockets-客户端双重打印

go - 重新连接外部数据库的好模式是什么?

fork 后调用信号