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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android tcp socket框架_最流行的 Web 框架 Gin 源码阅读

發布時間:2025/3/20 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android tcp socket框架_最流行的 Web 框架 Gin 源码阅读 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近公司大部分項目開始往golang換, api的框架選定使用gin, 于是將 gin的源碼看了一遍, 會用幾篇文章將gin的流程及流程做一個梳理, 下面進入正題.

gin框架預覽

上圖大概是 gin里面比較重要的模塊. 從 gin的官方第一個demo入手.

package main

import "github.com/gin-gonic/gin"

func main() {

r := gin.Default()

r.GET("/ping", func(c *gin.Context) {

c.JSON(200, gin.H{

"message": "pong",

})

})

r.Run() // listen and serve on 0.0.0.0:8080

}

r.Run()的源碼:

func (engine *Engine) Run(addr ...string) (err error) {

defer func() { debugPrintError(err) }()

address := resolveAddress(addr)

debugPrint("Listening and serving HTTP on %s\n", address)

err = http.ListenAndServe(address, engine)

return

}

然后看到開始調用的是 http.ListenAndServe(address,engine), 這個函數是 net/http的函數. 然后請求數據就在 net/http開始流轉.

所以, gin源碼閱讀系列就是要弄明白以下幾個問題:

  • request數據是如何流轉的

  • gin框架到底扮演了什么角色

  • 請求從gin流入net/http, 最后又是如何回到gin中

  • gin的context為何能承擔起來復雜的需求

  • gin的路由算法

  • gin的中間件是什么

  • gin的Engine具體是個什么東西

  • net/http的requeset, response都提供了哪些有用的東西

  • request數據是如何流轉的

    先不使用 gin, 直接使用 net/http來處理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)

    }

    }

    在瀏覽器中輸入 localhost:8000, 會看到 HelloWorld. 下面利用這個簡單demo看下 request的流轉流程.

    HTTP是如何建立起來的

    簡單的說一下http請求是如何建立起來的(需要有基本的網絡基礎, 可以找相關的書籍查看, 推薦看 UNIX網絡編程卷1:套接字聯網API)

    在 TCP/IP五層模型下, HTTP位于 應用層, 需要有 傳輸層來承載 HTTP協議. 傳輸層比較常見的協議是 TCP, UDP, SCTP等. 由于 UDP不可靠, SCTP有自己特殊的運用場景, 所以一般情況下 HTTP是由 TCP協議承載的(可以使用wireshark抓包然后查看各層協議)

    使用 TCP協議的話, 就會涉及到 TCP是如何建立起來的. 面試中能夠常遇到的名詞 三次握手, 四次揮手就是在這里產生的. 具體的建立流程就不在陳述了, 大概流程就是圖中左半邊

    所以說, 要想能夠對客戶端http請求進行回應的話, 就首先需要建立起來TCP連接, 也就是 socket. 下面要看下 net/http是如何建立起來 socket?

    net/http是如何建立?socket

    從圖上可以看出, 不管server代碼如何封裝, 都離不開 bind, listen, accept這些函數. 就從上面這個簡單的demo入手查看源碼.

    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)

    }

    }

    注冊路由

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

    w.Write([]byte("Hello World"))

    })

    這段代碼是在注冊一個路由及這個路由的handler到 DefaultServeMux中

    // server.go:L2366-2388

    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)

    }

    if mux.m == nil {

    mux.m = make(map[string]muxEntry)

    }

    mux.m[pattern] = muxEntry{h: handler, pattern: pattern}

    if pattern[0] != '/' {

    mux.hosts = true

    }

    }

    可以看到這個路由注冊太過簡單了, 也就給 gin, iris, echo等框架留下了擴展的空間, 后面詳細說這個東西

    服務監聽及響應

    上面路由已經注冊到 net/http了, 下面就該如何建立socket了, 以及最后又如何取到已經注冊到的路由, 將正確的響應信息從handler中取出來返回給客戶端

    if err := http.ListenAndServe(":8000", nil); err != nil {

    fmt.Println("start http server fail:", err)

    }

    // net/http/server.go:L3002-3005

    func ListenAndServe(addr string, handler Handler) error {

    server := &Server{Addr: addr, Handler: handler}

    return server.ListenAndServe()

    }

    // net/http/server.go:L2752-2765

    func (srv *Server) ListenAndServe() error {

    // ... 省略代碼

    ln, err := net.Listen("tcp", addr) //

    if err != nil {

    return err

    }

    return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})

    }

    // net/http/server.go:L2805-2853

    func (srv *Server) Serve(l net.Listener) error {

    // ... 省略代碼

    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) //

    }

    }

    // net/http/server.go:L1739-1878

    func (c *conn) serve(ctx context.Context) {

    // ... 省略代碼

    serverHandler{c.server}.ServeHTTP(w, w.req)

    w.cancelCtx()

    if c.hijacked() {

    return

    }

    w.finishRequest()

    // ... 省略代碼

    }

    // net/http/server.go:L2733-2742

    func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {

    handler := sh.srv.Handler

    if handler == nil {

    handler = DefaultServeMux

    }

    if req.RequestURI == "*" && req.Method == "OPTIONS" {

    handler = globalOptionsHandler{}

    }

    handler.ServeHTTP(rw, req)

    }

    // net/http/server.go:L2352-2362

    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)

    }

    // net/http/server.go:L1963-1965

    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來處理本次請求. 同時主goroutine繼續等待客戶端連接, 進行高并發操作

  • h,_:=mux.Handler(r)?獲取注冊的路由, 然后拿到這個路由的handler, 然后將處理結果返回給客戶端

  • 從這里也能夠看出來, net/http基本上提供了全套的服務.

    為什么會出現很多go框架

    // net/http/server.go:L2218-2238

    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.

    var n = 0

    for k, v := range mux.m {

    if !pathMatch(k, path) {

    continue

    }

    if h == nil || len(k) > n {

    n = len(k)

    h = v.h

    pattern = v.pattern

    }

    }

    return

    }

    從這段函數可以看出來, 匹配規則過于簡單, 當能匹配到路由的時候就返回其對應的handler, 當不能匹配到時就返回 /. 所以 net/http的路由匹配無法滿足復雜的需求開發. 所以基本所有的go框架干的最主要的一件事情就是重寫 net/http的route

    所以我們直接說 gin就是一個httprouter也不過分, 當然 gin也提供了其他比較主要的功能, 后面會一一介紹

    還有一個go框架要實現的東西是 http.ResponseWriter

    綜述, net/http基本已經提供 http服務的70%的功能, 那些號稱賊快的go框架, 基本上都是提供一些功能, 讓我們能夠更好的處理客戶端發來的請求.

  • T

  • 總結

    以上是生活随笔為你收集整理的android tcp socket框架_最流行的 Web 框架 Gin 源码阅读的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。