HTTP协议

HTTP协议是超文本传输协议,工作在应用层,它是基于TCP/IP通信协议来传输数据。
HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。
17.2、HTTP - 图1
主要特点有:

  • 1、简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
  • 2、灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
  • 3.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  • 4.无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
  • 5、支持B/S及C/S模式。

对其实现主要是客户端和服务端得实现,在Go语言中使用net/http包来实现服务端和客户端。

HTTP服务端

服务端的处理流程与Socket一样:

  • 监听
  • 等待请求
  • 处理请求

    默认Server

    ListenAndServe使用指定的监听地址和处理器启动一个HTTP服务端。处理器参数通常是nil,这表示采用包变量DefaultServeMux作为处理器。
    Handle和HandleFunc函数可以向DefaultServeMux添加处理器。
    1. http.Handle("/foo", fooHandler)
    2. http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
    3. fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
    4. })
    5. log.Fatal(http.ListenAndServe(":8080", nil))

示例:

  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. )
  6. func helloWorld(response http.ResponseWriter, request *http.Request) {
  7. fmt.Println("Hello World")
  8. response.Write([]byte("你好"))
  9. }
  10. func main() {
  11. // 接受请求并处理
  12. http.HandleFunc("/", helloWorld)
  13. // 监听
  14. err := http.ListenAndServe(":9000", nil)
  15. if err != nil {
  16. fmt.Println("http server listen failed. err", err)
  17. return
  18. }
  19. }

然后启动服务,在浏览器访问:
image.png

HTTP客户端

客户端主要是发一些GETPOST请求等,在Go语言中使用GetHeadPostPostForm函数来发送HTTP/HTTPS请求。

  1. resp, err := http.Get("http://example.com/")
  2. ...
  3. resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)
  4. ...
  5. resp, err := http.PostForm("http://example.com/form",
  6. url.Values{"key": {"Value"}, "id": {"123"}})

程序在使用完response后必须关闭回复的主体。

  1. resp, err := http.Get("http://example.com/")
  2. if err != nil {
  3. // handle error
  4. }
  5. defer resp.Body.Close()
  6. body, err := ioutil.ReadAll(resp.Body)
  7. // ...

Get请求

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/http"
  6. )
  7. func main(){
  8. resp,err:=http.Get("https://www.baidu.com")
  9. if err != nil {
  10. fmt.Println("请求失败. err:",err)
  11. return
  12. }
  13. defer resp.Body.Close()
  14. fmt.Println(resp.Status)
  15. // 读取王内容
  16. content,err:=ioutil.ReadAll(resp.Body)
  17. if err != nil {
  18. fmt.Println("读取文件内容失败. err",err)
  19. return
  20. }
  21. fmt.Println(string(content))
  22. }

带参数的Get请求

用上面的方法也可以带参数,如下:

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/http"
  6. )
  7. func main(){
  8. resp,err:=http.Get("https://baike.baidu.com/item/%E6%96%B0%E5%9E%A3%E7%BB%93%E8%A1%A3/8035884?fr=aladdin")
  9. if err != nil {
  10. fmt.Println("请求失败. err:",err)
  11. return
  12. }
  13. defer resp.Body.Close()
  14. fmt.Println(resp.Status)
  15. // 读取王内容
  16. content,err:=ioutil.ReadAll(resp.Body)
  17. if err != nil {
  18. fmt.Println("读取文件内容失败. err",err)
  19. return
  20. }
  21. fmt.Println(string(content))
  22. }

但是这种很多就不灵活。Go语言中net/url包可以用来处理加参数的请求。
如下:
Client端

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/http"
  6. "net/url"
  7. )
  8. func main() {
  9. // 请求的url
  10. reqURL := "http://127.0.0.1:9000/get"
  11. // 构造请求数据
  12. data := url.Values{}
  13. // 向里面放入值
  14. data.Set("name", "joker")
  15. data.Set("age", "20")
  16. // 解析上面的url
  17. u, err := url.ParseRequestURI(reqURL)
  18. if err != nil {
  19. fmt.Println("url解析失败. err:", err)
  20. return
  21. }
  22. // 对需要传输的数据进行编码
  23. u.RawQuery = data.Encode()
  24. fmt.Println(u.String())
  25. // 发送请求
  26. resp, err := http.Get(u.String())
  27. if err != nil {
  28. fmt.Println("请求失败. err:", err)
  29. return
  30. }
  31. fmt.Println(resp.Status)
  32. defer resp.Body.Close()
  33. // 读取请求到的内容
  34. bn, err := ioutil.ReadAll(resp.Body)
  35. if err != nil {
  36. fmt.Println("读取内容失败。err:", err)
  37. return
  38. }
  39. // 输出内容
  40. fmt.Println(string(bn))
  41. }

