WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。

WebSocket通信协议于2011年被IETF定为标准RFC 6455,并被RFC7936所补充规范。

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

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

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

下载gorilla

  1. # go get github.com/gorilla/websocket

下面例子中主要包括两部分,server和client。

client部分又包括:web client(浏览器)和非web client。

server

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

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

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

server启动方式

  1. # go run server.go

server.go代码如下:

  1. // Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build ignore
  5. package main
  6. import (
  7. "flag"
  8. "html/template"
  9. "log"
  10. "net/http"
  11. "github.com/gorilla/websocket"
  12. )
  13. var addr = flag.String("addr", "localhost:8080", "http service address")
  14. var upgrader = websocket.Upgrader{} // use default options
  15. func echo(w http.ResponseWriter, r *http.Request) {
  16. c, err := upgrader.Upgrade(w, r, nil)
  17. if err != nil {
  18. log.Print("upgrade:", err)
  19. return
  20. }
  21. defer c.Close()
  22. for {
  23. mt, message, err := c.ReadMessage()
  24. if err != nil {
  25. log.Println("read:", err)
  26. break
  27. }
  28. log.Printf("recv: %s", message)
  29. err = c.WriteMessage(mt, message)
  30. if err != nil {
  31. log.Println("write:", err)
  32. break
  33. }
  34. }
  35. }
  36. func home(w http.ResponseWriter, r *http.Request) {
  37. homeTemplate.Execute(w, "ws://"+r.Host+"/echo")
  38. }
  39. func main() {
  40. flag.Parse()
  41. log.SetFlags(0)
  42. http.HandleFunc("/echo", echo)
  43. http.HandleFunc("/", home)
  44. log.Fatal(http.ListenAndServe(*addr, nil))
  45. }
  46. var homeTemplate = template.Must(template.New("").Parse(`
  47. <!DOCTYPE html>
  48. <html>
  49. <head>
  50. <meta charset="utf-8">
  51. <script>
  52. window.addEventListener("load", function(evt) {
  53. var output = document.getElementById("output");
  54. var input = document.getElementById("input");
  55. var ws;
  56. var print = function(message) {
  57. var d = document.createElement("div");
  58. d.innerHTML = message;
  59. output.appendChild(d);
  60. };
  61. document.getElementById("open").onclick = function(evt) {
  62. if (ws) {
  63. return false;
  64. }
  65. ws = new WebSocket("{{.}}");
  66. ws.onopen = function(evt) {
  67. print("OPEN");
  68. }
  69. ws.onclose = function(evt) {
  70. print("CLOSE");
  71. ws = null;
  72. }
  73. ws.onmessage = function(evt) {
  74. print("RESPONSE: " + evt.data);
  75. }
  76. ws.onerror = function(evt) {
  77. print("ERROR: " + evt.data);
  78. }
  79. return false;
  80. };
  81. document.getElementById("send").onclick = function(evt) {
  82. if (!ws) {
  83. return false;
  84. }
  85. print("SEND: " + input.value);
  86. ws.send(input.value);
  87. return false;
  88. };
  89. document.getElementById("close").onclick = function(evt) {
  90. if (!ws) {
  91. return false;
  92. }
  93. ws.close();
  94. return false;
  95. };
  96. });
  97. </script>
  98. </head>
  99. <body>
  100. <table>
  101. <tr><td valign="top" width="50%">
  102. <p>Click "Open" to create a connection to the server,
  103. "Send" to send a message to the server and "Close" to close the connection.
  104. You can change the message and send multiple times.
  105. <p>
  106. <form>
  107. <button id="open">Open</button>
  108. <button id="close">Close</button>
  109. <p><input id="input" type="text" value="Hello world!">
  110. <button id="send">Send</button>
  111. </form>
  112. </td><td valign="top" width="50%">
  113. <div id="output"></div>
  114. </td></tr></table>
  115. </body>
  116. </html>
  117. `))

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启动方式

  1. # go run client.go

