日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

http.ListenAndServe()到底做了什么?

發(fā)布時間:2023/12/4 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 http.ListenAndServe()到底做了什么? 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

參考:https://studygolang.com/articles/25849?fr=sidebar

? http://blog.csdn.net/gophers

實現(xiàn)一個最簡短的hello world服務(wù)器

package mainimport "net/http"func main() {http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {w.Write([]byte(`hello world`))})http.ListenAndServe(":3000", nil) }

http.ListenAndServe()到底做了什么?

http.ListenAndServe()用到的所有依賴都在Go源碼中的/src/pkg/net/http/server.go文件中,我們可以看到它的定義:

ListenAndServe來監(jiān)聽TCP網(wǎng)絡(luò)地址(srv.Addr),然后調(diào)用Serve來處理傳入的請求;

// 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. After Shutdown or Close, // the returned error is ErrServerClosed. func (srv *Server) ListenAndServe() error {if srv.shuttingDown() {return ErrServerClosed}addr := srv.Addr// 如果不指定服務(wù)器地址信息,默認(rèn)以":http"作為地址信息if addr == "" {addr = ":http"}// 創(chuàng)建一個TCP Listener, 用于接收客戶端的連接請求ln, err := net.Listen("tcp", addr)if err != nil {return err}return srv.Serve(ln) }

最后調(diào)用了Server.Serve()并返回,繼續(xù)來看Server.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. // // HTTP/2 support is only enabled if the Listener returns *tls.Conn // connections and they were configured with "h2" in the TLS // Config.NextProtos. // // Serve always returns a non-nil error and closes l. // After Shutdown or Close, the returned error is ErrServerClosed. func (srv *Server) Serve(l net.Listener) error {if fn := testHookServerServe; fn != nil {fn(srv, l) // call hook with unwrapped listener}origListener := ll = &onceCloseListener{Listener: l}defer l.Close()if err := srv.setupHTTP2_Serve(); err != nil {return err}if !srv.trackListener(&l, true) {return ErrServerClosed}defer srv.trackListener(&l, false)baseCtx := context.Background()if srv.BaseContext != nil {baseCtx = srv.BaseContext(origListener)if baseCtx == nil {panic("BaseContext returned a nil context")}}// 接收失敗時,休眠多長時間;休眠時間不斷變長,知道等于time.Second(一千毫秒)var tempDelay time.Duration // how long to sleep on accept failurectx := context.WithValue(baseCtx, ServerContextKey, srv)for {// 等待新的連接建立rw, err := l.Accept()// 處理鏈接失敗if err != nil {select {case <-srv.getDoneChan():return ErrServerCloseddefault:}if ne, ok := err.(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", err, tempDelay)time.Sleep(tempDelay)continue}return err}connCtx := ctxif cc := srv.ConnContext; cc != nil {connCtx = cc(connCtx, rw)if connCtx == nil {panic("ConnContext returned nil")}}tempDelay = 0c := srv.newConn(rw)c.setState(c.rwc, StateNew, runHooks) // before Serve can return// 創(chuàng)建新的協(xié)程處理請求go c.serve(connCtx)} }

Server.Serve()的整個邏輯大概是:首先創(chuàng)建一個上下文對象,然后調(diào)用Listener的Accept()等待新的連接建立;一旦有新的連接建立,則調(diào)用Server的newConn()創(chuàng)建新的連接對象 ,并將連接的狀態(tài)標(biāo)志為StateNew,然后開啟一個新的goroutine處理連接請求。繼續(xù)看一下conn.Serve()方法:

// Serve a new connection. func (c *conn) serve(ctx context.Context) {c.remoteAddr = c.rwc.RemoteAddr().String()ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())// ...// 延遲釋放和TLS相關(guān)處理...for {// 循環(huán)調(diào)用readRequest()方法讀取下一個請求并進行處理w, err := c.readRequest(ctx)if c.r.remain != c.server.initialReadLimitSize() {// If we read any bytes off the wire, we're active.c.setState(c.rwc, StateActive, runHooks)}...... // HTTP cannot have multiple simultaneous active requests.[*]// Until the server replies to this request, it can't read another,// so we might as well run the handler in this goroutine.// [*] Not strictly true: HTTP pipelining. We could let them all process// in parallel even if their responses need to be serialized.// But we're not going to implement HTTP pipelining because it// was never deployed in the wild and the answer is HTTP/2.// 對請求進行處理serverHandler{c.server}.ServeHTTP(w, w.req)w.cancelCtx()if c.hijacked() {return}w.finishRequest()if !w.shouldReuseConnection() {if w.requestBodyLimitHit || w.closedRequestBodyEarly() {c.closeWriteAndWait()}return}// 將連接狀態(tài)置為空閑c.setState(c.rwc, StateIdle, runHooks)// 將當(dāng)前請求置為nilc.curReq.Store((*response)(nil))......c.rwc.SetReadDeadline(time.Time{})} }

