这里主要介绍网络包内主要的几个类型。
net包是提供了底层的网络接口的,但是平时我们需要就是两个主要的接口,Listener和Conn。

1. Conn

Conn是一个基本的接口类型,以数据流为向导的网络连接接口。注意他是接口类型,不需要我们来手动构造实现Conn接口。
生成满足Conn接口的类型的函数

func Dial(network, address string) (Conn, error)
func DialTimeout(network, address string, timeout time.Duration) (Conn, error)
func FileConn(f *os.File) (c Conn, err error)
func Pipe() (Conn, Conn)
"tcp""tcp4" (IPv4-only)"tcp6" (IPv6-only)"udp""udp4" (IPv4-only)"udp6" (IPv6-only)"ip""ip4" (IPv4-only)"ip6" (IPv6-only)"unix""unixgram""unixpacket"
Dial("ip4:1", "127.0.0.1")
Dial("ip6:ospf", "::1")

但是这样做之后,必须自己实现里面的IP数据包的内容,比如你协议使用TCP,那么TCP的字段都需要你手动来构造。通常我们直接使用TCP或UDP网络就可以了。

第二个参数是网络地址,在IP网络内,只用给出网络ip地址就可以了,在tcp或udp网络内,需要给出网络端口,但是如果你给出上层网络协议也是可以的,如

Dial("tcp", "12.34.56.78:80")
Dial("tcp", "google.com:http")
Dial("tcp", "[2001:db8::1]:http")   //注意ipv6,必须使用[]进行包裹
Dial("tcp", "[fe80::1%lo0]:80")     //[ipv6-host%zone]:80

Conn是中间的网络接口,实际上是Dial()会判断所属的网络类型,如果是TCP网络,那么它会返回TCPConn,如果是UDP网络,则会返回UDPConn,如果是IP网络,则返回IPConn。

由于获得的是Conn接口变量,net包内没有以Conn作为参数的函数,所以获得Conn之后,只能调用他的接口函数。

conn.Write(data)
conn.Read(data)

其实最终生成满足这些Conn接口的变量类型其实是一个内部包含socket的结构体。它的真是类型其实是conn,但是只是在net包内部使用,对外使用的只有Conn接口类型变量和下面会提到的内嵌它的TCPConn这类类型变量。

1.1 TCPConn

TCPConn是在TCP网络上Conn接口类型的一种实现。注意他是实实在在的结构体类型

type TCPConn struct {
    conn    //这个conn是内部的一个Conn接口类型的实现
}

这里提一下关于结构体内嵌接口(a struct with embedded an interface),其实含义比较简单,如果你通过传入一个非nil的接口变量来构造这个Struct,那么这个Struct就内嵌了满足这个接口的变量。但是也只能访问他的接口的函数。我们可以通过Struct来重新定义函数来覆盖原来的接口内的函数。比较明显的例子可以看sort.Reverse()。

TCPConn额外实现了一些便利的操作函数与TCP相关的函数。通常情况下,我们使用TCPConn已经满足了所有的TCP的连接需求。
比如ReadFrom()函数,示例如下

msg := bytes.NewBufferString("woqunidaye")  //可以创建一个Buffer变量
_, err = tcpConn.ReadFrom(msg)  //Buffer变量是实现了io.Reader的接口

1.2 UDPConn

与TCPConn同理,只是对conn的一个封装,并添加了许多额外的函数。

1.3 总结

在Conn里,其实扮演了一个连接通道的角色,类似于C语音的里的socket。在net包里,有实现Conn接口的类型有TCPConnUDPConnUnixConnIPConn这4个结构体类型。其实他们都是内部内嵌了一个conn结构体(它其实包含了socket的文件描述符),我们可以直接使用net.Dial()来获得Conn接口变量,也可以使用各自的如net.DialTCP()函数来获得TCPConn类型变量,我们也可以通过对Conn接口变量进行断言,来转化成如TCPConn这些实际的类型。

1.4 示例

func dialInTCP() {
    conn, err := net.Dial("tcp", "127.0.0.1:8080")  //开启TCP连接端口
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()
    msg := []byte("woqunidaye")
    if tconn, ok := conn.(*net.TCPConn); ok {   //这里可以断言,因为是tcp连接,其实值类型就是TCPConn
        fmt.Println("assert success")
        tconn.CloseRead()   //尝试调用TCPConn的函数
        _, err = tconn.Write(msg)
        if err != nil {
            log.Fatal(err)
        }
    } else {
        _, err = conn.Write(msg)
        if err != nil {
            log.Fatal(err)
        }
    }
}

func listenTCP() {
    l, err := net.Listen("tcp", ":8080")    //监听TCP,8080端口
    if err != nil {
        log.Fatal(err)
    }
    defer l.Close()
    for {
        conn, err := l.Accept() //接受连接,当上面Dial的时候,这就会接受连接
        if err != nil {
            log.Fatal(err)
        }
        go func(c net.Conn) {   //使用goroutine,并发
            buf := make([]byte, 256)    //接受字段
            _, err = c.Read(buf)    //读取内容
            if err != nil {
                log.Fatal(err)
            }
            fmt.Println(string(buf))
            c.Close()
        }(conn)
    }
}

2. PacketConn

ReadFromWriteToReadWrite

这里有个大坑在PacketConn里,PacketConn的ReadFrom(b []byte)函数指的是从Socket接收数据,写入到b字节切片中。刚好跟io.ReaderFrom接口的含义相反。相对应的WriteTo(b []byte, addr *UDPAddr)是把b的数据写入Socket,发送到指定地址。而TCPConn实现的是io.ReaderFrom接口,所以刚好和UDP的同名函数作用相反

这个以后再研究吧,todo

这里提示一下IPConn下,PacketConn实现的ReadFrom()和Conn实现的Read()的区别。Read会将整个IP包的内容读进来,而ReadFrom只会把IP包之上的内容读进来。比如在ICMP数据包。


3. Listener

Listener是流式协议网络的监听接口。它也是通过给定的函数来进行创建返回获得。满足Listener接口的类型有TCPListenerUnixListener
net包中创建Listener接口的函数有两个

func FileListener(f *os.File) (ln Listener, err error)  //这个函数用来拷贝Listener用的
func Listen(net, laddr string) (Listener, error)    //创建Listener的函数
tcptcp4tcp6unixunixpacketDial()

3.1 TCPListener

ListenTCP()
func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) 

3.2 UnixListener

3.3 UDP的listener

Accept()
func listenUDP() {
    addr, err := net.ResolveUDPAddr("udp4", ":8081")
    if err != nil {
        log.Fatal(err)
        return
    }
    l, err := net.ListenUDP("udp4", addr)
    if err != nil {
        log.Fatal(err)
        return
    }
    defer l.Close()
    buf := make([]byte, 256)
    len, _, err := l.ReadFrom(buf)
    if err != nil {
        log.Fatal(err)
    }
    buf = buf[:len]
    fmt.Println(string(buf))
}

func dialUDP() {
    remoteAddr, err := net.ResolveUDPAddr("udp4", "127.0.0.1:8081")
    if err != nil {
        log.Fatal(err)
        return
    }
    conn, err := net.DialUDP("udp4", nil, remoteAddr)
    if err != nil {
        log.Fatal(err)
        return
    }
    msg := []byte("UDP-woqunidaye")
    _, err = conn.Write(msg)
    if err != nil {
        log.Fatal(err)
        return
    }
} 

至此,传输层的东西都介绍完了。