溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Go語(yǔ)言網(wǎng)絡(luò)編程與Http源碼分析

發(fā)布時(shí)間:2023-03-28 16:20:37 來(lái)源:億速云 閱讀:96 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹“Go語(yǔ)言網(wǎng)絡(luò)編程與Http源碼分析”,在日常操作中,相信很多人在Go語(yǔ)言網(wǎng)絡(luò)編程與Http源碼分析問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Go語(yǔ)言網(wǎng)絡(luò)編程與Http源碼分析”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

    網(wǎng)絡(luò)分層

    這塊知識(shí)屬于計(jì)算機(jī)網(wǎng)絡(luò),可以直接去看書(shū)。

    我們這里直接上圖:

    Go語(yǔ)言網(wǎng)絡(luò)編程與Http源碼分析

    我們最常講的是五層協(xié)議,最重要的是運(yùn)輸層和應(yīng)用層,這兩層是大多數(shù)情況下,工程師可以在代碼中可以直接干預(yù)的模塊,我們大多數(shù)的網(wǎng)絡(luò)編程調(diào)優(yōu),就是在調(diào)這些協(xié)議的一些參數(shù)和細(xì)節(jié)。這兩層的情況:

    • 運(yùn)輸層協(xié)議:TCP和UDP。

    • 應(yīng)用層協(xié)議:Http,SMTP,F(xiàn)TP,WebSocket等等,這些協(xié)議需要使用運(yùn)輸層協(xié)議作為依托。

    引申,需要注意TCP和UDP的區(qū)別,和他們具體的使用場(chǎng)景。

    順便提一句,網(wǎng)絡(luò)分層本質(zhì)上也是我們反復(fù)提過(guò)得加一層的思想,也是高內(nèi)聚低耦合的一種具體的實(shí)現(xiàn)。

    進(jìn)程間通信(IPC)

    這塊知識(shí)屬于操作系統(tǒng),注意不是Linux操作系統(tǒng),還牽扯一點(diǎn)計(jì)算機(jī)組成原理的知識(shí)。

    IPC 是 Inter-Process Communication 的縮寫(xiě),可以被翻譯為進(jìn)程間通信。主要方法有: 系統(tǒng)信號(hào)(signal)、管道(pipe)、套接字 (socket)、文件鎖(file lock)、消息隊(duì)列(message queue)、信號(hào)量(semaphore)等。最常用的是系統(tǒng)信號(hào),套接字,還有一個(gè)叫共享內(nèi)存的,能實(shí)現(xiàn),但不提倡。Go底層的os包里也包含著這些常用的方法。

    這里需要再引申下,操作系統(tǒng)中進(jìn)程和線(xiàn)程是什么,協(xié)程又是什么。進(jìn)程間是如何通信的,線(xiàn)程間又是如何通信的。

    我們單獨(dú)把socket拎出來(lái)說(shuō),因?yàn)樵诒姸喾桨钢?,就屬它比較通用,比較靈活:使用socket可以跨機(jī)器進(jìn)行通訊。

    Socket

    實(shí)際上,現(xiàn)代操作系統(tǒng)的內(nèi)核都會(huì)帶有socket相關(guān)的API,我們的代碼在運(yùn)行時(shí),只需要調(diào)用操作系統(tǒng)提供的接口,就可以輕松建立網(wǎng)絡(luò)連接,這也是我們之前講過(guò)的面向接口編程的具體場(chǎng)景之一。

    我們這里直接講Go語(yǔ)言中的Socket。在GO語(yǔ)言中有一個(gè)叫做syscall的包,里面有對(duì)應(yīng)的一整套的socket的方法,并且這些方法是做過(guò)跨平臺(tái)處理的,我們最常用的Http包里的許多建立連接,接收內(nèi)容的方法都直接或者間接的用了syscall包。

    總而言之,我們常用的Http包在建立鏈接時(shí)需要使用到socket,socket建立連接時(shí)需要具體的傳輸層協(xié)議。

    Http

    基礎(chǔ)知識(shí)

    HTTP屬于應(yīng)用層協(xié)議,也就是最頂層協(xié)議。目前他有三個(gè)版本:

    • HTTP1.1 最常用的版本,使用TCP作為運(yùn)輸層協(xié)議。

    • HTTP2 一個(gè)升級(jí)版本,用的不多。同樣使用TCP作為運(yùn)輸層協(xié)議。

    • HTTP3 設(shè)計(jì)了一個(gè)新的傳輸層協(xié)議QUIC,可以選擇TCP或者UDP來(lái)傳輸數(shù)據(jù)。

    注意,HTTP協(xié)議誕生的年代相當(dāng)久遠(yuǎn),它是一個(gè)無(wú)狀態(tài)的協(xié)議。

    一個(gè)HTTP的請(qǐng)求有兩部分組成:頭部header和主體body。

    //這是一個(gè)GET請(qǐng)求的頭部。
    :authority: api.bilibili.com
    :method: GET
    :path: /x/web-interface/bgroup/member/in?business=MGR&name=PCQoE%E4%BA%BA%E7%BE%A41&dimension=1
    :scheme: https
    accept: application/json, text/plain, */*
    accept-encoding: gzip, deflate, br
    accept-language: zh-CN,zh;q=0.9,sm;q=0.8,en;q=0.7
    cache-control: no-cache
    cookie: 
    origin: https://www.bilibili.com
    pragma: no-cache
    referer: https://www.bilibili.com/?utm_source=gold_browser_extension
    user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36

    頭部中有幾個(gè)特別的字段需要關(guān)注下。origin,referer, user-agent, accept。另外,還有幾個(gè)特別的字段:Content-Length,Connection。TCP協(xié)議本身是基于字節(jié)流的,它無(wú)法區(qū)分消息邊界,需要應(yīng)用層協(xié)議自己來(lái)實(shí)現(xiàn)。

    可以詳細(xì)看下Response返回的頭部中都有哪些字段。另外,一些常見(jiàn)的字段我們經(jīng)常在Postman中使用。

    客戶(hù)端

    在Go語(yǔ)言中啟動(dòng)一個(gè)客戶(hù)端是相當(dāng)簡(jiǎn)單的一件事,Go為HTTP提供了大量的開(kāi)箱即用的工具。

    url := "https://www.bilibili.com" //我們要請(qǐng)求的地址
    resp, err := http.Get(url) //get請(qǐng)求,經(jīng)典返回:內(nèi)容和一個(gè)ERR
    defer func() {
    	_ = resp.Body.Close()  //通常我們需要及時(shí)關(guān)閉掉返回內(nèi)容。
    }()
    if err != nil {
    	fmt.Printf("請(qǐng)求錯(cuò)誤: %v\n", err)
    }
    fmt.Printf("返回狀態(tài):\n%s\n", resp.Status)

    但是,我們通常不會(huì)這樣直接調(diào)用。http.Get的底層調(diào)用的是http.Client,返回的是http.Response。通常情況下,我們會(huì)使用http.Client結(jié)合業(yè)務(wù)場(chǎng)景來(lái)構(gòu)造一些請(qǐng)求:

    url := "https://www.bilibili.com"
    req, _ := http.NewRequest(http.MethodGet, url, nil) //req 是一個(gè)Request結(jié)構(gòu),它有大量的方法的熟悉 可以自定義。
    req.Form.Add("test", "1231")                        //構(gòu)造一個(gè)表單提交
    req.Header.Set("Cookie", "123")                     //設(shè)置Cookie
    resp, err := http.DefaultClient.Do(req) //這里使用的依然是默認(rèn)的DefaultClient
    if err != nil {
    	fmt.Printf("請(qǐng)求錯(cuò)誤: %v\n", err)
    }
    defer func() {
    	_ = resp.Body.Close()
    }()
    fmt.Printf("返回狀態(tài):\n%s\n", resp.Status)

    正常情況下,我們使用http.DefaultClient.Do,直接調(diào)用默認(rèn)的http.Client就可以正常發(fā)起請(qǐng)求。在某些情況下,公司內(nèi)部會(huì)封裝一個(gè)統(tǒng)一的http.Client,里面會(huì)集成一些公司內(nèi)統(tǒng)一的調(diào)用標(biāo)識(shí),服務(wù)請(qǐng)求方,提供方,trace,機(jī)器編碼,統(tǒng)一的過(guò)期時(shí)間等配置信息。

    http.Client的結(jié)構(gòu)非常簡(jiǎn)單:

    type Client struct {
    	Transport RoundTripper //真正干活的結(jié)構(gòu)體
    	CheckRedirect func(req *Request, via []*Request) error //一個(gè)重定向校驗(yàn)方法,用的比較少
    	Jar CookieJar //Cookie包,我們常用的方法都在這個(gè)接口中
    	Timeout time.Duration //單次完整HTTP請(qǐng)求的超時(shí)時(shí)間,0代表沒(méi)有設(shè)置。
    }

    如果有時(shí)間,可以看下 DefaultTransport的源碼,通過(guò)簡(jiǎn)單配置,進(jìn)而理解Http與TCP的一些關(guān)鍵配置項(xiàng)的含義。

    最后,如果你愿意也可以自己造個(gè)輪子,但是我們決不提倡這種行為。

    conn, err := net.Dial("tcp", "bilibili.com:80")
    if err != nil {
    	fmt.Printf("connect err => %s\n", err.Error())
    }
    buf := bytes.Buffer{}
    buf.WriteString("GET / HTTP/1.1\r\n")
    buf.WriteString("Host: baidu.com\r\n")
    buf.WriteString("USer-Agent: Go-http-client/1.1\r\n")
    // 請(qǐng)求頭結(jié)束
    buf.WriteString("\r\n")
    // 請(qǐng)求body結(jié)束
    buf.WriteString("\r\n\r\n")
    _, _ = conn.Write(buf.Bytes())
    // 獲取響應(yīng)信息
    resp, _ := io.ReadAll(conn)
    fmt.Printf("響應(yīng)信息\n%q", resp)

    http.Client的底層是基于net.Dial實(shí)現(xiàn)的,net.Dial底層又調(diào)用了操作系統(tǒng)的Socket相關(guān)接口。

    可以嘗試實(shí)現(xiàn)一個(gè)Post方法。

    服務(wù)端

    Go語(yǔ)言搭建一個(gè)服務(wù)器非常簡(jiǎn)單,只需要用到幾個(gè)方法:

    http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
    	_, _ = fmt.Fprintf(writer, "關(guān)注 香香編程喵喵喵,關(guān)注香香編程謝謝喵喵喵!")
    })
    panic(http.ListenAndServe(":8080", nil))

    http.HandleFunc用來(lái)注冊(cè)一個(gè)處理器。其內(nèi)部會(huì)持有一個(gè)哈希,用來(lái)存儲(chǔ)路徑與處理器的映射關(guān)系。注意,這里和Gin框架就有區(qū)別了。

    http.ListenAndServe用來(lái)監(jiān)聽(tīng)一個(gè)端口上的TCP鏈接,并處理后續(xù)的請(qǐng)求。它的底層調(diào)用的是net.Listen,同樣也是基于Socket的方法,我們這里不做展開(kāi)。

    到此,關(guān)于“Go語(yǔ)言網(wǎng)絡(luò)編程與Http源碼分析”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI