Golang網絡編程-HTTP編程實戰篇


          Golang網絡編程-HTTP編程實戰篇

                               作者:尹正傑

版權聲明:原創作品,謝絕轉載!否則將追究法律責任。

 

 

 

 

一.HTTP概述

1>.HTTP概述

  一個Web服務器也被稱為HTTP服務器,它通過HTTP (HyperText Transfer Protocol 超文本傳輸協議)協議與客戶端通信。這個客戶端通常指的是Web瀏覽器(其實手機端客戶端內部也是瀏覽器實現的)。

  Web服務器的工作原理可以簡單地歸納為:
    1>.客戶機通過TCP/IP協議建立到服務器的TCP連接
    2>.客戶端向服務器發送HTTP協議請求包,請求服務器里的資源文檔
    3>.服務器向客戶機發送HTTP協議應答包,如果請求的資源包含有動態語言的內容,那么服務器會調用動態語言的解釋引擎負責處理“動態內容”,並將處理得到的數據返回給客戶端
    4>.客戶機與服務器斷開。由客戶端解釋HTML文檔,在客戶端屏幕上渲染圖形結果

2>.HTTP協議

  超文本傳輸協議(HTTP,HyperText Transfer Protocol)是互聯網上應用最為廣泛的一種網絡協議,它詳細規定了瀏覽器和萬維網服務器之間互相通信的規則,通過因特網傳送萬維網文檔的數據傳送協議。

  HTTP協議通常承載於TCP協議之上,有時也承載於TLS或SSL協議層之上,這個時候,就成了我們常說的HTTPS。

3>.HTTP請求報文格式說明

  如上圖所示,HTTP 請求報文由請求行、請求頭部、空行、請求包體4個部分組成。

  請求行
    請求行由方法字段、URL字段 和HTTP 協議版本字段 3個部分組成,他們之間使用空格隔開。常用的 HTTP 請求方法有GET、POST。
      GET:
        當客戶端要從服務器中讀取某個資源時,使用GET 方法。GET 方法要求服務器將URL 定位的資源放在響應報文的數據部分,回送給客戶端,即向服務器請求某個資源。
        使用GET方法時,請求參數和對應的值附加在 URL 后面,利用一個問號(“?”)代表URL 的結尾與請求參數的開始,傳遞參數長度受限制,因此GET方法不適合用於上傳數據。
        通過GET方法來獲取網頁時,參數會顯示在瀏覽器地址欄上,因此保密性很差。
      POST:
        當客戶端給服務器提供信息較多時可以使用POST 方法,POST 方法向服務器提交數據,比如完成表單數據的提交,將數據提交給服務器處理。
        GET 一般用於獲取/查詢資源信息,POST 會附帶用戶數據,一般用於更新資源信息。POST 方法將請求參數封裝在HTTP 請求數據中,而且長度沒有限制,因為POST攜帶的數據,在HTTP的請求正文中,以名稱/值的形式出現,可以傳輸大量數據。
  請求頭部     請求頭部為請求報文添加了一些附加信息,由“名
/值”對組成,每行一對,名和值之間使用冒號分隔。請求頭部通知服務器有關於客戶端請求的信息,典型的請求頭有:       User-Agent:         請求的瀏覽器類型       Accept:         客戶端可識別的響應內容類型列表,星號("*")用於按范圍將類型分組,用"*/*"指示可接受全部類型,用"type/*"指示可接受 type 類型的所有子類型       Accept-Language:         客戶端可接受的自然語言       Accept-Encoding:         客戶端可接受的編碼壓縮格式       Accept-Charset:         可接受的應答的字符集       Host:         請求的主機名,允許多個域名同處一個IP 地址,即虛擬主機       connection:         連接方式(close或keepalive)       Cookie:         存儲於客戶端擴展字段,向同一域名的服務端發送屬於該域的cookie
  空行     最后一個請求頭之后是一個空行,發送回車符和換行符,通知服務器以下不再有請求頭。
  請求包體     請求包體不在GET方法中使用,而在POST方法中使用。POST方法適用於需要客戶填寫表單的場合。與請求包體相關的最常使用的是包體類型Content
-Type和包體長度Content-Length。

