在net包中,listen、read和write函数之间的区别是什么?

13

我是Go的新手,作为我的第一个测试项目之一,我想编写一个使用UDP的简单客户端/服务器程序。我已经让它工作了,但有很多方法可以做到这一点,我想知道哪种方式最好。

net.Listen()与net.ListenUDP()与net.ListenPacket()之间的区别

net.Read()与net.ReadFrom()与net.ReadFromUDP()之间的区别

net.Write()与net.WriteTo()与net.WriteToUDP()之间的区别


这不是“最好”的问题,而是与您的用例有关。正如您所看到的,每个函数都具有略微不同的API,适用于不同的用例。这完全取决于您想要实现什么。 - Not_a_Golfer
@Not_a_Golfer 例如,我想在“:12345”上侦听udp数据包。我可以使用net.Listen(“udp”,“:12345”),也可以先使用net.ResolveUDPAddr(“udp”,“:12345”)解析地址,然后在net.ListenUDP(“udp”,address)中使用该地址。那么Listen是否与ResolveUDPAddr和ListenUDP组合起来做同样的事情? - user2768789
2
有了所有的监听器函数,它们之间的区别在于它们的通用性(listen vs listenudp),因此只需要用最少的代码实现你的目标。Read/ReadFrom和Write/WriteTo不仅仅是方便上的差异,它们执行的任务也不同,所以使用你需要的那个。总的来说,只要完成工作并尽可能保持代码简单和短小即可。 - Not_a_Golfer
遗传函数(例如net.Listen())适用于各种连接,但它们不公开UDP特定功能。如果您想执行特定于UDP的操作,请使用ListenUDP() - fuz
2个回答

27

让我们来看看你的问题。

1. net.Listen() vs. net.ListenUDP() vs. net.ListenPacket()

net.Listen()、net.ListenUDP()和net.ListenPacket()有何区别?

net.Listen()

Listen在本地网络地址laddr上进行通信。网络net必须是面向流的网络:"tcp","tcp4","tcp6","unix"或"unixpacket"。有关laddr的语法,请参见Dial。

使用示例#1:

tcpSock, err := net.Listen("tcp", "0.0.0.0:8080")

使用示例 #2

unixSock, err := net.Listen("unix", "/var/app/server.sock")

我们可以看到在源代码中net.Listen()作为一个开关语句,调用了net.ListenTCP或者net.ListenUnix,否则就会抛出默认错误。
func Listen(net, laddr string) (Listener, error) {
    la, err := resolveAddr("listen", net, laddr, noDeadline)
    if err != nil {
        return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
    }
    var l Listener
    switch la := la.toAddr().(type) {
    case *TCPAddr:
        l, err = ListenTCP(net, la)
    case *UnixAddr:
        l, err = ListenUnix(net, la)
    default:
        return nil, &OpError{
            Op:   "listen",
            Net:  net,
            Addr: la,
            Err:  &AddrError{Err: "unexpected address type", Addr: laddr},
        }
    }
    if err != nil {
        return nil, err // l is non-nil interface containing nil pointer
    }
    return l, nil
}

请查看net.Listen()文档以获取更多信息。

因此,我们可以从最初的比较中排除net.ListenUDP;并查看net.ListenPacket()

net.ListenPacket()

ListenPacket在本地网络地址laddr上进行通告。网络net必须是面向数据包的网络:"udp"、"udp4"、"udp6"、"ip"、"ip4"、"ip6"或"unixgram"。有关laddr的语法,请参见Dial。

使用示例 #1:

pListener, err := net.ListenPacket("ip4", "0.0.0.0:9090")

而且,如果我们深入了解,我们可以看到它的运作方式与net.Listen()非常相似:

func ListenPacket(net, laddr string) (PacketConn, error) {
    la, err := resolveAddr("listen", net, laddr, noDeadline)
    if err != nil {
        return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
    }
    var l PacketConn
    switch la := la.toAddr().(type) {
    case *UDPAddr:
        l, err = ListenUDP(net, la)
    case *IPAddr:
        l, err = ListenIP(net, la)
    case *UnixAddr:
        l, err = ListenUnixgram(net, la)
    default:
        return nil, &OpError{
            Op:   "listen",
            Net:  net,
            Addr: la,
            Err:  &AddrError{Err: "unexpected address type", Addr: laddr},
        }
    }
    if err != nil {
        return nil, err // l is non-nil interface containing nil pointer
    }
    return l, nil
}

2. net.Read() vs. net.ReadFrom() vs net.ReadFromUDP()

这些函数通常用于从不同的net连接中读取数据。

net.Read()

如果您查看net包,您会发现所有的Read()函数都来自实现了Conn接口的类型。

Conn接口定义如下:

……一个通用的面向流的网络连接。

为了实现net.Conn,你需要有以下方法:

type Conn interface {
    // Read reads data from the connection.
    // Read can be made to time out and return a Error with Timeout() == true
    // after a fixed time limit; see SetDeadline and SetReadDeadline.
    Read(b []byte) (n int, err error)

    // Write writes data to the connection.
    // Write can be made to time out and return a Error with Timeout() == true
    // after a fixed time limit; see SetDeadline and SetWriteDeadline.
    Write(b []byte) (n int, err error)

    // Close closes the connection.
    // Any blocked Read or Write operations will be unblocked and return errors.
    Close() error

    // LocalAddr returns the local network address.
    LocalAddr() Addr

    // RemoteAddr returns the remote network address.
    RemoteAddr() Addr

    // SetDeadline sets the read and write deadlines associated
    // with the connection. It is equivalent to calling both
    // SetReadDeadline and SetWriteDeadline.
    //
    // A deadline is an absolute time after which I/O operations
    // fail with a timeout (see type Error) instead of
    // blocking. The deadline applies to all future I/O, not just
    // the immediately following call to Read or Write.
    //
    // An idle timeout can be implemented by repeatedly extending
    // the deadline after successful Read or Write calls.
    //
    // A zero value for t means I/O operations will not time out.
    SetDeadline(t time.Time) error

    // SetReadDeadline sets the deadline for future Read calls.
    // A zero value for t means Read will not time out.
    SetReadDeadline(t time.Time) error

    // SetWriteDeadline sets the deadline for future Write calls.
    // Even if write times out, it may return n > 0, indicating that
    // some of the data was successfully written.
    // A zero value for t means Write will not time out.
    SetWriteDeadline(t time.Time) error
}

源代码

因此,这应该清楚地表明实际上没有net.Read();而是操作实现了net.Conn接口的类型的Read()函数。

net.ReadFrom()

与net.Read()一样,所有的实现都来自于实现一个接口。在这种情况下,该接口是net.PacketConn。

PacketConn是一种通用的面向数据包的网络连接。

type PacketConn interface {
    // ReadFrom reads a packet from the connection,
    // copying the payload into b.  It returns the number of
    // bytes copied into b and the return address that
    // was on the packet.
    // ReadFrom can be made to time out and return
    // an error with Timeout() == true after a fixed time limit;
    // see SetDeadline and SetReadDeadline.
    ReadFrom(b []byte) (n int, addr Addr, err error)

    // WriteTo writes a packet with payload b to addr.
    // WriteTo can be made to time out and return
    // an error with Timeout() == true after a fixed time limit;
    // see SetDeadline and SetWriteDeadline.
    // On packet-oriented connections, write timeouts are rare.
    WriteTo(b []byte, addr Addr) (n int, err error)

    // Close closes the connection.
    // Any blocked ReadFrom or WriteTo operations will be unblocked
    // and return errors.
    Close() error

    // LocalAddr returns the local network address.
    LocalAddr() Addr

    // SetDeadline sets the read and write deadlines associated
    // with the connection.
    SetDeadline(t time.Time) error

    // SetReadDeadline sets the deadline for future Read calls.
    // If the deadline is reached, Read will fail with a timeout
    // (see type Error) instead of blocking.
    // A zero value for t means Read will not time out.
    SetReadDeadline(t time.Time) error

    // SetWriteDeadline sets the deadline for future Write calls.
    // If the deadline is reached, Write will fail with a timeout
    // (see type Error) instead of blocking.
    // A zero value for t means Write will not time out.
    // Even if write times out, it may return n > 0, indicating that
    // some of the data was successfully written.
    SetWriteDeadline(t time.Time) error
}

注意:TCPConn.ReadFrom 来自实现 io.ReaderFrom ReadFrom 方法。

3. net.Write()

正如你所猜测的那样,我们一遍又一遍地看着相同的模式。这被称为实现接口。我会让你根据上面的解释自己查找这个特定方法以及它是如何工作的。

你最好先看一下 Effective Go 关于接口的部分

更多 net 包信息可在 源代码GoDoc 中找到。


1
如果你只使用UDP数据包,最好的解决方案是使用UDP函数。
例如,net.ListenUDP() 可以监听udp、udp4和udp6数据包。net.ListenPacket可以监听所有UDP数据包,也可以监听ip、ip4、ip6和unixgram数据包。net.Listen()可以监听tcp、tcp4、tcp6、unix和unixpacket数据包。
来源: http://golang.org/pkg/net/

现在,实现这个的最佳/首选方式是什么? - ThreeFx
抱歉,我不理解你的问题... 你能否详细解释一下? - trent
问题是什么是首选样式来监听UDP查询。 - ThreeFx
谢谢您的澄清。在我的初始回答中,我所指的“UDP函数”是net.ListenUDP()net.ReadFromUDP()等函数。 - trent
我的意思不是要告诉你使用哪个函数,而是告诉你在什么时候使用哪个函数。 - ThreeFx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接