WebSocket介绍

WebSocket通信协议通过单个TCP连接提供全双工通信通道。与HTTP相比,WebSocket不需要你为了获得响应而发送请求。它允许双向数据流,因此您只需等待服务器发送的消息即可。当Websocket可用时,它将向您发送一条消息。对于需要连续数据交换的服务(例如即时通讯程序,在线游戏和实时交易系统),WebSocket是一个很好的解决方案。 WebSocket连接由浏览器请求,并由服务器响应,然后建立连接,此过程通常称为握手。 WebSocket中的特殊标头仅需要浏览器与服务器之间的一次握手即可建立连接,该连接将在其整个生命周期内保持活动状态。 WebSocket解决了许多实时Web开发的难题,并且与传统的HTTP相比,具有许多优点:

轻量级报头减少了数据传输开销。

单个Web客户端仅需要一个TCP连接。

WebSocket服务器可以将数据推送到Web客户端。

WebSocket协议实现起来相对简单。它使用HTTP协议进行初始握手。握手成功后即建立连接,WebSocket实质上使用原始TCP读取/写入数据。

客户端请求如下所示:

GET /chat HTTP/1.1

Host: server.example.com

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==

Sec-WebSocket-Protocol: chat, superchat

Sec-WebSocket-Version: 13

Origin: http://example.com

这是服务器响应:

HTTP/1.1 101 Switching Protocols

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=

Sec-WebSocket-Protocol: chat

如何在Go中创建WebSocket应用

要基于Go 语言内置的net/http 库编写WebSocket服务器,你需要:

发起握手

从客户端接收数据帧

发送数据帧给客户端

关闭握手

发起握手

首先,让我们创建一个带有WebSocket端点的HTTP处理程序:

// HTTP server with WebSocket endpoint

func Server() {

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

ws, err := NewHandler(w, r)

if err != nil {

// handle error

}

if err = ws.Handshake(); err != nil {

// handle error

}

然后初始化WebSocket结构。

初始握手请求始终来自客户端。服务器确定了WebSocket请求后,需要使用握手响应进行回复。

请记住,你无法使用http.ResponseWriter编写响应,因为一旦开始发送响应,它将关闭其基础的TCP连接(这是HTTP 协议的运行机制决定的,发送响应后即关闭连接)。

因此,您需要使用HTTP劫持(hijack)。通过劫持,可以接管基础的TCP连接处理程序和bufio.Writer。这使可以在不关闭TCP连接的情况下读取和写入数据。

// NewHandler initializes a new handler

func NewHandler(w http.ResponseWriter, req *http.Request) (*WS, error) {

hj, ok := w.(http.Hijacker)

if !ok {

// handle error

}                  .....

}

要完成握手,服务器必须使用适当的头进行响应。

// Handshake creates a handshake header

func (ws *WS) Handshake() error {

hash := func(key string) string {

h := sha1.New()

h.Write([]byte(key))

h.Write([]byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))

return base64.StdEncoding.EncodeToString(h.Sum(nil))

}(ws.header.Get("Sec-WebSocket-Key"))

.....

}

客户端发起WebSocket连接请求时用的Sec-WebSocket-key是随机生成的,并且是Base64编码的。接受请求后,服务器需要将此密钥附加到固定字符串。假设秘钥是x3JJHMbDL1EzLkh9GBhXDw==。在这个例子中,可以使用SHA-1计算二进制值,并使用Base64对其进行编码。得到HSmrc0sMlYUkAGmm5OPpG2HaGWk=。然后使用它作为Sec-WebSocket-Accept 响应头的值。

传输数据帧

握手成功完成后,您的应用程序可以从客户端读取数据或向客户端写入数据。WebSocket规范定义了一个客户机和服务器之间使用的特定帧格式。这是框架的位模式:

图:传输数据帧的位模式

使用以下代码对客户端有效负载进行解码:

// Recv receives data and returns a Frame