4>.HTTP響應報文說明

  如上圖所示,HTTP 響應報文由狀態行、響應頭部、空行、響應包體4個部分組成。

  狀態行
    狀態行由 HTTP 協議版本字段、狀態碼和狀態碼的描述文本3個部分組成,他們之間使用空格隔開。
    狀態碼:狀態碼由三位數字組成,第一位數字表示響應的類型,常用的狀態碼有五大類如下所示:
      1xx:    
        表示服務器已接收了客戶端請求,客戶端可繼續發送請求
      2xx:
        表示服務器已成功接收到請求並進行處理
      3xx:
        表示服務器要求客戶端重定向
      4xx:
        表示客戶端的請求有非法內容
      5xx:
        表示服務器未能正常處理客戶端的請求而出現意外錯誤
    常見的狀態碼舉例:
      200:
        表示OK,即客戶端請求成功
      400:
        表示Bad Request,即請求報文有語法錯誤
      401:
        表示Unauthorized,即未授權
      403:
        表示Forbidden,即服務器拒絕服務
      404:
        表示Not Found,即請求的資源不存在
      500:
        表示Internal Server Error,即服務器內部錯誤
      503:
        表示Server Unavailable,即服務器臨時不能處理客戶端請求(稍后可能可以)

  響應頭部
    響應頭可能包括:
      Location:
        響應報頭域用於重定向接受者到一個新的位置
      Server:
        響應報頭域包含了服務器用來處理請求的軟件信息及其版本
      Vary:
        指示不可緩存的請求頭列表
      Connection:
        連接方式

  空行
    最后一個響應頭部之后是一個空行,發送回車符和換行符,通知服務器以下不再有響應頭部。

  響應包體
    服務器返回給客戶端的文本信息。

5>.博主推薦閱讀

  http協議版本,工作機制及http服務器應用掃盲篇:
    https://www.cnblogs.com/yinzhengjie/p/11986869.html

  HTTP協議詳解:
    https://www.cnblogs.com/yinzhengjie/p/12014076.html

  前后端分離-Restful最佳實踐
    https://www.cnblogs.com/yinzhengjie/p/12037939.html

 

二.使用Golang編寫一個簡單的Web服務器(生產環境建議初學者使用開源的web框架,比如Beego,Gin等)

package main

import (
    "fmt"
    "net/http"
)

func UserResp(resp http.ResponseWriter, req *http.Request) {
    fmt.Printf("請求方法: %s\n", req.Method)
    fmt.Printf("瀏覽器發送請求文件路徑: %s\n", req.URL)
    fmt.Printf("請求頭: %s\n", req.Header)
    fmt.Printf("請求包體: %s\n", req.Body)
    fmt.Printf("客戶端網絡地址: %s\n", req.RemoteAddr)
    fmt.Printf("客戶端Agent: %s\n", req.UserAgent())

    /**
    給客戶端回復數據
    */
    resp.Write([]byte("User response"))
}

func IndexResp(resp http.ResponseWriter, req *http.Request) {
    resp.Write([]byte("Index response"))
}

func main() {
    /**
    為不同的請求注冊不同的函數
    */
    http.HandleFunc("/user", UserResp)
    http.HandleFunc("/index", IndexResp)

    //開啟服務器,監聽客戶端的請求
    http.ListenAndServe("127.0.0.1:8080", nil)

}

 

三.使用Golang發起HTTP請求

package main

import (
    "fmt"
    "net/http"
)

func main() {

    /**
    該URL是咱們自己剛剛編寫簡單的Web服務器對應的資源。
    */
    url := "http://127.0.0.1:8080/user"

    resp, err := http.Get(url)
    if err != nil {
        fmt.Println("獲取數據失敗,錯誤原因: ", err)
        return
    }
    defer resp.Body.Close()

    /**
    獲取從服務器端讀到數據
    */
    fmt.Printf("狀態: %s\n", resp.Status)
    fmt.Printf("狀態碼: %v\n", resp.StatusCode)
    fmt.Printf("響應頭部: %s\n", resp.Header)
    fmt.Println("響應包體: ", resp.Body)

    /**
    定義切片緩沖區,臨時存儲讀到的數據,並將每次讀到的結果拼接到data中
    */
    buf := make([]byte, 4096)
    var data string

    for {
        n, _ := resp.Body.Read(buf)
        if n == 0 {
            fmt.Println("數據讀取完畢....")
            break
        }
        if err != nil {
            fmt.Println("數據讀取失敗,錯誤原因: ", err)
            return
        }
        data += string(buf[:n])
    }

    fmt.Printf("從服務端獲取到的內容是: [%s]\n", data)
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM