疑問
Go で UDP 送信する際、DialUDP() でUDPConnを作って、send()するが、DialUDP()って実際何をしているのだろうか?
func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error)
socket作る?bindもしちゃう?
システムコールするものなので、大量に呼び出すものではないよね?
というあたりを確認したい。
ソース追ってみる
DialUDP()
から追っかける。
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()
を呼ぶ。
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" 以外は無視されているな。
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。
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()
を呼ぶ。
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()を呼ぶとエラーになるのだなぁ。なるほど。