其中最關(guān)鍵的一行代碼為serverHandler{c.server}.ServeHTTP(w, w.req),可以繼續(xù)看一下serverHandler:

// serverHandler delegates to either the server's Handler or // DefaultServeMux and also handles "OPTIONS *" requests. type serverHandler struct {srv *Server }func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {handler := sh.srv.Handlerif handler == nil {handler = DefaultServeMux}if req.RequestURI == "*" && req.Method == "OPTIONS" {handler = globalOptionsHandler{}}handler.ServeHTTP(rw, req) }

這里的sh.srv.Handler就是最初在http.ListenAndServe()中傳入的Handler對象,也就是我們自定義的ServeMux對象。如果該Handler對象為nil,則會使用默認(rèn)的DefaultServeMux,最后調(diào)用ServeMux的ServeHTTP()方法匹配當(dāng)前路由對應(yīng)的handler方法。

ServeMux是一個HTTP請求多路復(fù)用器,它將每個傳入請求的URL與注冊模式列表進行匹配,并調(diào)用與這個URL最匹配的模式的處理程序。

type ServeMux struct {mu sync.RWMutexm map[string]muxEntryes []muxEntry // slice of entries sorted from longest to shortest.hosts bool // whether any patterns contain hostnames }func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {// CONNECT requests are not canonicalized.if r.Method == "CONNECT" {// If r.URL.Path is /tree and its handler is not registered,// the /tree -> /tree/ redirect applies to CONNECT requests// but the path canonicalization does not.if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {return RedirectHandler(u.String(), StatusMovedPermanently), u.Path}return mux.handler(r.Host, r.URL.Path)}// All other requests have any port stripped and path cleaned// before passing to mux.handler.host := stripHostPort(r.Host)path := cleanPath(r.URL.Path)// If the given path is /tree and its handler is not registered,// redirect for /tree/.if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {return RedirectHandler(u.String(), StatusMovedPermanently), u.Path}if path != r.URL.Path {_, pattern = mux.handler(host, path)url := *r.URLurl.Path = pathreturn RedirectHandler(url.String(), StatusMovedPermanently), pattern}return mux.handler(host, r.URL.Path) }// handler is the main implementation of Handler. // The path is known to be in canonical form, except for CONNECT methods. func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {mux.mu.RLock()defer mux.mu.RUnlock()// Host-specific pattern takes precedence over generic onesif mux.hosts {h, pattern = mux.match(host + path)}if h == nil {h, pattern = mux.match(path)}if h == nil {h, pattern = NotFoundHandler(), ""}return }// Find a handler on a handler map given a path string. // Most-specific (longest) pattern wins. func (mux *ServeMux) match(path string) (h Handler, pattern string) {// Check for exact match first.v, ok := mux.m[path]if ok {return v.h, v.pattern}// Check for longest valid match. mux.es contains all patterns// that end in / sorted from longest to shortest.for _, e := range mux.es {if strings.HasPrefix(path, e.pattern) {return e.h, e.pattern}}return nil, "" }

ServeMux的Handler方法就是根據(jù)url調(diào)用指定handler方法,handler方法的作用是調(diào)用match匹配路由。在 match 方法里我們看到之前提到的 map[string]muxEntry和 []muxEntry,在 map[string]muxEntry 中查找是否有對應(yīng)的路由規(guī)則存在;如果沒有匹配的路由規(guī)則,則會進行近似匹配。

ServeMux的Handler方法中找到要執(zhí)行的handler之后,就調(diào)用handler的serveHTTP方法。

總結(jié)

以上是生活随笔為你收集整理的http.ListenAndServe()到底做了什么?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。