zoukankan      html  css  js  c++  java
  • 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/

  • 相关阅读:
    FZU 2098 刻苦的小芳(卡特兰数,动态规划)
    卡特兰数总结
    FZU 1064 教授的测试(卡特兰数,递归)
    HDU 4745 Two Rabbits(区间DP,最长非连续回文子串)
    Java 第十一届 蓝桥杯 省模拟赛 正整数的摆动序列
    Java 第十一届 蓝桥杯 省模拟赛 反倍数
    Java 第十一届 蓝桥杯 省模拟赛 反倍数
    Java 第十一届 蓝桥杯 省模拟赛 反倍数
    Java 第十一届 蓝桥杯 省模拟赛 凯撒密码加密
    Java 第十一届 蓝桥杯 省模拟赛 凯撒密码加密
  • 原文地址:https://www.cnblogs.com/lanyangsh/p/9822403.html
Copyright © 2011-2022 走看看