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