syscall.Sockaddr 类型断言

huangapple go评论75阅读模式
英文:

syscall.Sockaddr 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接口中的值:

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。尝试将我的msg结构体成员更改为该类型会失败,因为Recvmsg尝试进行赋值时,它不是syscall.Sockaddr类型。

我可以使用类型断言来解决问题:

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

>>> 2

我对Go语言非常陌生;这是我第一次接触静态类型语言,所以我不理解为什么Recvmsg函数要求一个syscall.Sockaddr类型,但返回一个*syscall.SockaddrLinklayer类型?我显然漏掉了一些非常基本的东西。另外,使用类型断言的方式是否正确?它感觉不太对...但我没有资格做出这样的判断!

英文:

I am opening a Linux packet socket and trying to read received packets into a struct:

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)

Per the documentation (http://golang.org/pkg/syscall/#Recvmsg) Recvmsg() returns the msghdr as a syscall.Sockaddr and the code pieces I've outlined above works.

Printing out the pkt.from struct member, I can see the values in the 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]}}

However, if I try to access them, I get an error:

fmt.Println(pkt.from.Ifindex)

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

Via reflect.TypeOf(pkt.from) I found it was of type *syscall.SockaddrLinklayer. Trying to change my msg struct member to that type fails when Recvmsg tries to do the assignment since it not of type syscall.Sockaddr.

I was able to use a type assertion:

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

>>> 2

I'm very new to Go; it is my first statically typed language so I don't understand how the Recvmsg func is requiring a syscall.Sockaddr but returning a *syscall.SockaddrLinklayer? I'm clearly missing something very fundamental. Also, is using the type assertion the correct way to do this? It doesn't really feel right... but I'm not really qualified to make such judgments!

答案1

得分: 3

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

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

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

bar, ok := pkt.from.(*syscall.SockaddrLinklayer) 
if !ok {
	//记录错误并跳出循环
}
使用bar进行操作
英文:

Sockaddr is an interface, so Recvmsg can return different types that fulfill that interface.

For more details about interfaces check Effective Go.

You should check if the assertion is valid or not so you wouldn't end up with a runtime error, for example:

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

huangapple
  • 本文由 发表于 2014年9月4日 07:02:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/25654928.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定