func (ws *WS) Recv() (frame Frame, _ error) {

frame = Frame{}

head, err := ws.read(2)

if err != nil {

// handle error

}

反过来,这些代码行允许对数据进行编码:

// Send sends a Frame

func (ws *WS) Send(fr Frame) error {

// make a slice of bytes of length 2

data := make([]byte, 2)

// Save fragmentation & opcode information in the first byte

data[0] = 0x80 | fr.Opcode

if fr.IsFragment {

data[0] &= 0x7F

}

.....

关闭握手

当各方之一发送状态为关闭的关闭帧作为有效负载时,握手将关闭。可选的,发送关闭帧的一方可以在有效载荷中发送关闭原因。如果关闭是由客户端发起的,则服务器应发送相应的关闭帧作为响应。

// Close sends a close frame and closes the TCP connection

func (ws *Ws) Close() error {

f := Frame{}

f.Opcode = 8

f.Length = 2

f.Payload = make([]byte, 2)

binary.BigEndian.PutUint16(f.Payload, ws.status)

if err := ws.Send(f); err != nil {

return err

}

return ws.conn.Close()

}

使用第三方库快速构建WebSocket服务

通过上面的章节可以看到用Go自带的net/http库实现WebSocket服务还是太复杂了。好在有很多对WebSocket支持良好的第三方库,能减少我们很多底层的编码工作。这里我们使用gorilla web toolkit家族的另外一个库gorilla/websocket来实现我们的WebSocket服务,构建一个简单的Echo服务(echo意思是回音,就是客户端发什么,服务端再把消息发回给客户端)。

我们在http_demo项目的handler目录下新建一个ws子目录用来存放WebSocket服务相关的路由对应的请求处理程序。

增加两个路由:

/ws/echo echo应用的WebSocket服务的路由。

/ws/echo_display echo应用的客户端页面的路由。

创建WebSocket服务端

// handler/ws/echo.go

package ws

import (

"fmt"

"github.com/gorilla/websocket"

"net/http"

)

var upgrader = websocket.Upgrader{

ReadBufferSize:  1024,

WriteBufferSize: 1024,

}

func EchoMessage(w http.ResponseWriter, r *http.Request) {

conn, _ := upgrader.Upgrade(w, r, nil) // 实际应用时记得做错误处理

for {

// 读取客户端的消息

msgType, msg, err := conn.ReadMessage()

if err != nil {

return

}

// 把消息打印到标准输出

fmt.Printf("%s sent: %s\n", conn.RemoteAddr(), string(msg))

// 把消息写回客户端,完成回音

if err = conn.WriteMessage(msgType, msg); err != nil {

return

}

}

}

conn变量的类型是*websocket.Conn, websocket.Conn类型用来表示WebSocket连接。服务器应用程序从HTTP请求处理程序调用Upgrader.Upgrade方法以获取*websocket.Conn

调用连接的WriteMessage和ReadMessage方法发送和接收消息。上面的msg接收到后在下面又回传给了客户端。msg的类型是[]byte。

创建WebSocket客户端

前端页面路由对应的请求处理程序如下,直接返回views/websockets.html给到浏览器渲染页面即可。

// handler/ws/echo_display.go

package ws

import "net/http"

func DisplayEcho(w http.ResponseWriter, r *http.Request) {

http.ServeFile(w, r, "views/websockets.html")

}

websocket.html里我们需要用JavaScript连接WebScoket服务进行收发消息,篇幅原因我就只贴JS代码了,完整的代码通过本节的口令去公众号就能获取到下载链接。

<form>

<input id="input" type="text" />

<button οnclick="send()">Send</button>

<pre id="output"></pre>

</form>

...

<script>

var input = document.getElementById("input");

var output = document.getElementById("output");

var socket = new WebSocket("ws://localhost:8000/ws/echo");

socket.onopen = function () {

output.innerHTML += "Status: Connected\n";

};

socket.onmessage = function (e) {

output.innerHTML += "Server: " + e.data + "\n";

};

function send() {

socket.send(input.value);

input.value = "";

}

</script>

...

注册路由

服务端和客户端的程序都准备好后,我们按照之前约定好的路径为他们注册路由和对应的请求处理程序:

// router/router.go

func RegisterRoutes(r *mux.Router) {

...

wsRouter := r.PathPrefix("/ws").Subrouter()

wsRouter.HandleFunc("/echo", ws.EchoMessage)

wsRouter.HandleFunc("/echo_display", ws.DisplayEcho)

}

测试验证

重启服务后访问http://localhost:8000/ws/echo_display,在输入框中输入任何消息都能再次回显到浏览器中。

图片

服务端则是把收到的消息打印到终端中然后把调用writeMessage把消息再回传给客户端,可以在终端中查看到记录。

总结

WebSocket在现在更新频繁的应用中使用非常广泛,进行WebSocket编程也是我们需要掌握的一项必备技能。文章的实践练习稍微简单了一些,也没有做错误和安全性检查。主要是为了讲清楚大概的流程。关于gorilla/websocket更多的细节在使用时还需要查看官方文档才行。

来源:微点阅读  https://www.weidianyuedu.com

如何用Go语言创建WebSocket服务相关推荐

  1. go tcp客户端自动重连_使用 Go 语言创建 WebSocket 服务

    今天介绍如何用 Go 语言创建 WebSocket 服务,文章的前两部分简要介绍了 WebSocket 协议以及用 Go 标准库如何创建 WebSocket 服务.第三部分实践环节我们使用了 gori ...

  2. 使用Go语言创建WebSocket服务

    今天介绍如何用Go语言创建WebSocket服务,文章的前两部分简要介绍了WebSocket协议以及用Go标准库如何创建WebSocket服务.第三部分实践环节我们使用了gorilla/websock ...

  3. node.js创建WebSocket服务,并使用原生js ES6完成对WebSocket数据交互

    注意,前情提示: 本代码基于<Node.js(nodejs)对本地JSON文件进行增.删.改.查操作(轻车熟路)> 传送门Node.js(nodejs)对本地JSON文件进行增.删.改.查 ...

  4. 单片机开发板sv32wb0x,C语言,创建websocket客户端

    本人参考大佬的代码移植进单片机,调试BUG后并且成功跑通,如果你不了解websocket协议建议参考Linux下c语言实验Websocket通讯 含客户端和服务器测试代码 我只是把这位大佬写的提取出我 ...

  5. php websocket udp,swoole创建websocket服务并且支持https服务,同时监听tcp,udp端口

    namespace serve\websocket; // use serve\websocket\task\Message as taskMessage; use serve\websocket\c ...

  6. 使用php创建WebSocket服务

    执行方法: 首先先修改server.php与index.html的ip 通过命令行执行 [php路径]\php.exe "[文件路径]\server.php" 然后通过浏览器打开i ...

  7. C语言创建指针需要给大小吗,如何用c语言创建一个指针(示例代码)

    正如我们所知,在c语言中,char指针逐字节遍历内存,即每次1字节,每次整数指针4字节(在gcc编译器中),每次2字节(在TC编译器中). 例如: char *cptr; // if this poi ...

  8. linux怎么创建新的c语言文件夹,如何用c语言创建文件夹

    参考: ########################################################## 判断文件夹是否存在: 在windows环境下头文件为: #include ...

  9. c语言创建新指针,如何用c语言创建一个指针

    您总是可以将指针强制转换为整数,即整数大小比系统中使用的字节指针大3位.然后在向左移动3位后移动指针.然后将位信息存储在最低有效3位上. 然后可以用正常算术递增该整数"位指针". ...

最新文章

  1. macOs下全局安装npm包的设置问题
  2. c语言编程怎么自学网,c语言函数
  3. java之 Timer 类的使用以及深入理解
  4. 软件无法连接oracle数据库,全面解析Oracle无法连接本地数据库问题
  5. 亚信安全携五大创新安全方案,发布AI²亚信安全智能框架
  6. CentOS 6.5上安装Confluence 5.4.4
  7. android开发(37) android使用android_serialport_api 操作串口,解决权限问题
  8. opencv数字图像处理(1) - 灰度变换函数
  9. 在SOLIDWORKS Electrical中,如何创建电气原理图符号库?
  10. 宁夏诗词学会红寺堡采风专辑
  11. 使用Simscape搭建车辆仿真模块
  12. 电容式门把手工作原理
  13. ElasticSearch设置字段的keyword属性
  14. 浙江工大学计算机学院保研,浙江工业大学计算机学院保研初试名单
  15. 获取百度首页的源代码
  16. 最近网上比较火的虎年西游记金钱豹头像制作小程序源码
  17. 服务器gpt分区不能安装系统,安装Win10原版系统提示“Windows无法安装到GPT分区形式磁盘”怎么办...
  18. 【经验篇】聊聊双非计算机硕士如何进大厂搞算法
  19. 关于 promise链式调用与中止
  20. [数据库] 一文读懂Mysql数据库索引实现原理

热门文章

  1. 安装ENVI5.3:the installation of MSVC_2010_SP1_x64_32bit has failed
  2. 新员工转正述职报告PPT模板
  3. mysql 创建存储过程语法_mysql存储过程语法及实例
  4. Cesium之粒子---简单粒子特效
  5. 什么东西可以帮助睡眠,对睡眠好的东西分享
  6. 开关电源-反激+单级PFC超低纹波超低THD
  7. 计算机在手机找不到了怎么办,手机找不到了怎么办 手机找不到了找回方法
  8. Visual Studio Community2019 30天试用期过期,无法登录微软账户
  9. chrome设置默认首页无效
  10. ES 7.0.1安装head和sql插件报错处理