加 Golang學習 QQ群共同學習進步成家立業工作 ^-^ 群号:96933959
net
import "net"
net包提供了可移植的網絡I/O接口,包括TCP/IP、UDP、域名解析和Unix域socket。
雖然本包提供了對網絡原語的通路,大部分使用者隻需要Dial、Listen和Accept函數提供的基本接口;以及相關的Conn和Listener接口。crypto/tls包提供了相同的接口和類似的Dial和Listen函數。
Listen函數建立的服務端:
ln, err := net.Listen("tcp", ":8080")
if err != nil {
// handle error
}
for {
conn, err := ln.Accept()
if err != nil {
// handle error
continue
}
go handleConnection(conn)
}
Dial函數和服務端建立連接配接:
conn, err := net.Dial("tcp", "google.com:80")
if err != nil {
// handle error
}
fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n")
status, err := bufio.NewReader(conn).ReadString('\n')
// ...
TCPConn
TCPConn代表一個TCP網絡連接配接,實作了Conn接口。
Conn接口
Conn接口代表通用的面向流的網絡連接配接。多個線程可能會同時調用同一個Conn的方法。
type Conn interface {
// Read從連接配接中讀取資料
// Read方法可能會在超過某個固定時間限制後逾時傳回錯誤,該錯誤的Timeout()方法傳回真
Read(b []byte) (n int, err error)
// Write從連接配接中寫入資料
// Write方法可能會在超過某個固定時間限制後逾時傳回錯誤,該錯誤的Timeout()方法傳回真
Write(b []byte) (n int, err error)
// Close方法關閉該連接配接
// 并會導緻任何阻塞中的Read或Write方法不再阻塞并傳回錯誤
Close() error
// 傳回本地網絡位址
LocalAddr() Addr
// 傳回遠端網絡位址
RemoteAddr() Addr
// 設定該連接配接的讀寫deadline,等價于同時調用SetReadDeadline和SetWriteDeadline
// deadline是一個絕對時間,超過該時間後I/O操作就會直接因逾時失敗傳回而不會阻塞
// deadline對之後的所有I/O操作都起效,而不僅僅是下一次的讀或寫操作
// 參數t為零值表示不設定期限
SetDeadline(t time.Time) error
// 設定該連接配接的讀操作deadline,參數t為零值表示不設定期限
SetReadDeadline(t time.Time) error
// 設定該連接配接的寫操作deadline,參數t為零值表示不設定期限
// 即使寫入逾時,傳回值n也可能>0,說明成功寫入了部分資料
SetWriteDeadline(t time.Time) error
}
栗子一(tcp)
tcp服務端
package main
import (
"fmt"
"net"
)
func process(conn net.Conn) {
defer conn.Close()
for {
buf := make([]byte, 512)
n, err := conn.Read(buf)
if err != nil {
fmt.Println("read err:", err)
return
}
fmt.Println("read:", string(buf[:n]))
}
}
func main() {
fmt.Println("server start...")
listen, err := net.Listen("tcp", "0.0.0.0:8000")
if err != nil {
fmt.Println("listen failed, err:", err)
return
}
for {
conn, err := listen.Accept()
if err != nil {
fmt.Println("accept failed, err:", err)
continue
}
go process(conn)
}
}
tcp用戶端
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
)
func main() {
conn, err := net.Dial("tcp", "localhost:8000")
if err != nil {
fmt.Println("err dialing:", err.Error())
return
}
defer conn.Close()
inputReader := bufio.NewReader(os.Stdin)
for {
input, _ := inputReader.ReadString('\n')
trimedInput := strings.Trim(input, "\r\n")
if trimedInput == "Q" {
return
}
_, err := conn.Write([]byte(trimedInput))
if err != nil {
fmt.Println("err conn.write:", err)
return
}
}
}
栗子二(http)
封裝一個http連接配接,請求百度
package main
import (
"fmt"
"io"
"net"
)
func main() {
conn, err := net.Dial("tcp", "www.baidu.com:80")
if err != nil {
fmt.Println("err dialing:", err.Error())
return
}
defer conn.Close()
msg := "GET / HTTP/1.1\r\n"
msg += "Host: www.baidu.com\r\n"
msg += "Connection: close\r\n"
// msg += "Connection: keep-alive\r\n"
msg += "\r\n\r\n"
_, err = io.WriteString(conn, msg)
if err != nil {
fmt.Println("io write string failed, err:", err)
return
}
buf := make([]byte, 4096)
for {
count, err := conn.Read(buf)
if err != nil {
break
}
fmt.Println(string(buf[:count]))
}
}
net/http
import "net/http"
http包提供了HTTP用戶端和服務端的實作。
Get、Head、Post和PostForm函數發出HTTP/ HTTPS請求。
resp, err := http.Get("http://example.com/")
...
resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)
...
resp, err := http.PostForm("http://example.com/form",
url.Values{"key": {"Value"}, "id": {"123"}})
程式在使用完回複後必須關閉回複的主體。
resp, err := http.Get("http://example.com/")
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
// ...
要管理HTTP用戶端的頭域、重定向政策和其他設定,建立一個Client:
client := &http.Client{
CheckRedirect: redirectPolicyFunc,
}
resp, err := client.Get("http://example.com")
// ...
req, err := http.NewRequest("GET", "http://example.com", nil)
// ...
req.Header.Add("If-None-Match", `W/"wyzzy"`)
resp, err := client.Do(req)
// ...
要管理代理、TLS配置、keep-alive、壓縮和其他設定,建立一個Transport:
tr := &http.Transport{
TLSClientConfig: &tls.Config{RootCAs: pool},
DisableCompression: true,
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://example.com")
Client和Transport類型都可以安全的被多個go程同時使用。出于效率考慮,應該一次建立、盡量重用。
ListenAndServe使用指定的監聽位址和處理器啟動一個HTTP服務端。處理器參數通常是nil,這表示采用包變量DefaultServeMux作為處理器。Handle和HandleFunc函數可以向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))
要管理服務端的行為,可以建立一個自定義的Server:
s := &http.Server{
Addr: ":8080",
Handler: myHandler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
log.Fatal(s.ListenAndServe())
栗子server
import (
"fmt"
"net/http"
)
func Hello(w http.ResponseWriter, r *http.Request) {
fmt.Println("Hello World.")
fmt.Fprintf(w, "Hello World.\n")
}
func main() {
http.HandleFunc("/", Hello)
err := http.ListenAndServe("0.0.0.0:6000", nil)
if err != nil {
fmt.Println("http listen failed.")
}
}
栗子client
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
res, err := http.Get("http://www.baidu.com")
if err != nil {
fmt.Println("Get error:", err)
return
}
data, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println("Get data error:", err)
return
}
fmt.Println(string(data))
}
栗子head
import (
"fmt"
"net/http"
"time"
)
var url = []string{
"http://www.baidu.com",
"http://www.google.com",
"http://pan.263.net",
}
func main() {
for _, v := range url {
http.DefaultClient.Timeout = time.Second * 2
resp, err := http.Head(v)
if err != nil {
fmt.Printf("head %s failed, err: %v\n", v, err)
continue
}
fmt.Printf("head %s succ, status: %v\n", v, resp.Status)
}
}
栗子(form)
import (
"fmt"
"io"
"log"
"net/http"
)
const form = `
<html>
<body>
<form action="#" method="post" name="bar">
<input type="text" name="in"/>
<input type="text" name="in"/>
<input type="submit" value="Submit"/>
</form>
</body>
</html>
`
func SimpleServer(w http.ResponseWriter, request *http.Request) {
io.WriteString(w, "Hello World.")
}
func FormServer(w http.ResponseWriter, request *http.Request) {
w.Header().Set("Content-Type", "text/html")
switch request.Method {
case "GET":
io.WriteString(w, form)
case "POST":
request.ParseForm()
io.WriteString(w, request.Form["in"][1])
io.WriteString(w, "\n")
io.WriteString(w, request.FormValue("in"))
}
}
func logPanics(handle http.HandlerFunc) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
defer func() {
if x := recover(); x != nil {
log.Printf("[%v] caught panic: %v", request.RemoteAddr, x)
}
}()
handle(writer, request)
}
}
func main() {
http.HandleFunc("/test1", SimpleServer)
http.HandleFunc("/test2", logPanics(FormServer))
err := http.ListenAndServe("0.0.0.0:6000", nil)
if err != nil {
fmt.Println("http listen failed.")
}
}
栗子(template)
package main
import (
"fmt"
"html/template"
"net/http"
)
type Person struct {
Title string
Name string
Age int
}
func SimpleServer(w http.ResponseWriter, request *http.Request) {
indexFailPath := "./index.html"
t, err := template.ParseFiles(indexFailPath)
if err != nil {
fmt.Println("parse file err:", err)
return
}
p := Person{Name: "Nick", Age: 18, Title: "Good."}
if err = t.Execute(w, p); err != nil {
fmt.Println("There was an error:", err.Error())
return
}
}
func main() {
http.HandleFunc("/test1", SimpleServer)
err := http.ListenAndServe("0.0.0.0:9000", nil)
if err != nil {
fmt.Println("http listen failed.")
}
}
<html>
<head>
<title>
{{.Title}}
</title>
</head>
<body>
<p>{{.Name}}</p>
{{if gt .Age 18}}
<p>MAN: {{.Name}}</p>
{{else}}
<p>Kid: {{.Name}}</p>
{{end}}
</body>
</html>
更多用法
- not 非 {{if not .condition}} {{end}}
- and 與 {{if and .condition1 .condition2}} {{end}}
- or 或 {{if or .condition1 .condition2}} {{end}}
- eq 等于 {{if eq .var1 .var2}} {{end}}
- ne 不等于 {{if ne .var1 .var2}} {{end}}
- lt 小于 (less than) {{if lt .var1 .var2}} {{end}}
- le 小于等于 {{if le .var1 .var2}} {{end}}
- gt 大于 {{if gt .var1 .var2}} {{end}}
- ge 大于等于 {{if ge .var1 .var2}} {{end}}
- range 循環 {{range.}} {{end }}
Appendix
大端位元組序的實作
data, err := json.Marshal("hello world")
if err != nil {
return
}
var buf [4]byte
packLen := uint32(len(data))
fmt.Println("packlen:", packLen)
// 前4個位元組表示data大小
binary.BigEndian.PutUint32(buf[0:4], packLen)
n, err := conn.Write(buf[:])
if err != nil || n != 4 {
fmt.Println("write data failed")
return
}
_, err = conn.Write([]byte(data))
if err != nil {
return
}
Http 狀态碼
const (
StatusContinue = 100
StatusSwitchingProtocols = 101
StatusOK = 200
StatusCreated = 201
StatusAccepted = 202
StatusNonAuthoritativeInfo = 203
StatusNoContent = 204
StatusResetContent = 205
StatusPartialContent = 206
StatusMultipleChoices = 300
StatusMovedPermanently = 301
StatusFound = 302
StatusSeeOther = 303
StatusNotModified = 304
StatusUseProxy = 305
StatusTemporaryRedirect = 307
StatusBadRequest = 400
StatusUnauthorized = 401
StatusPaymentRequired = 402
StatusForbidden = 403
StatusNotFound = 404
StatusMethodNotAllowed = 405
StatusNotAcceptable = 406
StatusProxyAuthRequired = 407
StatusRequestTimeout = 408
StatusConflict = 409
StatusGone = 410
StatusLengthRequired = 411
StatusPreconditionFailed = 412
StatusRequestEntityTooLarge = 413
StatusRequestURITooLong = 414
StatusUnsupportedMediaType = 415
StatusRequestedRangeNotSatisfiable = 416
StatusExpectationFailed = 417
StatusTeapot = 418
StatusInternalServerError = 500
StatusNotImplemented = 501
StatusBadGateway = 502
StatusServiceUnavailable = 503
StatusGatewayTimeout = 504
StatusHTTPVersionNotSupported = 505
)