一个例子
内部实现
我们先来看下 http.ListenAndServe
函数:
会进一步调用 Server
的 ListenAndServer
函数:
该函数用来监听指定的 TCP 地址, Listen
函数会返回一个 Listener
并调用 Serve
函数。
我们先忽略 Serve
函数具体实现的细节,先看下大致的函数处理流程。 Serve
函数接收并为每一个连接请求都创建一个 goroutine 进行处理, serve
函数会读取请求的 request
并调用 srv.Handler
来具体处理对应的请求。
在这里调用了 l.Accept()
函数返回一个 conn
连接,我们回过头来看下前面 ln, err := net.Listen("tcp", addr)
做了些什么事情, ln
是 Listener
接口的一个实现:
Listen
函数这里是直接调用了 lc.Listen
函数:
前面得到的 ln Listener
实例就是从这里得到,因为我们指定了 tcp 参数,这里调用的正是 sl.listenTCP
函数。
既然我们知道了上面的 Listener
指的就是 TCPListener
,那么上面 l.Accept()
函数得到的 rw
值又是什么东西呢,
这就还得看下 TCPListener.Accept
函数里头返回的具体是什么:
accept
函数这里返回的就是一个 TCP 连接对象,所以到目前为止的整体流程是:
- 首先根据给定协议和地址(地址包含端口号),创建 socket,得到一个 Listener,用来监听特定网络地址的请求;
- 在一个循环体里不停接收监听地址的请求,处理该 TCP 连接请求;
- 最终每一个请求都会
go c.serve(connCtx)
发起一个 goroutine 来进行处理;
还记得我们在一开始调用 http.HandleFunc()
函数吗,正是这里将我们自己编写的 handler 添加到 DefaultServeMux
中:
可以看到,在调用 ListenAndServe
函数 http.Handler
参数为 nil
的情况,使用的是 DefaultServeMux
,用的正是 ServeMux
对象:
我们来看下 handler 是如何添加到我们的 ServeMux
中的:
知道如何构造 ServeMux
后,剩下的就是在得到一个请求,如何根据请求的 path 得到 pattern 对应的 handler 的逻辑了: