GO实验(3)net包阅读(Listen/Dial)

前文

网络协议概要

版本

go1.18
CentOs 7

1.Listen函数

demo

func main() {
    l, _ := net.Listen("tcp", ":8080")
    fmt.Println(l)
}

dlv进入打断点

dlv debug main.go
b net.Listen
c
image.png

进入ListenConfig.Listen

b net.ListenConfig.Listen
c
print lc //*net.ListenConfig {Control: nil, KeepAlive: 0}

ListenConfig.Listen函数

func (lc *ListenConfig) Listen(ctx context.Context, network, address string) (Listener, error) {
//解析地址存入addrs数组addrList-[ TCPAddr{} ]
        addrs, err := DefaultResolver.resolveAddrList(ctx, "listen", network, address, nil)
        if err != nil {
                return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: err}
        }
        sl := &sysListener{
                ListenConfig: *lc,
                network:      network,
                address:      address,
        }
        var l Listener
//isIPV4是一个返回布尔类型的函数
//first轮询数组中的成员判断是否为IPV4地址,并返回满足条件的第一个成员(实际上只有一个成员)
// la net.Addr / *TCPAddr
        la := addrs.first(isIPv4)
//根据不同的地址族生成对应的socket句柄
//且该socket已经准备好被连接
        switch la := la.(type) {
        case *TCPAddr:
                l, err = sl.listenTCP(ctx, la)
        case *UnixAddr:
                l, err = sl.listenUnix(ctx, la)
        default:
                return nil, &OpError{Op: "listen", Net: sl.network, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: address}}
        }
        if err != nil {
                return nil, &OpError{Op: "listen", Net: sl.network, Source: nil, Addr: la, Err: err} // l is non-nil interface containing nil pointer
        }
        return l, nil
}

1.1关于DefaultResolver

print DefaultResolver //实际是一个net.Resolver这是1.18特有的结构体

给net.Resolver.resolveAddrList打断点进入函数,可以看到这个函数实际上是使用Resolver.interAddrList解析addr(host:ip),存入指定结构体(TCP就是TCPAddr{IP,Port,Zone}结构体)并保存于addrList类型中

type addrList []Addr

net.addrList len: 1, cap: 1, [
    *net.TCPAddr {
        IP: net.IP len: 0, cap: 0, nil,
        Port: 8080,
        Zone: "",},
]

1.2 listenTCP

函数很简单,调用ipsock_posix.go的internetSocket函数,返回socket的fd

func (sl *sysListener) listenTCP(ctx context.Context, laddr *TCPAddr) (*TCPListener, error) {
        fd, err := internetSocket(ctx, sl.network, laddr, nil, syscall.SOCK_STREAM, 0, "listen", sl.ListenConfig.Control)
        if err != nil {
                return nil, err
        }
        return &TCPListener{fd: fd, lc: sl.ListenConfig}, nil
}

internetSocket函数十分复杂,里面分别调用了favoriteAddrFamily确定地址族,net.socket调用syscallsocket返回一个绑定好host:ip且可以准备监听的socket的文件句柄

小结

net.Listen

2.Dial函数

demo

func main() {
    l, _ := net.Dial("tcp", ":8080")
    fmt.Println(l)
}

image.png

image.png

实际上是调用Dialer结构体的DialContext方法


image.png

DialContext首先是设置超时时间
image.png

resolver函数返回一个net.Resolver结构体,并调用resloveAddrList函数,这个函数上面Listen有分析过,是解析host:port生成 []AddrList{} 数组,实际上输入的参数是tcp,成员是TCPAddr(AddrList接口的实现结构体之一)
image.png

partition函数与上面的addrList.first函数很相似,执行传入的函数,轮询返回满足条件的数组
image.png

通过dialSerial函数调用dialSingle函数,根据地址族调用不同的生成连接函数(dialTCP,dialUDP,dialIP和dialUnix),这里我们调用dialTCP,他内部是对doDialTCP的再包装,接着进入该函数
image.png

当看到internetSocket我们就不陌生了,他底层会调用syscall.socket来创建套接字,但此时我们不需要“Listen”状态的套接字,需要“Dial”的套接字,即可连接的
至此连接connect套接字已经创建

小结

net.Dial.png

3.Accept函数

与多路复用有关,先放着,之后回头来看

你可能感兴趣的:(GO实验(3)net包阅读(Listen/Dial))