Server端

  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. )
  6. func helloWorld(response http.ResponseWriter, request *http.Request) {
  7. fmt.Println("Hello World")
  8. response.Write([]byte("你好"))
  9. }
  10. func getTest(response http.ResponseWriter, request *http.Request) {
  11. defer request.Body.Close()
  12. fmt.Println(request.URL.Query())
  13. answer := []byte("ok")
  14. response.Write(answer)
  15. }
  16. func main() {
  17. // 接受请求并处理
  18. http.HandleFunc("/", helloWorld)
  19. http.HandleFunc("/get", getTest)
  20. // 监听
  21. err := http.ListenAndServe(":9000", nil)
  22. if err != nil {
  23. fmt.Println("http server listen failed. err", err)
  24. return
  25. }
  26. }

POST请求

Client端

  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. "strings"
  6. )
  7. func main() {
  8. // post请求url
  9. postURL := "http://127.0.0.1:9000/post"
  10. // 定义文件类型
  11. contentType := "application/json"
  12. // 定义post的数据
  13. data := `{"name":"joker","age":20}`
  14. // 发送post请求
  15. resp, err := http.Post(postURL, contentType, strings.NewReader(data))
  16. if err != nil {
  17. fmt.Println("post请求失败。err:", err)
  18. return
  19. }
  20. defer resp.Body.Close()
  21. fmt.Println(resp.Status)
  22. }

Server端 :

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/http"
  6. )
  7. func helloWorld(response http.ResponseWriter, request *http.Request) {
  8. fmt.Println("Hello World")
  9. response.Write([]byte("你好"))
  10. }
  11. func getTest(response http.ResponseWriter, request *http.Request) {
  12. defer request.Body.Close()
  13. fmt.Println(request.URL.Query())
  14. answer := []byte("ok")
  15. response.Write(answer)
  16. }
  17. func postTest(response http.ResponseWriter, request *http.Request) {
  18. defer request.Body.Close()
  19. // 解析form数据
  20. request.ParseForm()
  21. fmt.Println(request.ParseForm())
  22. fmt.Println(request.PostForm.Get("name"), request.PostForm.Get("age"))
  23. b, err := ioutil.ReadAll(request.Body)
  24. if err != nil {
  25. fmt.Println("数据读取失败。err:", err)
  26. return
  27. }
  28. fmt.Println(string(b))
  29. }
  30. func main() {
  31. // 接受请求并处理
  32. http.HandleFunc("/", helloWorld)
  33. http.HandleFunc("/get", getTest)
  34. http.HandleFunc("/post", postTest)
  35. // 监听
  36. err := http.ListenAndServe(":9000", nil)
  37. if err != nil {
  38. fmt.Println("http server listen failed. err", err)
  39. return
  40. }
  41. }

自定义Client

要管理HTTP客户端的头域、重定向策略和其他设置,创建一个Client:

  1. package main
  2. import (
  3. "net/http"
  4. "strings"
  5. "fmt"
  6. "io/ioutil"
  7. "log"
  8. "encoding/json"
  9. )
  10. func main() {
  11. client := &http.Client{}
  12. req, err := http.NewRequest("POST", "http://www.maimaiche.com/loginRegister/login.do",
  13. strings.NewReader("mobile=xxxxxxxxx&isRemberPwd=1"))
  14. if err != nil {
  15. log.Println(err)
  16. return
  17. }
  18. req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
  19. resp, err := client.Do(req)
  20. defer resp.Body.Close()
  21. body, err := ioutil.ReadAll(resp.Body)
  22. if err != nil {
  23. log.Println(err)
  24. return
  25. }
  26. fmt.Println(resp.Header.Get("Content-Type")) //application/json;charset=UTF-8
  27. type Result struct {
  28. Msg string
  29. Status string
  30. Obj string
  31. }
  32. result := &Result{}
  33. json.Unmarshal(body, result) //解析json字符串
  34. if result.Status == "1" {
  35. fmt.Println(result.Msg)
  36. } else {
  37. fmt.Println("login error")
  38. }
  39. fmt.Println(result)
  40. }

自定义Transport

要管理代理、TLS配置、keep-alive、压缩和其他设置,创建一个Transport:

  1. tr := &http.Transport{
  2. TLSClientConfig: &tls.Config{RootCAs: pool},
  3. DisableCompression: true,
  4. }
  5. client := &http.Client{Transport: tr}
  6. resp, err := client.Get("https://example.com")

Client和Transport类型都可以安全的被多个goroutine同时使用。出于效率考虑,应该一次建立、尽量重用。