client代码如下:

  1. // Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build ignore
  5. package main
  6. import (
  7. "flag"
  8. "log"
  9. "net/url"
  10. "os"
  11. "os/signal"
  12. "time"
  13. "github.com/gorilla/websocket"
  14. )
  15. var addr = flag.String("addr", "localhost:8080", "http service address")
  16. func main() {
  17. flag.Parse()
  18. log.SetFlags(0)
  19. interrupt := make(chan os.Signal, 1)
  20. signal.Notify(interrupt, os.Interrupt)
  21. u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"}
  22. log.Printf("connecting to %s", u.String())
  23. c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
  24. if err != nil {
  25. log.Fatal("dial:", err)
  26. }
  27. defer c.Close()
  28. done := make(chan struct{})
  29. go func() {
  30. defer close(done)
  31. for {
  32. _, message, err := c.ReadMessage()
  33. if err != nil {
  34. log.Println("read:", err)
  35. return
  36. }
  37. log.Printf("recv: %s", message)
  38. }
  39. }()
  40. ticker := time.NewTicker(time.Second)
  41. defer ticker.Stop()
  42. for {
  43. select {
  44. case <-done:
  45. return
  46. case t := <-ticker.C:
  47. err := c.WriteMessage(websocket.TextMessage, []byte(t.String()))
  48. if err != nil {
  49. log.Println("write:", err)
  50. return
  51. }
  52. case <-interrupt:
  53. log.Println("interrupt")
  54. // Cleanly close the connection by sending a close message and then
  55. // waiting (with timeout) for the server to close the connection.
  56. err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
  57. if err != nil {
  58. log.Println("write close:", err)
  59. return
  60. }
  61. select {
  62. case <-done:
  63. case <-time.After(time.Second):
  64. }
  65. return
  66. }
  67. }
  68. }

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/

golang gorilla websocket例子的更多相关文章

  1. golang实现WebSocket的商业化使用的开发逻辑(1)

    WebSocket是什么 WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议.其最大特点之一就是:服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对 ...

  2. golang之websocket 源码分析

    下载go的websocket包. 1. 通过google官方的方法, 需要hg来同步代码. 由于墙的原因, 还需要设置代理. 比较麻烦 2. http://gopm.io/ 通过该网站下载, 这是go ...

  3. 带ssl的websocket例子

    还是在那个websocket_demo的例子 rebar-creator create-app websocket_demo tree一下看看大概目录 ├── cert │   ├── cowboy- ...

  4. golang 移动应用例子 example/basic 源码框架分析

    条件编译 我们在源码中可以看到2个文件: main.go 和 main_x.go 这两个包名都是 package main , 都有 main 函数. 不会冲突么? 答案是不会的, main_x.go ...

  5. (网页)websocket例子

    转载自博客园张果package action; import javax.websocket.CloseReason; import javax.websocket.OnClose; import j ...

  6. 基于netty的websocket例子

    nettyServer package com.atguigu.netty.websocket; import javax.annotation.PostConstruct; import org.s ...

  7. erlang的websocket例子

    创建工程 rebar-creator create-app websocket_demo 文件列表 route_helper.erl -module(route_helper). -export([g ...

  8. Springboot WebSocket例子

    Springboot整合WebSocket 1.application.properties #设置服务端口号 server.port=8080 #thymeleaf配置 #是否启用模板缓存. spr ...

  9. golang LMDB入门例子——key range查询

    如下,使用gomb库 package main import ( "bytes" "fmt" "io/ioutil" "os&qu ...

随机推荐

  1. 替换Jar包内的文件

    要替换Jar包内的文件可以用以下命令实现: jar uvf myjar.jar com/test/myclass.class 这里值得注意的是  myclass.class 必须放在com/test ...

  2. react native 之 获取键盘高度

    多说不如多撸: /** * Created by shaotingzhou on 2017/2/23. *//** * Sample React Native App * https://github ...

  3. Netty核心概念

    一个Netty程序始于Bootstrap类,Bootstrap类是Netty提供的一个可以通过简单配置来设置或“引导”程序的一个重要的类.Netty中设计了Handlers来处理特定的"ev ...

  4. HDU2717-Catch That Cow (BFS入门)

    题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2717 Catch That Cow Time Limit: 5000/2000 MS (Java/O ...

  5. busybox 安装问题解决

    直接编译错误 1.loginutils/passwd.c:93:16: error: storage size of ‘rlimit_fsize’ isn’t known 解决方法:在busybox根 ...

  6. Oracle账户管理

    登录:conn 用户名/口令创建用户create user 用户名 identified by 口令修改密码password 用户名删除用户drop user 用户名如果要删除的用户已经创建表,那么再 ...

  7. linux 如何使用密钥登录 (CentOs)

    说明:一般的密码方式登录容易被密码暴力破解.所以一般我们会将 SSH 的端口设置为默认22以外的端口,或者禁用root账户登录.其实可以通过密钥登录这种方式来更好地保证安全. 密钥形式登录的原理是:利 ...

  8. Angular 插值字符串

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  9. [LeetCode&Python] Problem 383. Ransom Note

    Given an arbitrary ransom note string and another string containing letters from all the magazines, ...

  10. textarea去掉右下三角号

    /*去掉textarea右下角三角符号*/ resize : none; 修改样式直接覆盖就行,会把默认样式覆盖掉.如border,width,height,border-radius