• 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/

    Just try, don't shy.
  • 相关阅读:
    JavaFx在macOS下的文字渲染Bug
    Java多接口同名方法的冲突
    旧技术的惯性
    一点思考(1)
    slisp:编译到JVM平台上的lisp方言
    Arcee:又一个 Parser Generator 轮子
    使用Java实现一门简单的动态语言
    Hello World!
    [NOI2008]奥运物流
    [IOI2005]Riv河流
  • 原文地址:https://www.cnblogs.com/dfsxh/p/11995864.html
Copyright © 2020-2023  润新知