go語(yǔ)言中的socket和http
【摘要】 Socket 編程以前我們使用Socket編程時(shí),會(huì)按照如下步驟展開。(1) 建立Socket:使用socket()函數(shù)。(2) 綁定Socket:使用bind()函數(shù)。(3) 監(jiān)聽:使用listen()函數(shù)。或者連接:使用connect()函數(shù)。(4) 接受連接:使用accept()函數(shù)。(5) 接收:使用receive()函數(shù)。或者發(fā)送:使用send()函數(shù)。Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)對(duì)此過程進(jìn)行...
Socket 編程以前我們使用Socket編程時(shí),會(huì)按照如下步驟展開。
(1) 建立Socket:使用socket()函數(shù)。
(2) 綁定Socket:使用bind()函數(shù)。
(3) 監(jiān)聽:使用listen()函數(shù)。或者連接:使用connect()函數(shù)。
(4) 接受連接:使用accept()函數(shù)。
(5) 接收:使用receive()函數(shù)。或者發(fā)送:使用send()函數(shù)。
Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)對(duì)此過程進(jìn)行了抽象和封裝。無(wú)論我們期望使用什么協(xié)議建立什么形式的連接,都只需要調(diào)用net.Dial()即可。
Dial()函數(shù)
Dial()函數(shù)的原型如下:
func Dial(net, addr string) (Conn, error)
其中net參數(shù)是網(wǎng)絡(luò)協(xié)議的名字,addr參數(shù)是IP地址或域名,而端口號(hào)以“:”的形式跟隨在地址或域名的后面,端口號(hào)可選。如果連接成功,返回連接對(duì)象,否則返回error。
TCP鏈接:
conn, err := net.Dial(“tcp”, “192.168.0.10:2100”)
UDP鏈接:
conn, err := net.Dial(“udp”, “192.168.0.12:975”)
ICMP鏈接:
conn, err := net.Dial(“ip4:icmp”, “www.baidu.com”)
ICMP鏈接:
conn, err := net.Dial(“ip4:1”, “10.0.0.3”)
鏈接查看協(xié)議編號(hào)的含義:http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml。
目前,Dial()函數(shù)支持如下幾種網(wǎng)絡(luò)協(xié)議:“tcp”、“tcp4”(僅限IPv4)、“tcp6”(僅限IPv6)、“udp”、“udp4”(僅限IPv4)、“udp6”(僅限IPv6)、“ip”、“ip4”(僅限IPv4)和"ip6"
(僅限IPv6)。
在成功建立連接后,我們就可以進(jìn)行數(shù)據(jù)的發(fā)送和接收。發(fā)送數(shù)據(jù)時(shí),使用conn的Write()成員方法,接收數(shù)據(jù)時(shí)使用Read()方法。
ICMP示例程序
下面我們實(shí)現(xiàn)這樣一個(gè)例子:我們使用ICMP協(xié)議向在線的主機(jī)發(fā)送一個(gè)問候,并等待主機(jī)返回。
package main
import (
"net"
"os"
"bytes"
"fmt"
)
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: ", os.Args[0], "host")
os.Exit(1)
}
service := os.Args[1]
conn, err := net.Dial("ip4:icmp", service)
checkError(err)
var msg [512]byte
msg[0] = 8 // echo
msg[1] = 0 // code 0
msg[2] = 0 // checksum
msg[3] = 0 // checksum
msg[4] = 0 // identifier[0]
msg[5] = 13 //identifier[1]
msg[6] = 0 // sequence[0]
msg[7] = 37 // sequence[1]
len := 8
check := checkSum(msg[0:len])
msg[2] = byte(check >> 8)
msg[3] = byte(check & 255)
_, err = conn.Write(msg[0:len])
checkError(err)
_, err = conn.Read(msg[0:])
checkError(err)
fmt.Println("Got response")
if msg[5] == 13 {
fmt.Println("Identifier matches")
}
if msg[7] == 37 {
fmt.Println("Sequence matches")
}
os.Exit(0)
}
func checkSum(msg []byte) uint16 {
sum := 0
// 先假設(shè)為偶數(shù)
for n := 1; n <len(msg)-1; n += 2 {
sum += int(msg[n])*256 + int(msg[n+1])
}
sum = (sum >> 16) + (sum & 0xffff)
sum += (sum >> 16)
var answer uint16 = uint16(^sum)
return answer
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)
}
}
func readFully(conn net.Conn) ([]byte, error) {
defer conn.Close()
result := bytes.NewBuffer(nil)
var buf [512]byte
for {
n, err := conn.Read(buf[0:])
result.Write(buf[0:n])
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
}
return result.Bytes(), nil
}
執(zhí)行結(jié)果如下:
$ go build icmptest.go
$ ./icmptest www.baidu.com
Got response
Identifier matches
Sequence matches
TCP示例程序
下面我們建立TCP鏈接來(lái)實(shí)現(xiàn)初步的HTTP協(xié)議,通過向網(wǎng)絡(luò)主機(jī)發(fā)送HTTP Head請(qǐng)求,讀取網(wǎng)絡(luò)主機(jī)返回的信息
package main
import (
"net"
"os"
"bytes"
"fmt"
)
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s host:port", os.Args[0])
os.Exit(1)
}
service := os.Args[1]
conn, err := net.Dial("tcp", service)
checkError(err)
_, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
checkError(err)
result, err := readFully(conn)
checkError(err)
fmt.Println(string(result))
os.Exit(0)
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)
}
}
func readFully(conn net.Conn) ([]byte, error) {
defer conn.Close()
result := bytes.NewBuffer(nil)
var buf [512]byte
for {
n, err := conn.Read(buf[0:])
result.Write(buf[0:n])
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
}
return result.Bytes(), nil
}
執(zhí)行這段程序并查看執(zhí)行結(jié)果:
$ go build simplehttp.go
$ ./simplehttp qbox.me:80
HTTP/1.1 301 Moved Permanently
Server: nginx/1.0.14
Date: Mon, 21 May 2012 03:15:08 GMT
Content-Type: text/html
Content-Length: 184
Connection: close
Location: https://qbox.me/
HTTP(HyperText Transfer Protocol,超文本傳輸協(xié)議)是互聯(lián)網(wǎng)上應(yīng)用最為廣泛的一種網(wǎng)絡(luò)協(xié)議,定義了客戶端和服務(wù)端之間請(qǐng)求與響應(yīng)的傳輸標(biāo)準(zhǔn)。
Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)內(nèi)建提供了net/http包,涵蓋了HTTP客戶端和服務(wù)端的具體實(shí)現(xiàn)。使用net/http包,我們可以很方便地編寫HTTP客戶端或服務(wù)端的程序。
HTTP客戶端
Go內(nèi)置的net/http包提供了最簡(jiǎn)潔的HTTP客戶端實(shí)現(xiàn),
基本方法
net/http包的Client類型提供了如下幾個(gè)方法,讓我們可以用最簡(jiǎn)潔的方式實(shí)現(xiàn) HTTP 請(qǐng)求:
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()
要請(qǐng)求一個(gè)資源,只需調(diào)用http.Get()方法(等價(jià)于http.DefaultClient.Get())即可,示例代碼如下:
resp, err := http.Get("http://example.com/")
if err != nil {
// 處理錯(cuò)誤 ...
return
}
defer resp.Body.close()
io.Copy(os.Stdout, resp.Body)
? http.Post()
要以POST的方式發(fā)送數(shù)據(jù),也很簡(jiǎn)單,只需調(diào)用http.Post()方法并依次傳遞下面的3個(gè)參數(shù)即可:
? 請(qǐng)求的目標(biāo) URL
? 將要 POST 數(shù)據(jù)的資源類型(MIMEType)
? 數(shù)據(jù)的比特流([]byte形式)
下面的示例代碼演示了如何上傳一張圖片:
resp, err := http.Post("http://example.com/upload", "image/jpeg", &imageDataBuf)
if err != nil {
// 處理錯(cuò)誤
return
}
if resp.StatusCode != http.StatusOK {
// 處理錯(cuò)誤
return
}
? http.PostForm()
http.PostForm()方法實(shí)現(xiàn)了標(biāo)準(zhǔn)編碼格式為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 {
// 處理錯(cuò)誤
return
}
? **http.Head() **
HTTP 中的 Head 請(qǐng)求方式表明只請(qǐng)求目標(biāo) URL 的頭部信息,即 HTTP Header 而不返回 HTTP Body。Go 內(nèi)置的 net/http 包同樣也提供了 http.Head() 方法,該方法同 http.Get() 方法一樣,只需傳入目標(biāo) URL 一個(gè)參數(shù)即可。下面的示例代碼請(qǐng)求一個(gè)網(wǎng)站首頁(yè)的 HTTP Header信息:
resp, err := http.Head("http://example.com/")
? *(http.Client).Do()
在多數(shù)情況下,http.Get()和http.PostForm() 就可以滿足需求,但是如果我們發(fā)起的HTTP 請(qǐng)求需要更多的定制信息,我們希望設(shè)定一些自定義的 Http Header 字段,比如:
? 設(shè)定自定義的"User-Agent",而不是默認(rèn)的 “Go http package”
? 傳遞 Cookie
此時(shí)可以使用net/http包http.Client對(duì)象的Do()方法來(lái)實(shí)現(xiàn):
req, err := http.NewRequest("GET", "http://example.com", nil)
// ...
req.Header.Add("User-Agent", "Gobook Custom User-Agent")
// ...
client := &http.Client{ //... }
resp, err := client.Do(req)
// ...
鏈接:https://bbs.huaweicloud.com/blogs/422710
( 版權(quán)歸華為云社區(qū) 原作者所有,侵刪)

