Go語言七篇入門教程六網絡編程
1. Socket 編程
在 Go 語言中編寫網絡程序時,我們將看不到傳統的編碼形式。以前我們使用 Socket 編程時,會按照如下步驟展開。
建立 Socket:使用 socket()
函數。
綁定 Socket:使用 bind()
函數。
監聽:使用 listen()
函數。或者連接:使用 connect()
函數。
接受連接:使用 accept()
函數。
接收:使用 receive()
函數。或者發送:使用 send()
函數。
Go 語言標準庫對此過程進行瞭抽象和封裝。無論我們期望使用什麼協議建立什麼形式的連接,都隻需要調用 net.Dial()
即可。
1.1 Dial()函數
Dial()函數的原型如下:
func Dial(net, addr string) (Conn, error)
其中 net
參數是網絡協議的名字,addr
參數是IP
地址或域名,而端口號以:
的形式跟隨在地址或域名的後面,端口號可選。如果連接成功,返回連接對象,否則返回 error
。
我們來看一下幾種常見協議的調用方式。
TCP
鏈接:
conn, err := net.Dial("tcp", "192.168.1.8:3000")
UDP
鏈接:
conn, err := net.Dial("udp", "192.168.1.12:975")
ICMP
鏈接(使用協議名稱):
conn, err := net.Dial("ip4:icmp", "www.baidu.com")
ICMP
鏈接(使用協議編號):
conn, err := net.Dial("ip4:1", "10.0.0.3")
在成功建立連接後,我們就可以進行數據的發送和接收。發送數據時,使用 conn
的Write()
成員方法,接收數據時使用 Read()
方法。
2. HTTP 編程
2.1 HTTP 客戶端
Go 內置的 net/http
包提供瞭最簡潔的 HTTP
客戶端實現,我們無需借助第三方網絡通信庫(比如 libcurl
)就可以直接使用 HTTP 中用得最多的 GET
和 POST
方式請求數據。
基本方法
net/http
包的 Client
類型提供瞭如下幾個方法,讓我們可以用最簡潔的方式實現HTTP
請求:
func (c *Client) Get(url string) (r *Response, err error) func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err error) func (c *Client) PostForm(url string, data url.Values) (r *Response, err error) func (c *Client) Head(url string) (r *Response, err error) func (c *Client) Do(req *Request) (resp *Response, err error)
下面概要介紹這幾個方法。
- http.Get()
要請求一個資源,隻需調用 http.Get()方法(等價於 http.DefaultClient.Get())即可,示例代碼如下:
resp, err := http.Get("http://example.com/") if err != nil { // 處理錯誤 ... return } defer resp.Body.close() io.Copy(os.Stdout, resp.Body)
上面這段代碼請求一個網站首頁,並將其網頁內容打印到標準輸出流中。
- http.Post()
要以POST
的方式發送數據,也很簡單,隻需調用 http.Post()
方法並依次傳遞下面的 3 個參數即可:
請求的目標 URL將要 POST 數據的資源類型(MIMEType)數據的比特流([]byte 形式)
下面的示例代碼演示瞭如何上傳一張圖片:
resp, err := http.Post("http://example.com/upload", "image/jpeg", &imageDataBuf) if err != nil{ // 處理錯誤 return } if resp.StatusCode != http.StatusOK {// 處理錯誤 return }
- http.PostForm()
http.PostForm()
方法實現瞭標準編碼格式為 application/x-www-form-urlencoded
的表單提交。下面的示例代碼模擬 HTML 表單提交一篇新文章:
resp, err := http.PostForm("http://example.com/posts", url.Values{"title": {"article title"}, "content": {"article body"}}) if err != nil{ // 處理錯誤 return }
- http.Head()
HTTP 中的 Head 請求方式表明隻請求目標 URL 的頭部信息,即 HTTP Header 而不返回 HTTP
Body。Go 內置的 net/http
包同樣也提供瞭 http.Head()
方法,該方法同 http.Get()
方法一樣,
隻需傳入目標 URL 一個參數即可。下面的示例代碼請求一個網站首頁的 HTTP Header 信息:
resp, err := http.Head("http://baidu.com/")
- (*http.Client).Do()
在多數情況下,http.Get()
和 http.PostForm()
就可以滿足需求,但是如果我們發起的
HTTP 請求需要更多的定制信息,我們希望設定一些自定義的 Http Header 字段,比如:
- 設定自定義的”User-Agent”
- 傳遞 Cookie
此時可以使用 net/http
包 http.Client
對象的 Do()
方法來實現
req, err := http.NewRequest("GET", "http://baidu.com", nil) // ... req.Header.Add("User-Agent", "Gobook Custom User-Agent") // ... client := &http.Client{ //... } resp, err := client.Do(req)
2.2 HTTP 服務端
2.2.1 處理 HTTP 請求
使用 net/http
包提供的 http.ListenAndServe()
方法,可以在指定的地址進行監聽,開啟一個 HTTP,服務端該方法的原型如下:
func ListenAndServe(addr string, handler Handler) error
該方法用於在指定的 TCP
網絡地址 addr
進行監聽,然後調用服務端處理程序來處理傳入的連接請求。該方法有兩個參數:第一個參數 addr
即監聽地址;第二個參數表示服務端處理程序,通常為空,這意味著服務端調用 http.DefaultServeMux
進行處理,而服務端編寫的業務邏輯處理程序 http.Handle()
或 http.HandleFunc()
默認註入 http.DefaultServeMux
中,具體代碼如下:
http.Handle("/foo", fooHandler) http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) }) log.Fatal(http.ListenAndServe(":8080", nil))
net/http
包還提供 http.ListenAndServeTLS()
方法,用於處理 HTTPS
連接請求:
func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) error
ListenAndServeTLS()
和ListenAndServe()
的行為一致,區別在於隻處理 HTTPS
請求。此外,服務器上必須存在包含證書和與之匹配的私鑰的相關文件,比如 certFile
對應 SSL證書
文件存放路徑,keyFile
對應證書私鑰文件路徑。如果證書是由證書頒發機構簽署的,certFile
參數指定的路徑必須是存放在服務器上的經由 CA
認證過的 SSL 證書
。
3. RPC 編程
在 Go 中,標準庫提供的 net/rpc 包實現瞭 RPC 協議需要的相關細節,開發者可以很方便地使用該包編寫 RPC 的服務端和客戶端程序,這使得用 Go 語言開發的多個進程之間的通信變得非常簡單。
net/rpc
包允許 RPC
客戶端程序通過網絡或是其他 I/O 連接調用一個遠端對象的公開方法(必須是大寫字母開頭、可外部調用的)。在 RPC 服務端,可將一個對象註冊為可訪問的服務,之後該對象的公開方法就能夠以遠程的方式提供訪問。一個 RPC 服務端可以註冊多個不同類型的對象,但不允許註冊同一類型的多個對象。
一個對象中隻有滿足如下這些條件的方法,才能被 RPC 服務端設置為可供遠程訪問:
- 必須是在對象外部可公開調用的方法(首字母大寫);
- 必須有兩個參數,且參數的類型都必須是包外部可以訪問的類型或者是 Go 內建支持的類型;
- 第二個參數必須是一個指針;
- 方法必須返回一個 error 類型的值。
以上 4 個條件,可以簡單地用如下一行代碼表示:
func (t *T) MethodName(argType T1, replyType *T2) error
在上面這行代碼中,類型 T、T1 和 T2 默認會使用 Go 內置的 encoding/gob
包進行編碼解碼。
該方法(MethodName)的第一個參數表示由 RPC
客戶端傳入的參數,第二個參數表示要返回給 RPC
客戶端的結果,該方法最後返回一個 error
類型的值。
RPC
服務端可以通過調用 rpc.ServeConn
處理單個連接請求。多數情況下,通過 TCP
或是 HTTP
在某個網絡地址上進行監聽來創建該服務是個不錯的選擇。
3.1 Go 語言中的 RPC 支持與處理
在 Go 中,標準庫提供的net/rpc
包實現瞭RPC
協議需要的相關細節,開發者可以很方便地使用該包編寫 RPC 的服務端和客戶端程序,這使得用 Go 語言開發的多個進程之間的通信變得非常簡單。
net/rpc
包允許 RPC
客戶端程序通過網絡或是其他 I/O 連接調用一個遠端對象的公開方法(必須是大寫字母開頭、可外部調用的)。在RPC
服務端,可將一個對象註冊為可訪問的服務,之後該對象的公開方法就能夠以遠程的方式提供訪問。一個RPC
服務端可以註冊多個不同類型的對象,但不允許註冊同一類型的多個對象。
一個對象中隻有滿足如下這些條件的方法,才能被RPC
服務端設置為可供遠程訪問:
- 必須是在對象外部可公開調用的方法(首字母大寫);
- 必須有兩個參數,且參數的類型都必須是包外部可以訪問的類型或者是
Go
內建支持的類型; - 第二個參數必須是一個指針;
- 方法必須返回一個 error 類型的值。
以上 4 個條件,可以簡單地用如下一行代碼表示:
func (t *T) MethodName(argType T1, replyType *T2) error
在上面這行代碼中,類型 T、T1 和 T2 默認會使用 Go 內置的 encoding/gob
包進行編碼解碼。關於 encoding/gob
包的內容,稍後我們將會對其進行介紹。
該方法(MethodName)的第一個參數表示由 RPC 客戶端傳入的參數,第二個參數表示要返回給 RPC
客戶端的結果,該方法最後返回一個 error 類型的值。
RPC
服務端可以通過調用 rpc.ServeConn
處理單個連接請求。多數情況下,通過 TCP
或是 HTTP
在某個網絡地址上進行監聽來創建該服務是個不錯的選擇。
3.2 Gob 簡介
Gob
是 Go 的一個序列化數據結構的編碼解碼工具,在Go
標準庫中內置 encoding/gob
包以供使用。一個數據結構使用 Gob
進行序列化之後,能夠用於網絡傳輸。
與JSON
或XML
這種基於文本描述的數據交換語言不同,Gob
是二進制編碼的數據流,並且 Gob
流是可以自解釋的,它在保證高效率的同時,也具備完整的表達能力。
作為針對 Go
的數據結構進行編碼和解碼的專用序列化方法,這意味著 Gob
無法跨語言使用。在 Go
的 net/rpc
包中,傳輸數據所需要用到的編碼解碼器,默認就是 Gob
。由於Gob
僅局限於使用 Go
語言開發的程序,這意味著我們隻能用 Go
的 RPC
實現進程間通信。
然而,大多數時候,我們用Go
編寫的 RPC
服務端(或客戶端),可能更希望它是通用的,與語言無關的,無論是 Python 、 Java 或其他編程語言實現的 RPC
客戶端,均可與之通信。
3.3 設計優雅的 RPC 接口
Go 的 net/rpc
很靈活,它在數據傳輸前後實現瞭編碼解碼器的接口定義。這意味著,開發者可以自定義數據的傳輸方式以及 RPC 服務端和客戶端之間的交互行為。
RPC
提供的編碼解碼器接口如下:
type ClientCodec interface { WriteRequest(*Request, interface{}) error ReadResponseHeader(*Response) error ReadResponseBody(interface{}) error Close() error } type ServerCodec interface { ReadRequestHeader(*Request) error ReadRequestBody(interface{}) error WriteResponse(*Response, interface{}) error Close() error }
接口 ClientCodec
定義瞭 RPC
客戶端如何在一個 RPC
會話中發送請求和讀取響應。客戶端程序通過 WriteRequest()
方法將一個請求寫入到 RPC 連接中,並通過ReadResponseHeader()
和 ReadResponseBody()
讀取服務端的響應信息。當整個過程執行完畢後,再通過 Close()
方法來關閉該連接。
接口 ServerCodec
定義瞭 RPC
服務端如何在一個 RPC
會話中接收請求並發送響應。服務端程序通過ReadRequestHeader()
和ReadRequestBody()
方法從一個 RPC 連接中讀取 請求信息,然後再通過WriteResponse() 方法向該連接中的 RPC 客戶端發送響應。當完成該過程後,通過 Close()
方法來關閉連接。
通過實現上述接口,我們可以自定義數據傳輸前後的編碼解碼方式,而不僅僅局限於Gob
。同樣,可以自定義RPC
服務端和客戶端的交互行為。實際上,Go
標準庫提供的 net/rpc/json
包,就是一套實現瞭 rpc.ClientCodec
和rpc.ServerCodec
接口的 JSON-RPC
模塊。
以上就是Go語言七篇入門教程六網絡編程第的詳細內容,更多關於Go語言網絡編程的資料請關註WalkonNet其它相關文章!
如何學習Go
如果你是小白,你可以這樣學習Go語言~
七篇入門Go語言
第一篇:Go簡介初識
第二篇:程序結構&&數據類型的介紹
第三篇:函數方法接口的介紹
第四篇:通道與Goroutine的並發編程
第五篇:文件及包的操作與處理
第七篇:GC垃圾回收三色標記