Go语言的内置包net提供了大量api,功能十分强大、实现非常优美,不读一读实在是有点可惜呀。
程序
首先,写一个简单的服务器。
package main
import (
"fmt"
"log"
"net/http"
"strings"
)
func main() {
// A simple http server.
//
// This server send "Hello user" to client based on URL.
// Example:
//
// $ curl localhost:8080/foo/bar
// > Hello foo bar!
//
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello %s!\n",
strings.Join(strings.Split(r.URL.String(), "/")[1:], " "))
})
fmt.Println("Listening to port 8080.")
log.Fatal(http.ListenAndServe(":8080", nil))
}如注释所说,这个服务器会发一个“Hello xxx”给客户端,一个简单的hello world式程序。
代码分析
虽然这个服务器只有不到10行,但是通过代码追踪(ctrl+鼠标左键),我们可以一层一层地看到net/http包里各种各样的接口。
http.HandleFunc
http.HandleFunc(pattern string, handler func(ResponseWriter, *Request))
-> (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))
-> (mux *ServeMux) Handle(pattern string, handler Handler)http包中的HandleFunc方法会给默认的ServerMux注册一个Handler,用于响应客户端发来的请求;它包含两个参数,一个是字符串类型的URL,另一个是Handler函数,这两个参数经过三层传递传递到DefaultServeMux.Handle函数中,才会被正式执行注册操作。ServerMux是一个HTTP协议请求复用器,其中包含多个URL到Handler的映射,用于匹配不同的客户端请求,并执行相应的Handler;ServerMux支持近似匹配,当匹配不完全时,它会寻找最接近的匹配。DefaultServerMux是包中自带的默认复用器,也就是说,开发者可以定义自己的复用器。Handler是一个接口,包含一个处理函数。
这个过程涉及到两个重要结构ServeMux和muxEntry。
// type ServeMux
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
hosts bool // whether any patterns contain hostnames
}
// type muxEntry
type muxEntry struct {
explicit bool
h Handler
pattern string
}mu一个读写排它锁,用于保证注册Handler过程的原子性。(操作系统知识怎么在这出现了)m映射,储存了从URL到muxEntry的映射,muxEntry中包含了处理函数Handler。hosts表示是否有某个pattern包含主机名。
http.ListenAndServe
http.ListenAndServe(addr string, handler Handler)
-> (srv *Server) ListenAndServe()
-> (srv *Server) Serve(l net.Listener)http.ListenAndServe方法创建一个Server,监听TCP地址addr并使用handler来处理接收到的请求。Server定义了运行一个HTTP服务器的各种参数,包括TCP地址、Handler、TLS参数、超时时间、最大头长度等等。Server的ListenAndServe方法创建一个net包中的传输层TCP监听器,并调用Serve方法。Serve方法接收一个TCP监听器,通过该监听器获得连接信息,并为每一个连接创建一个线程并调用Server的Handler来响应。具体实现细节涉及网络编程。