1.概览
这篇文章是分析golang 的 net/http包,了解 server 端是如何建立 socket 并处理客户端请求的
c语言中,server端调用的系统方法(socket、bind、listen、accept):
1.int listenfd=socket(AF_INET,SOCK_STREAM,0);
2.bind(listenfd,(struct sockaddr*)&local,sizeof(local))
3.listen(listenfd,BLOCK)<0)
while(1){
// 从listen_fd的请求连接队列中取出一个请求并创建一个新的套接字,
// 这个新的套接字用来和客户端进行通信
4.int connfd=accept(listen_fd,(struct sockaddr*)&client,&len);
if(connfd<0){
continue;
}
5.int size=recv(connfd,&buf,sizeof(buf)-1,0);
}
具体介绍可以看我的这一篇文章,有完整的demo实例 blog.51cto.com/lingdandan/…
先看一下 golang 如何使用 net/http :
func main() {
//注册路由
http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("pong"))
})
http.ListenAndServe("127.0.0.1:8080", nil)
2.接下来是了解底层:
0.注册处理器
http.HandleFunc在DefaultServeMux中注册一个handler。DefaultServeMux是go默认提供的ServeMux。
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler) 《===== HandleFunc 在 DefaultServeMux 中注册处理程序函数。
}
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
// ServeMux 是一个http请求的多路复用器.
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // whether any patterns contain hostnames
}
1.创建socket
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
func (srv *Server) ListenAndServe() error {
// ... 省略代码
ln, err := net.Listen("tcp", addr) 《=====创建listen_fd
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}) 《=====这里accept
}
2.accept等待客户端链接
func (srv *Server) Serve(l net.Listener) error {
// ... 省略代码
for {
rw, e := l.Accept() 《=====这里进行accept
if e != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
// ... 省略代码
return e
}
tempDelay = 0
c := srv.newConn(rw) 《=====创建连接准备处理请求
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx) 《=====处理请求
}
}
3.提供新连接处理请求
func (c *conn) serve(ctx context.Context) {
// ... 省略代码
serverHandler{c.server}.ServeHTTP(w, w.req) 《=====
// ... 省略代码
}
// serverHandler 是 委托给服务器的Handler 或 DefaultServeMux
type serverHandler struct {
srv *Server
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux 《=====使用默认的ServeMux(是一个http请求的多路复用器)
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
// Gin框架中用的handler是 engine.Handler(),调用 func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {}
// net/http中默认使用的handler是 DefaultServeMux ,调用 func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {}
handler.ServeHTTP(rw, req) 《=====
}
// ServeHTTP 用来响应http请求。
// ServeMux实现了该方法,其主要作用是分发http请求给相应的handler。
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r) 《=====返回用于给定请求的处理程序,参考 r.Method、r.Host 和 r.URL.Path。 它总是返回一个非零处理程序。
h.ServeHTTP(w, r) 《=====
}
4.调用实际要执行的 handler处理程序
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
总结一下整个过程:
- ln, err := net.Listen("tcp", addr) 做了socket、bind、listen的初始化操作
- rw, e := l.Accept() 进行阻塞的accept,等待客户端的到来
- go c.serve(ctx) 启动新的goroutine处理本次请求
- h, _ := mux.Handler(r) 获取注册的路由,然后拿到这个路由的handler,然后将处理结果返回给客户端