zoukankan      html  css  js  c++  java
  • Golang http编程

    web 工作流程

    • Web 服务器的工作原理可以简单地归纳为
      • 客户机通过 TCP/IP 协议建立到服务器的 TCP 连接
      • 客户端向服务器发送 HTTP 协议请求包,请求服务器里的资源文档
      • 服务器向客户机发送 HTTP 协议应答包,如果请求的资源包含有动态语言的内容,那么服务器会调用动态语言的解释引擎负责处理“动态内容”,并将处理得到的数据返回给客户端
      • 客户机与服务器断开。由客户端解释 HTML 文档,在客户端屏幕上渲染图形结果

    HTTP 协议

    • 超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议,它详细规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议
    • HTTP 协议通常承载于 TCP 协议之上

    http 服务端

    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    // /go handler
    func goHandler(w http.ResponseWriter, r *http.Request) {
    	// 获取远程地址
    	fmt.Println(r.RemoteAddr, "连接成功")
    	// 获取请求类型
    	fmt.Println("请求方法: ", r.Method)
    	// 获取url路径
    	fmt.Println("url path: ", r.URL.Path)
    	// 获取header数据
    	fmt.Println("header: ", r.Header)
    	// 获取请求body内容
    	fmt.Println("body: ", r.Body)
    
    	// 返回信息
    	_, _ = w.Write([]byte("hello world"))
    }
    
    func main()  {
    	// 单独写回掉函数
    	http.HandleFunc("/go", goHandler)
    
    	// 启动http监听
    	_ = http.ListenAndServe("127.0.0.1:8000", nil)
    }
    

    http 包客户端

    package main
    
    import (
    	"fmt"
    	"io"
    	"net/http"
    )
    
    func main() {
    	resp, err := http.Get("http://127.0.0.1:8000/go")
    	if err != nil {
    		fmt.Println("获取数据失败, err: ", err)
    		return
    	}
    	defer resp.Body.Close()
    	// 获取http状态码
    	fmt.Println("status: ", resp.Status)
    	// 获取header信息
    	fmt.Println("header: ", resp.Status)
    
    	buf := make([]byte, 124)
    	for {
    		// 接收服务端数据
    		n, err := resp.Body.Read(buf)
    
    		fmt.Println(string(buf[:n]))
    
    		if err == io.EOF {
    			break
    		}
    
    		if err != nil {
    			fmt.Println("数据读取失败, err: ", err)
    			continue
    		}
    	}
    }
    

    tcp 包实现客户端

    package main
    
    import (
    	"fmt"
    	"io"
    	"net"
    )
    
    func main() {
    	conn, err := net.Dial("tcp", "127.0.0.1:8000")
    	if err != nil {
    		fmt.Println("connect err: ", err)
    		return
    	}
    	defer conn.Close()
    	msg := "GET /go HTTP/1.1
    "
    	msg += "Host: 127.0.0.1:8000
    "
    	msg += "Connection: close
    "
    	msg += "
    
    "
    
    	_, err = io.WriteString(conn, msg)
    	if err != nil {
    		fmt.Println("write string failed, ", err)
    		return
    	}
    	buf := make([]byte, 4096)
    	for {
    		n, err := conn.Read(buf)
    		if err == io.EOF {
    			break
    		}
    		if err != nil {
    			fmt.Println("recv data has err, ", err)
    			break
    		}
    		fmt.Println(string(buf[:n]))
    	}
    }
    

    其他错误

    golang GET 出现 x509: certificate signed by unknown authority

    我们编写一个 Go 程序来尝试与这个 HTTPS server 建立连接并通信。

    //gohttps/4-https/client1.go
    package main
    
    import (
        "fmt"
        "io/ioutil"
        "net/http"
    )
    
    func main() {
        resp, err := http.Get("https://localhost:8081")
        if err != nil {
            fmt.Println("error:", err)
            return
        }
        defer resp.Body.Close()
        body, err := ioutil.ReadAll(resp.Body)
        fmt.Println(string(body))
    }
    

    运行这个 client,我们得到如下错误:

    $go run client1.go
    error: Get https://localhost:8081: x509: certificate signed by unknown authority
    

    此时服务端也给出了错误日志提示:

    2015/04/30 16:03:31 http: TLS handshake error from 127.0.0.1:62004: remote error: bad certificate
    

    显然从客户端日志来看,go 实现的 Client 端默认也是要对服务端传过来的数字证书进行校验的,但客户端提示:这个证书是由不知名 CA 签发 的!

    我们可以修改一下 client1.go 的代码,让 client 端略过对证书的校验:

    //gohttps/4-https/client2.go
    package main
    
    import (
    "crypto/tls"
    "fmt"
    "io/ioutil"
    "net/http"
    )
    
    func main() {
    tr := &http.Transport{
    TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    }
    client := &http.Client{Transport: tr}
    resp, err := client.Get("https://localhost:8081")
    
        if err != nil {
            fmt.Println("error:", err)
            return
        }
        defer resp.Body.Close()
        body, err := ioutil.ReadAll(resp.Body)
        fmt.Println(string(body))
    
    }
    

    通过设置 tls.Config 的 InsecureSkipVerify 为 true,client 将不再对服务端的证书进行校验。执行后的结果 也证实了这一点:

    $go run client2.go
    Hi, This is an example of http service in golang!
    

    http 请求设置 header

    package main
    import (
        "fmt"
        "io/ioutil"
        "net/http"
        "os"
        "encoding/json"
    )
    
    func main() { //生成client 参数为默认
        client := &http.Client{}
        //生成要访问的url
        url := "http://somesite/somepath/"
        //提交请求
        reqest, err := http.NewRequest("GET", url, nil)
    
        //增加header选项
        reqest.Header.Add("Cookie", "xxxxxx")
        reqest.Header.Add("User-Agent", "xxx")
        reqest.Header.Add("X-Requested-With", "xxxx")
    
        if err != nil {
            panic(err)
        }
        //处理返回结果
        response, _ := client.Do(reqest)
        defer response.Body.Close()
    }
    

    http 代理请求

    package main
    
    import (
    	"crypto/tls"
    	"fmt"
    	"io"
    	"net/http"
    	"net/url"
    	"time"
    )
    
    // http proxy get方法
    func httpProxyGet(dataUrl, proxyIp string) (data []byte,  err error) {
    	transport := &http.Transport{
    		TLSClientConfig:   &tls.Config{InsecureSkipVerify: true}, //ssl证书报错问题
    		DisableKeepAlives: false,                                 //关闭连接复用,因为后台连接过多最后会造成端口耗尽
    		MaxIdleConns:      -1,                                    //最大空闲连接数量
    		IdleConnTimeout:   time.Duration(5 * time.Second),        //空闲连接超时时间
    		Proxy: http.ProxyURL(&url.URL{
    			Scheme: "http",
    			Host:   proxyIp,
    		}),
    	}
    	// 设置代理方式二
    	//proxyUrl, _ := url.Parse("http://" + proxyIp)
    	//transport.Proxy = http.ProxyURL(proxyUrl)
    
    	// 创建http客户端
    	client := &http.Client{
    		Timeout:   time.Duration(30 * time.Second),
    		Transport: transport,
    	}
    
    	request, err := http.NewRequest("GET", dataUrl, nil)
    	if err != nil {
    		return
    	}
    	// 请求数据
    	resp, err := client.Do(request)
    	if err != nil {
    		err = fmt.Errorf("request %s, proxyIp: (%s),err: %v", dataUrl, proxyIp, err)
    		return
    	}
    	defer resp.Body.Close()
    	// 读取数据
    	buf := make([]byte, 128)
    	data = make([]byte, 0, 2048)
    	for {
    		n, err := resp.Body.Read(buf)
    		data = append(data, buf[:n]...)
    
    		if err == io.EOF {
    			break
    		}
    		if err != nil {
    			continue
    		}
    	}
    	return
    }
    
    
    func main() {
    	data, err := httpProxyGet("http://www.baidu.com/", "89.22.11.55:9000")
    	if err != nil {
    		println(err)
    		return
    	}
    	fmt.Println(string(data))
    }
    

    websocket 编程

    webSocket 是什么

    • WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议
    • WebSocket 使得客户端和服务器之间的数据交换变得更加简单, 允许服务端主动向客户端推送数据
    • 在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输
    • 需要安装第三方包:
      • cmd 中:go get -u -v github.com/gorilla/websocket

    webSocket 示例

    服务端

    package main
    
    import (
    	"github.com/gorilla/websocket"
    	"log"
    	"net/http"
    )
    
    var upgrader = websocket.Upgrader{	// 配置websocker选项
    	// 允许websocket跨域
    	CheckOrigin: func(r *http.Request) bool {
    		return true
    	},
    }
    
    func echo(w http.ResponseWriter, r *http.Request) {
    	c, err := upgrader.Upgrade(w, r, nil)
    	if err != nil {
    		log.Print("upgrade:", err)
    		return
    	}
    	defer c.Close()
    	for {
    		mt, message, err := c.ReadMessage()
    		if err != nil {
    			log.Println("read:", err)
    			break
    		}
    		log.Printf("recv: %s", message)
    		err = c.WriteMessage(mt, message)
    		if err != nil {
    			log.Println("write:", err)
    			break
    		}
    	}
    }
    func main() {
    	log.SetFlags(0)
    	http.HandleFunc("/echo", echo)
    	log.Fatal(http.ListenAndServe("127.0.0.1:8081", nil))
    }
    

    html 客户端

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <title>websocket demo</title>
      </head>
      <body>
        <table>
          <tr>
            <td valign="top" width="50%">
              <p>
                Click "Open" to create a connection to the server, "Send" to send a
                message to the server and "Close" to close the connection. You can
                change the message and send multiple times.
              </p>
              <form>
                <button id="open">Open</button>
                <button id="close">Close</button>
                <input id="input" type="text" value="Hello world!" />
                <button id="send">Send</button>
              </form>
            </td>
            <td valign="top" width="50%">
              <div id="output"></div>
            </td>
          </tr>
    
          <script>
            window.addEventListener('load', function(evt) {
              var output = document.getElementById('output')
              var input = document.getElementById('input')
              var ws
              var print = function(message) {
                var d = document.createElement('div')
                d.innerHTML = message
                output.appendChild(d)
              }
              document.getElementById('open').onclick = function(evt) {
                if (ws) {
                  return false
                }
                ws = new WebSocket('ws://127.0.0.1:8081/echo')
                ws.onopen = function(evt) {
                  print('OPEN')
                }
                ws.onclose = function(evt) {
                  print('CLOSE')
                  ws = null
                }
                ws.onmessage = function(evt) {
                  print('RESPONSE: ' + evt.data)
                }
                ws.onerror = function(evt) {
                  print('ERROR: ' + evt.data)
                }
                return false
              }
              document.getElementById('send').onclick = function(evt) {
                if (!ws) {
                  return false
                }
                print('SEND: ' + input.value)
                ws.send(input.value)
                return false
              }
              document.getElementById('close').onclick = function(evt) {
                if (!ws) {
                  return false
                }
                ws.close()
                return false
              }
            })
          </script>
        </table>
      </body>
    </html>
    
  • 相关阅读:
    python下multiprocessing和gevent的组合使用
    TCP的三次握手与四次挥手理解及面试题(很全面)
    Python设计模式
    python定义接口继承类
    pycharm 中自动补全代码提示前符号 p,m ,c,v, f 是什么意思
    21天打造分布式爬虫-urllib库(一)
    redis的使用
    Memcached的使用
    12.Flask-Restful
    11.Flask钩子函数
  • 原文地址:https://www.cnblogs.com/zhichaoma/p/12509733.html
Copyright © 2011-2022 走看看