暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Golang避坑系列 - net & net/http

Golang开发 2021-08-27
972


头条真实面试题

Go中的http包的使用?


Golang中http包中处理 HTTP 请求主要跟两个东西相关:ServeMux 和 Handler。

ServeMux 本质上是一个 HTTP 请求路由器(或者叫多路复用器,Multiplexor)。它把收到的请求与一组预先定义的 URL 路径列表做对比,然后在匹配到路径的时候调用关联的处理器(Handler)。

处理器(Handler)负责输出HTTP响应的头和正文。任何满足了http.Handler接口的对象都可作为一个处理器。通俗的说,对象只要有个如下签名的ServeHTTP方法即可:

ServeHTTP(http.ResponseWriter, *http.Request)

Go 语言的 HTTP 包自带了几个函数用作常用处理器,比如 FileServer
NotFoundHandler
 和 RedirectHandler

应用示例:

    package main


    import (
    "log"
    "net/http"
    )


    func main() {
    mux := http.NewServeMux()


    rh := http.RedirectHandler("http://www.baidu.com", 307)
    mux.Handle("/foo", rh)


    log.Println("Listening...")
    http.ListenAndServe(":3000", mux)
    }

    在这个应用示例中,首先在 main 函数中我们只用了 http.NewServeMux
     函数来创建一个空的 ServeMux
    。 然后我们使用 http.RedirectHandler
     函数创建了一个新的处理器,这个处理器会对收到的所有请求,都执行307重定向操作到 http://www.baidu.com

    接下来我们使用 ServeMux.Handle
     函数将处理器注册到新创建的 ServeMux
    ,所以它在 URL 路径 /foo
     上收到所有的请求都交给这个处理器。 最后我们创建了一个新的服务器,并通过 http.ListenAndServe
     函数监听所有进入的请求,通过传递刚才创建的 ServeMux
    来为请求去匹配对应处理器。

    在浏览器中访问 http://localhost:3000/foo
    ,你应该能发现请求已经成功的重定向了。

    此刻你应该能注意到一些有意思的事情:ListenAndServer
     的函数签名是 ListenAndServe(addr string, handler Handler)
     ,但是第二个参数我们传递的是个 ServeMux

    通过这个例子我们就可以知道,net/http
    包在编写golang web应用中有很重要的作用,它主要提供了基于HTTP协议进行工作的client实现和server实现,可用于编写HTTP服务端和客户端。


    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""baidu.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 }}



                                        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
                                          )


                                          文章转载自Golang开发,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                          评论