GolangでのUDP処理メモ

More than 1 year has passed since last update.


疑問

Go で UDP 送信する際、DialUDP() でUDPConnを作って、send()するが、DialUDP()って実際何をしているのだろうか?

func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error)

socket作る?bindもしちゃう?

システムコールするものなので、大量に呼び出すものではないよね?

というあたりを確認したい。


ソース追ってみる

DialUDP() から追っかける。


net/udpsock.go

func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error) {

switch network {
case "udp", "udp4", "udp6":
default:
return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: UnknownNetworkError(network)}
}
if raddr == nil {
return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress}
}
c, err := dialUDP(context.Background(), network, laddr, raddr)
if err != nil {
return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err}
}
return c, nil
}

DialUDP()dialUDP() を呼ぶ。


net/udpsock_posix.go

func dialUDP(ctx context.Context, net string, laddr, raddr *UDPAddr) (*UDPConn, error) {

fd, err := internetSocket(ctx, net, laddr, raddr, syscall.SOCK_DGRAM, 0, "dial")
if err != nil {
return nil, err
}
return newUDPConn(fd), nil
}

dialUDP()internetSocket() を呼ぶ。その際、sotype に SOCK_DGRAM を、proto に 0 を、mode に "dial" を指定。mode は後で family 判定に使っているが、"listen" 以外は無視されているな。


net/ipsock_posix.go

func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string) (fd *netFD, err error) {

if (runtime.GOOS == "windows" || runtime.GOOS == "openbsd" || runtime.GOOS == "nacl") && mode == "dial" && raddr.isWildcard() {
raddr = raddr.toLocal(net)
}
family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr)
}

internetSocket() は family を判定後、socket() を呼ぶ。family は AF_INET か AF_INET6。


net/sock_posix.go

func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr) (fd *netFD, err error) {

s, err := sysSocket(family, sotype, proto)

if fd, err = newFD(s, family, sotype, net); err != nil {

if err := fd.dial(ctx, laddr, raddr); err != nil {

}


socket() は結局 socket(2) システムコールを呼んだ後、fd.dial() を呼ぶ。


sock_posix.go

func (fd *netFD) dial(ctx context.Context, laddr, raddr sockaddr) error {

var err error
var lsa syscall.Sockaddr
if laddr != nil {
if lsa, err = laddr.sockaddr(fd.family); err != nil {
return err
} else if lsa != nil {
if err := syscall.Bind(fd.pfd.Sysfd, lsa); err != nil {
return os.NewSyscallError("bind", err)
}
}
}


fd.dial() 中で bind(2) システムコールを呼んでいる。ここで local address を使っているので、ソースが同じでデスティネーションの違うUDPDial()を呼ぶとエラーになるのだなぁ。なるほど。