2014-09-03 86 views
0

我打開一個Linux包套接字,並嘗試閱讀收到的數據包成一個結構:syscall.Sockaddr類型斷言

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/#RecvmsgRecvmsg()返回指向msghdr爲syscall.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]}} 

,如果我嘗試訪問它們,我得到一個錯誤:

打印出pkt.from結構成員,我可以看到的值在Sockaddr接口

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 

我很新的地方;這是我的第一個靜態類型的語言,所以我不明白如何Recvmsg func是需要 a syscall.Sockaddr返回*syscall.SockaddrLinklayer?我顯然錯過了一些非常基本的東西。此外,正在使用類型斷言正確的方式來做到這一點?這並不是真的感覺正確......但我並不真的有資格做出這樣的判斷!

回答

2

Sockaddr是一個接口,因此Recvmsg可以返回滿足該接口的不同類型。

有關接口的更多詳細信息,請參閱Effective Go

你應該檢查斷言是否有效,所以你不會有一個運行時錯誤結束了,例如:

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

確定 - 這是有道理的。使用類型斷言「正確」的方式來讀取這些數據? – tMC 2014-09-03 23:22:00

+0

@tMC是的,但你必須確保它是有效的,我添加了一個小例子。 – OneOfOne 2014-09-03 23:27:01