zoukankan      html  css  js  c++  java
  • Go语言学习笔记(六)net & net/http

    加 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
    
    ")
    status, err := bufio.NewReader(conn).ReadString('
    ')
    // ...

    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('
    ')
            trimedInput := strings.Trim(input, "
    ")
            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
    "
        msg += "Host: www.baidu.com
    "
        msg += "Connection: close
    "
        // msg += "Connection: keep-alive
    "
        msg += "
    
    "
    
        _, 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.
    ")
    }
    
    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
    ", v, err)
                continue
            }
            fmt.Printf("head %s succ, status: %v
    ", 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, "
    ")
            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
    )
  • 相关阅读:
    SDUT 1488 数据结构实验:连通分量个数
    SDUT 3364 数据结构实验之图论八:欧拉回路
    SDUT 2413 n a^o7 !
    SDUT 3363 数据结构实验之图论七:驴友计划
    SDUT 3362 数据结构实验之图论六:村村通公路
    SDUT 2139 数据结构实验之图论五:从起始点到目标点的最短步数(BFS)
    POJ 3278 Catch That Cow
    SDUT 3361 数据结构实验之图论四:迷宫探索
    SDUT 2107 数据结构实验之图论二:图的深度遍历
    SDUT 2142 数据结构实验之图论二:基于邻接表的广度优先搜索遍历
  • 原文地址:https://www.cnblogs.com/xumaojun/p/8547503.html
Copyright © 2011-2022 走看看