Go net/http 主要功能及部分源码阅读
2017-11-14 21:02
926 查看
http 服务器
客户端主机使用浏览器去访问一个网站,这个网站的 http 服务器接收到客户端的请求后向用户返回所请求的信息,这个工作流程主要包括以下过程:用户访问一个网站的过程就如上图所示,而在上面这个过程中,在 http 服务器上进行了如下工作:
客户端通过 TCP/IP 协议建立与服务器的 TCP 连接
客户端向服务器发送 HTTP 协议请求报文,请求获得服务器资源
服务器解析接收到的 HTTP 协议请求报文,并根据报文内容处理相关的数据,然后把请求的资源通过 HTTP 协议响应报文发送回给客户端
客户端与服务器断开,由客户端的浏览器解析并渲染返回的 HTML 文档,并把网页显示在屏幕上
URL
用户通过在浏览器上输入“统一资源定位符” URL (Uniform Resource Locator) 来向服务器请求资源和发送数据,URL 的基本格式如下:scheme://host[:port#]/path/[?query][#anchor] scheme 指定使用的协议,如 http, https, ftp 等 host 对应服务器或主机的 IP 地址或域名 port# 端口号,HTTP 服务器的默认端口是80,如果指定了其他端口,则需要手动输入,如localhost:8090 path 访问资源在服务器上的路径 query 发送给服务器的数据,以键值对 key=value 的形式发送 anchor 指向网页上特定位置的锚
net/http 库如何实现 http 服务器功能
要使一个 http 服务器能够正常工作,必须至少实现如下的核心功能:监听主机的某个端口
当在监听的端口上有客户端的请求到来时,接收该客户端的请求
处理客户端的请求
net/http 库对应这三个功能都有相应的实现:
监听端口
监听主机上的某个窗口应该使用函数http.ListenAndServe,该函数的源码定义如下:
// ListenAndServe listens on the TCP network address addr // and then calls Serve with handler to handle requests // on incoming connections. // Accepted connections are configured to enable TCP keep-alives. // Handler is typically nil, in which case the DefaultServeMux is // used. // // ListenAndServe always returns a non-nil error. func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() }
调用该函数的时候,指定要监听的端口
addr和相应的处理函数
handler(当 http 服务器的路由为
DefaultServeMux时,这个
handler的值为
nil)即可:
err := http.ListenAndServe(":23333", nil) if err != nil { log.Fatal("ListenAndServe: ", err) }
观察源码可以发现,
http.ListenAndServe是先利用端口号和处理函数初始化一个
Server结构实例,然后通过调用这个实例的
ListenAndServe()来实现监听功能的,而
Server结构的
ListenAndServe()函数定义如下:
// ListenAndServe listens on the TCP network address srv.Addr and then // calls Serve to handle requests on incoming connections. // Accepted connections are configured to enable TCP keep-alives. // If srv.Addr is blank, ":http" is used. // ListenAndServe always returns a non-nil error. func (srv *Server) ListenAndServe() error { addr := srv.Addr if addr == "" { addr = ":http" } ln, err := net.Listen("tcp", addr) if err != nil { return err } return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}) }
如上代码所示,
Server结构通过调用
net包的
net.Listen("tcp", addr)在底层用 TCP 协议搭建了一个服务,然后监听我们设置的端口,这个函数将会返回一个
Listener接口,这个接口定义了方法
Accept、
Close、
Addr,分别用于建立新的连接、关闭该接口对应的连接和返回接口对应的网络地址。
// A Listener is a generic network listener for stream-oriented protocols. // // Multiple goroutines may invoke methods on a Listener simultaneously. type Listener interface { // Accept waits for and returns the next connection to the listener. Accept() (Conn, error) // Close closes the listener. // Any blocked Accept operations will be unblocked and return errors. Close() error // Addr returns the listener's network address. Addr() Addr }
在
ListenAndServe()函数的最后一行
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}),先把调用
net.Listen("tcp", addr)得到的接口利用类型断言转化为实现了相应函数的
*net.TCPListener的一个结构实例,然后用这个结构实例创建用于在限定时间内保持 TCP 连接的
tcpKeepAliveListener结构实例,最后,调用
srv.Serve函数接收客户端传来的数据。
由此可见,Go 的 net/http 包是通过调用下层的 tcp 协议来设置端口进行监听的。
接收客户端请求
设置好监听端口后,Go 通过调用*Server结构实例的
Serve(net.Listener)来接收客户端的请求。
Serve函数定义如下:
// Serve accepts incoming connections on the Listener l, creating a // new service goroutine for each. The service goroutines read requests and // then call srv.Handler to reply to them. // // For HTTP/2 support, srv.TLSConfig should be initialized to the // provided listener's TLS Config before calling Serve. If // srv.TLSConfig is non-nil and doesn't include the string "h2" in 4000 // Config.NextProtos, HTTP/2 support is not enabled. // // Serve always returns a non-nil error. After Shutdown or Close, the // returned error is ErrServerClosed. func (srv *Server) Serve(l net.Listener) error { defer l.Close() if fn := testHookServerServe; fn != nil { fn(srv, l) } var tempDelay time.Duration // how long to sleep on accept failure if err := srv.setupHTTP2_Serve(); err != nil { return err } srv.trackListener(l, true) defer srv.trackListener(l, false) baseCtx := context.Background() // base is always background, per Issue 16220 ctx := context.WithValue(baseCtx, ServerContextKey, srv) for { rw, e := l.Accept() if e != nil { select { case <-srv.getDoneChan(): return ErrServerClosed default: } if ne, ok := e.(net.Error); ok && ne.Temporary() { if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := 1 * time.Second; tempDelay > max { tempDelay = max } srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay) time.Sleep(tempDelay) continue } return e } tempDelay = 0 c := srv.newConn(rw) c.setState(c.rwc, StateNew) // before Serve can return go c.serve(ctx) } }
在这个函数的循环体
for{}内,首先通过调用接口的
Accept()函数接收客户端请求,请求信息保存在变量
rw中,然后调用
newConn(rw)创建一个新的连接
c,最后单独创建一个 goroutine ,把相应的上下文信息作为参数去调用新连接的
c.serve(ctx)函数,处理客户端的请求。
处理客户端请求
在处理客户端请求的c.serve(ctx)函数内,关键的操作有两个:
调用
w, err := c.readRequest(ctx),取出分析相应的请求信息。
调用
serverHandler{c.server}.ServeHTTP(w, w.req),获取相应的处理函数对请求信息进行处理。具体做法是先创建一个
serverHandler,
handler相当于路由器,它会根据客户端传来的 URL 跳转到相应的处理函数,然后在处理函数中对请求信息进行处理。这个
serverHandler调用了
ServeHTTP函数,而
ServeHTTP函数内部则会调用我们自定义的处理函数对客户端的请求信息进行处理。
整个 http 服务器的流程图如下所示:
参考文献
build-web-application-with-golang astaxie .相关文章推荐
- nsq源码阅读 nsqlookupd源码五 http.go http_server.go
- net/http/server.go部分代码的解释
- Go的http源码阅读笔记
- ucos2源码阅读之主要函数功能分析
- Go语言Http Server源码阅读
- Go net/PRC源码阅读client.go
- 淘宝数据库OceanBase SQL编译器部分 源码阅读--生成物理查询计划
- .NET MVC插件化开发框架源码(插件功能完善版)
- asp.net 客户端回调功能的实现机制探讨(响应部分及可能的优化)
- go-tour源码阅读
- 最开始下载的内核源码和机子的kernel不匹配,参照http://blog.csdn.net/hecant/archive/2007/10/31/1859606.aspx:
- muduo库阅读(37)——Net部分:压缩数据流ZlibOutputStream
- 淘宝数据库OceanBase SQL编译器部分 源码阅读--解析SQL语法树
- Single Shot MultiBox Detector(MXNet)源码阅读笔记(2)
- JDK部分源码阅读与理解
- 深度学习(七十一)darknet 源码阅读
- 关于网址截取最后部分,例http://www.ganchi.net/app.php/1232323.mp3
- http://zh.visualgo.net/mst.html
- nsq源码阅读 nsqlookupd源码二 registration_db.go
- Asp.net web Api源码分析-HttpActionDescriptor的创建