• golang gorilla websocket例子


    WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。
    WebSocket通信协议于2011年被IETF定为标准RFC 6455,并被RFC7936所补充规范。

    在golang语言中,目前有两种比较常用的实现方式:一个是golang自带的库,另一个是gorilla,功能强大。

    golang自带库的使用例子可参考以前的博文:Golang如何使用websocket

    本文以gorilla为例,介绍websocket的使用。

    下载gorilla

    # go get github.com/gorilla/websocket
    

    下面例子中主要包括两部分,server和client。
    client部分又包括:web client(浏览器)和非web client。

    server

    server端是一个HTTP 服务器,监听8080端口。

    当接收到连接请求后,将连接使用的http协议升级为websocket协议。后续通信过程中,使用websocket进行通信。

    对每个连接,server端等待读取数据,读到数据后,打印数据,然后,将数据又发送给client.

    server启动方式

    # go run server.go
    

    server.go代码如下:

    // Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style
    // license that can be found in the LICENSE file.
    
    // +build ignore
    
    package main
    
    import (
    	"flag"
    	"html/template"
    	"log"
    	"net/http"
    
    	"github.com/gorilla/websocket"
    )
    
    var addr = flag.String("addr", "localhost:8080", "http service address")
    
    var upgrader = websocket.Upgrader{} // use default options
    
    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 home(w http.ResponseWriter, r *http.Request) {
    	homeTemplate.Execute(w, "ws://"+r.Host+"/echo")
    }
    
    func main() {
    	flag.Parse()
    	log.SetFlags(0)
    	http.HandleFunc("/echo", echo)
    	http.HandleFunc("/", home)
    	log.Fatal(http.ListenAndServe(*addr, nil))
    }
    
    var homeTemplate = template.Must(template.New("").Parse(`
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <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.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>
    </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>
    <p><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></table>
    </body>
    </html>
    `))
    
    

    server output:

    recv: 2018-10-20 17:09:12.497129965 +0800 CST m=+1.010137585
    recv: 2018-10-20 17:09:13.495602484 +0800 CST m=+2.008641088
    recv: 2018-10-20 17:09:14.49401062 +0800 CST m=+3.007080206
    recv: 2018-10-20 17:09:15.497114615 +0800 CST m=+4.010215329
    recv: 2018-10-20 17:09:16.494394706 +0800 CST m=+5.007526368
    read: websocket: close 1000 (normal)

    非web client

    client启动后,首先连接server。

    连接建立后,主routine每一秒钟向server发送消息(当前时间)。

    另一个routine从server接收数据,并打印。

    当client退出时,会向server发送关闭消息。接着,等待退出。

    client启动方式

    # go run client.go
    

    client代码如下:

    // Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style
    // license that can be found in the LICENSE file.
    
    // +build ignore
    
    package main
    
    import (
    	"flag"
    	"log"
    	"net/url"
    	"os"
    	"os/signal"
    	"time"
    
    	"github.com/gorilla/websocket"
    )
    
    var addr = flag.String("addr", "localhost:8080", "http service address")
    
    func main() {
    	flag.Parse()
    	log.SetFlags(0)
    
    	interrupt := make(chan os.Signal, 1)
    	signal.Notify(interrupt, os.Interrupt)
    
    	u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"}
    	log.Printf("connecting to %s", u.String())
    
    	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
    	if err != nil {
    		log.Fatal("dial:", err)
    	}
    	defer c.Close()
    
    	done := make(chan struct{})
    
    	go func() {
    		defer close(done)
    		for {
    			_, message, err := c.ReadMessage()
    			if err != nil {
    				log.Println("read:", err)
    				return
    			}
    			log.Printf("recv: %s", message)
    		}
    	}()
    
    	ticker := time.NewTicker(time.Second)
    	defer ticker.Stop()
    
    	for {
    		select {
    		case <-done:
    			return
    		case t := <-ticker.C:
    			err := c.WriteMessage(websocket.TextMessage, []byte(t.String()))
    			if err != nil {
    				log.Println("write:", err)
    				return
    			}
    		case <-interrupt:
    			log.Println("interrupt")
    
    			// Cleanly close the connection by sending a close message and then
    			// waiting (with timeout) for the server to close the connection.
    			err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
    			if err != nil {
    				log.Println("write close:", err)
    				return
    			}
    			select {
    			case <-done:
    			case <-time.After(time.Second):
    			}
    			return
    		}
    	}
    }
    
    

    client output:

    connecting to ws://localhost:8080/echo
    recv: 2018-10-20 17:09:12.497129965 +0800 CST m=+1.010137585
    recv: 2018-10-20 17:09:13.495602484 +0800 CST m=+2.008641088
    recv: 2018-10-20 17:09:14.49401062 +0800 CST m=+3.007080206
    recv: 2018-10-20 17:09:15.497114615 +0800 CST m=+4.010215329
    recv: 2018-10-20 17:09:16.494394706 +0800 CST m=+5.007526368
    ^Cinterrupt
    read: websocket: close 1000 (normal)

    web client

    web client,也就是使用浏览器。
    在浏览器中输入http://127.0.0.1:8080

    浏览器

    "Open",然后"send"

    server output:

    recv: Hello world!!

    参考

    百度百科

    https://baike.baidu.com/item/WebSocket

    github

    https://github.com/gorilla/websocket

    doc

    https://godoc.org/github.com/gorilla/websocket

    example

    https://github.com/gorilla/websocket/blob/master/examples/

  • 相关阅读:
    Java 语义网编程系列二: 本体
    Java 语义网编程系列三: 现实世界中的知识建模
    Windows编程--线程和内核对象的同步-等待定时器内核对象
    Windows编程--虚拟内存的使用
    Windows编程--线程和内核对象的同步-事件内核对象
    Python 中文问题
    Windows编程--线程和内核对象的同步-信标(信号量)内核对象
    Windows编程--伪句柄
    Windows编程-- 线程和内核对象的同步 - 互斥对象内核对象
    Windows编程-- Windows的内存结构
  • 原文地址:https://www.cnblogs.com/lanyangsh/p/9822403.html
Copyright © 2020-2023  润新知