• 源码解析---net包


    写在前面

    并不是对net包所有内容解析,只是对常用的函数等相关部分进行一定程度的解剖

    引子

    import (
    	"fmt"
    	"net/http"
    )
    func main() {
    	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    		w.Write([]byte("Hello World"))
    	})
    
    	if err := http.ListenAndServe(":8000", nil); err != nil {
    		fmt.Println("start http server fail:", err)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    http/server.go

    ServeMux

    type ServeMux struct {
    	mu    sync.RWMutex
    	m     map[string]muxEntry //路由:{处理函数,路由}
    	es    []muxEntry // slice of entries sorted from longest to shortest.
    	hosts bool       // 是否有任一路由包含主机名 =>pattern[0] != '/' 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. HTTP请求多路复用器。
    2. 它将每个传入请求的URL与已注册路由列表进行匹配,并为与URL最接近的模式调用处理程序。
    3. 注册“/images/”会导致ServeMux将对“/images”的请求重定向到“/images/”,除非“/images”已经单独注册了。
    • 这里访问 /a OK, 但是访问/a/ 404,返回Hello World /a
    	//http.HandleFunc("/a/", func(w http.ResponseWriter, r *http.Request) {
    	//	w.Write([]byte("Hello World /a/"))
    	//})
    	http.HandleFunc("/a", func(w http.ResponseWriter, r *http.Request) {
    		w.Write([]byte("Hello World /a"))
    	})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 这里访问 /a/a/ 都OK,返回Hello World /a/
    	http.HandleFunc("/a/", func(w http.ResponseWriter, r *http.Request) {
    		w.Write([]byte("Hello World /a/"))
    	})
    	//http.HandleFunc("/a", func(w http.ResponseWriter, r *http.Request) {
    	//	w.Write([]byte("Hello World /a"))
    	//})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 这里访问/a/a/就会走各自的路由
    	http.HandleFunc("/a/", func(w http.ResponseWriter, r *http.Request) {
    		w.Write([]byte("Hello World /a/"))
    	})
    	http.HandleFunc("/a", func(w http.ResponseWriter, r *http.Request) {
    		w.Write([]byte("Hello World /a"))
    	})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    记录一条路由映射

    
    type muxEntry struct {
    	h       Handler //存放handler函数
    	pattern string //路由字符串
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    注册路由

    func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    	DefaultServeMux.HandleFunc(pattern, handler)
    }
    
    • 1
    • 2
    • 3
    // HandleFunc registers the handler function for the given pattern.
    func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    	if handler == nil {
    		panic("http: nil handler")
    	}
    	mux.Handle(pattern, HandlerFunc(handler))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    // Handle registers the handler for the given pattern.
    // If a handler already exists for pattern, Handle panics.
    func (mux *ServeMux) Handle(pattern string, handler Handler) {
    	mux.mu.Lock()
    	defer mux.mu.Unlock()
    
    	if pattern == "" {
    		panic("http: invalid pattern")
    	}
    	if handler == nil {
    		panic("http: nil handler")
    	}
    	if _, exist := mux.m[pattern]; exist {
    		panic("http: multiple registrations for " + pattern)
    	}
    //注册路由就是把,handler和路由放入ServeMux的es属性中
    	if mux.m == nil {
    		mux.m = make(map[string]muxEntry)
    	}
    	e := muxEntry{h: handler, pattern: pattern}
    	mux.m[pattern] = e
    	if pattern[len(pattern)-1] == '/' {
    		mux.es = appendSorted(mux.es, e) //从长到短排序
    	}
    
    	if pattern[0] != '/' {
    		mux.hosts = true
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    运行web服务

    http.ListenAndServe(":8080", nil) //处理程序通常为nil,在这种情况下使用DefaultServeMux。
    
    • 1
    func ListenAndServe(addr string, handler Handler) error {
    	server := &Server{Addr: addr, Handler: handler}
    	return server.ListenAndServe()
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Listen

    func (srv *Server) ListenAndServe() error {
    	if srv.shuttingDown() {
    		return ErrServerClosed
    	}
    	addr := srv.Addr
    	if addr == "" {
    		addr = ":http"
    	}
    	ln, err := net.Listen("tcp", addr) //监听tcp端口
    	if err != nil {
    		return err
    	}
    	return srv.Serve(ln)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    监听到请求( Accept), 通过go 协程处理: go c.serve(connCtx)

    func (srv *Server) Serve(l net.Listener) error {
    	if fn := testHookServerServe; fn != nil {
    		fn(srv, l) // call hook with unwrapped listener
    	}
    
    	origListener := l
    	l = &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")
    		}
    	}
    
    	var tempDelay time.Duration // how long to sleep on accept failure
    
    	ctx := context.WithValue(baseCtx, ServerContextKey, srv)
    	for {
    		rw, err := l.Accept()
    		if err != nil {
    			select {
    			case <-srv.getDoneChan():
    				return ErrServerClosed
    			default:
    			}
    			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 := ctx
    		if cc := srv.ConnContext; cc != nil {
    			connCtx = cc(connCtx, rw)
    			if connCtx == nil {
    				panic("ConnContext returned nil")
    			}
    		}
    		tempDelay = 0
    		c := srv.newConn(rw)
    		c.setState(c.rwc, StateNew, runHooks) // before Serve can return
    		go c.serve(connCtx)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    // Serve a new connection.
    func (c *conn) serve(ctx context.Context) {
    	..........................................
    	defer func() {
    		if err := recover(); err != nil && err != ErrAbortHandler {
    			const size = 64 << 10
    			buf := make([]byte, size)
    			buf = buf[:runtime.Stack(buf, false)]
    			c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
    		}
    		if !c.hijacked() {
    			c.close()
    			c.setState(c.rwc, StateClosed)
    		}
    	}()
    
      //此处省略了很多代码
    
      //留意注意这里的for循环, 长连接时, 这个go协程就不会退出
      //当时最终调用的处理函数是: serverHandler{c.server}.ServeHTTP(w, w.req)
    	for {
    		w, err := c.readRequest(ctx)
    		
            //此处省略了很多代码
        
    		// 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) //最终调用我们注册路由时注册的处理函数的地方
            
            //此处省略了很多代码
            
    		if !w.conn.server.doKeepAlives() {
    			// We're in shutdown mode. We might've replied
    			// to the user without "Connection: close" and
    			// they might think they can send another
    			// request, but such is life with HTTP/1.1.
    			return
    		}
        //此处省略了很多代码
    
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    回到最开始注册路由时,Handler实现的ServeHTTP

    func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    	handler := sh.srv.Handler //此处的handler就是 http.ListenAndServe(":8080", nil)的nil
    	if handler == nil {
    		handler = DefaultServeMux //所以当初注册路由时会被间接调用的 DefaultServeMux.HandleFunc(pattern, handler), 路由信息,应该会全部注册到 DefaultServeMux 变量。
    
    	}
    	if req.RequestURI == "*" && req.Method == "OPTIONS" {
    		handler = globalOptionsHandler{}
    	}
    
    	if req.URL != nil && strings.Contains(req.URL.RawQuery, ";") {
    		var allowQuerySemicolonsInUse int32
    		req = req.WithContext(context.WithValue(req.Context(), silenceSemWarnContextKey, func() {
    			atomic.StoreInt32(&allowQuerySemicolonsInUse, 1)
    		}))
    		defer func() {
    			if atomic.LoadInt32(&allowQuerySemicolonsInUse) == 0 {
    				sh.srv.logf("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192")
    			}
    		}()
    	}
    
    	handler.ServeHTTP(rw, req)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    //ServeHTTP将请求分发到其模式与请求URL最接近的处理程序。
    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)
    	h.ServeHTTP(w, r)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    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)
    		u := &url.URL{Path: path, RawQuery: r.URL.RawQuery}
    		return RedirectHandler(u.String(), StatusMovedPermanently), pattern
    	}
    
    	return mux.handler(host, r.URL.Path)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    // 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 ones
    	if mux.hosts {
    		h, pattern = mux.match(host + path)
    	}
    	if h == nil {
    		h, pattern = mux.match(path)
    	}
    	if h == nil {
    		h, pattern = NotFoundHandler(), ""
    	}
    	return
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    找到最终的handler

    // 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, ""
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    到这里就应该知道原生的路由注册和调用过程了。

  • 相关阅读:
    通用后台管理系统前端界面Ⅵ——首页、登录页、404页面
    【计算机网络】TCP协议与UDP协议的区别
    Windows OpenGL 图像绿幕抠图
    基于Springboot搭建java项目(十六)——Kafka的简介
    利用MySQL主从复制延迟拯救误删数据
    Spark本地环境搭建(local模式)
    Linux基础操作命令详解
    elementui实现图片和表单信息同时上传
    YOLOv5量化调优
    信号系统|信号的分类|确定信号与随机信号 连续信号与离散信号 周期信号与非周期信号 能量信号与功率信号 奇异信号
  • 原文地址:https://blog.csdn.net/newbieJ/article/details/127522765