Go語言學習筆記(六)net & net/http

加 Golang學習 QQ羣共同窗習進步成家立業工做 ^-^ 羣號:96933959html

net

import "net"json

net包提供了可移植的網絡I/O接口,包括TCP/IP、UDP、域名解析和Unix域socket。安全

雖然本包提供了對網絡原語的訪問,大部分使用者只須要Dial、Listen和Accept函數提供的基本接口;以及相關的Conn和Listener接口。crypto/tls包提供了相同的接口和相似的Dial和Listen函數。網絡

 

Listen函數建立的服務端:less

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函數和服務端創建鏈接:socket

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接口。tcp

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鏈接,請求百度post

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
)
相關文章
相關標籤/搜索