启动 TCP 服务器
zhuanlan.zhihu.com/p/609629545 启动 tcp 服务的代码框架,伪代码如下:
// 启动一个 tcp 服务端代码示例
func main(){
// 创建一个 tcp 端口监听器
l,_ := net.Listen("tcp",":8080")
// 主动轮询模型
for{
// 等待 tcp 连接到达
conn,_ := l.Accept()
// 开启一个 goroutine 负责一笔客户端请求的处理
go serve(conn)
}
}
// 处理一笔 tcp 连接
func serve(conn net.Conn){
defer conn.Close()
var buf []byte
// 读取连接中的数据
_,_ = conn.Read(buf)
// ...
}
listen && bind
sysSocket 中
- syscall.Socket 创建套接字
- syscall.SetNonblock 将 socket 设置为非阻塞模式
listenStream 中
- 发起系统调用 syscall.Bind 实现 socket fd 和端口的绑定
- 发起系统调用,实现对 fd 的监听
- 调用 netFD.init 方法对 socket fd 进行初始化, 在其中执行了 epoll 操作
var serverInit sync.Once
func (pd *pollDesc) init(fd *FD) error {
serverInit.Do(runtime_pollServerInit)
ctx, errno := runtime_pollOpen(uintptr(fd.Sysfd))
// ...
}
从 netFD.init 方法出发,历经 netFD.init -> FD.Init -> pollDesc.init 的链路,
-
通过 sync.Once 保证全局只执行一次 runtime_pollServerInit 方法, 调用 epollcreate1
-
调用 runtime_pollOpen 方法将当前 fd 添加到 epoll 池中.
func (fd *netFD) init() error {
return fd.pfd.Init(fd.net, true)
}
accept
历经 TCPListener.Accept -> TCPListener.accept -> netFD.accept -> FD.Accept 的辗转,最终获取 tcp 连接及阻塞处理的核心逻辑实现于 internal/poll/fd_unix.go 的 FD.Accept 方法.
func (fd *netFD) accept() (netfd *netFD, err error) {
// 倘若有 tcp 连接到达,则成功取出并返回
// 倘若没有 tcp 连接到达,会 gopark 进入被动阻塞,等待被唤醒
d, rsa, errcall, err := fd.pfd.Accept()
// ...
}