深入探讨Golang网络开发系列之二,聚焦于`net`包的使用与实现原理。在前文《Golang网络开发系列(一)—— netpoll》中,我们了解了`internal/poll.FD`,它作为Golang向上层包提供的网络描述符封装。本篇将引入`net`包,通过实例化`net.Listener`接口,从`net.Listen()`开始,逐步解析其内部逻辑,直至触及`internal/poll.FD`。`net.Listen()`返回一个`net.Listener`接口实例,这标志着Golang网络编程模型的起点。
`net.Listen()`内部实质调用了`ListenConfig`对象的`Listen()`方法,通过实例化`sysListener`并调用`sysListener.listenTCP()`来创建监听配置。以TCP为例,该过程首先生成`fd`,即`net.netFD`实例,并最终返回`net.TCPListener`对象,此对象封装了`net.Listener`接口。
`net.netFD`实际上是对`internal/poll.FD`的封装,提供了一组只读字段,包括`ReadXXX`和`WriteXXX`方法。这些方法执行逻辑直接调用`pfd`对应的方法实现,其代码逻辑简洁明了。
`accept()`方法的实现逻辑基于`net.netFD`对`internal/poll.FD`的封装假设,推测其调用了`internal/poll.FD`的`Accept()`方法。`accept()`返回新的`net.netFD`实例,该实例代表新连接的服务端对象。
在`netFD`的创建与初始化过程中,同时初始化了`internal/poll.FD`。通过`accept()`操作,`laddr`和`raddr`被初始化,而`isConnected`默认值为`false`,表示`net.netFD`尚未连接。
以TCP为例,`listenStream()`方法通过`syscall.Bind()`完成端口与socket的关联,随后调用`fd.init()`实现epoll监控。`socket()`方法通过`sysSocket()`创建并设置socket文件为非阻塞模式。`socket()`调用`newFD(s, family, sotype, net)`完成文件描述符的初始化,并根据参数调用`fd.listenStream`、`fd.listenDatagram`或`fd.dial(ctx, laddr, raddr, ctrlFn)`,这表明调用`net.socket()`时,`listen`或`dial`方法已执行。
当`dial()`成功时,`fd.isConnected`被设置为`true`,即`fd.dial()`成功执行后。回到`sysListener.listenTCP()`中,`listenTCP()`通过`internetSocket()`获得`net.netFD`实例,核心逻辑在`socket()`方法实现。当调用`internetSocket()`时,`raddr`参数为`nil`,此分支对应于`socket()`代码逻辑,即仅`laddr`而无`raddr`,实现`listen`功能。
`TCPListener`对象封装了上述逻辑,`Accept()`方法实现了内部逻辑,返回的`Conn`代表连接对象,而`TCPConn`作为`Conn`的实现,确保所有连接对象遵循相同的接口。
`net.conn`确实实现了`net.Conn`接口,这为后续的网络连接提供了统一的抽象层。至此,我们构建了包含`net`包、`internal/poll.FD`、`net.netFD`和`TCPListener`等组件的类图。
`net`包涵盖了多种网络协议,包括TCP、UDP、Unix网络、IP网络等。通过构建`sysListener`工厂,我们可以实现对不同网络的监听。`net.Listen()`、`net.ListenPacket()`、`net.ListenMulticastUDP()`等方法提供了不同类型的监听方式。借助`net`包,我们能够开发出复杂且高效的网络应用。
下一文将深入探讨如何使用`net`包实现HTTP协议,进一步展现Golang在构建现代网络应用方面的强大能力。