go 系列实现websocket

一、简介

websocket是个二进制协议,需要先通过Http协议进行握手,从而协商完成从Http协议向websocket协议的转换。一旦握手结束,当前的TCP连接后续将采用二进制websocket协议进行双向双工交互,自此与Http协议无关。

二、websocket demo

ws.go
Go 复制代码
package main

import (
	"fmt"
	"github.com/gorilla/websocket"
	"net/http"
	"time"
)

type wsMessage struct {
	mType int
	data  []byte
}

type wsConnection struct {
	wsSocket *websocket.Conn // 底层websocket
	inChan   chan *wsMessage // 读队列
	outChan  chan *wsMessage // 写队列
	state    int             // 连接状态
}

func (wsCon *wsConnection) wsReadLoop() {
	for {
		if wsCon.state == 1 {
			return
		}
		mType, data, err := wsCon.wsSocket.ReadMessage() // 阻塞
		if err != nil {
			fmt.Println("read err: %v", err)
			continue
		}
		msg := &wsMessage{
			mType,
			data,
		}
		wsCon.inChan <- msg
	}

}

func (wsCon *wsConnection) wsWriteLoop() {
	for {
		if wsCon.state == 1 {
			return
		}
		msg := <-wsCon.outChan // 阻塞
		err := wsCon.wsSocket.WriteMessage(msg.mType, msg.data)
		if err != nil {
			fmt.Printf("write err: %v", err)
			continue
		}
	}
}

func (wsCon *wsConnection) bizLoop() {
	for {
		if wsCon.state == 1 {
			return
		}
		// 处理消息
		msg := <-wsCon.inChan
		fmt.Println("process msg", string(msg.data))
		// 返回响应
		wsCon.outChan <- &wsMessage{
			mType: 2,
			data:  []byte(fmt.Sprintf("%s done", string(msg.data))),
		}
	}
}

func (wsCon *wsConnection) healthLoop() {
	defer wsCon.wsSocket.Close()
	for {
		time.Sleep(3 * time.Second)
		// 返回响应
		if err := wsCon.wsSocket.WriteMessage(2,
			[]byte("心跳检查")); err != nil {
			fmt.Println("write err: %v", err)
			wsCon.state = 1
			return
		}

	}
}

func wsHandler(resp http.ResponseWriter, req *http.Request) {
	// 升级websocket协议
	wsSocket, err := websocket.Upgrade(resp, req, nil, 1024, 1024)
	if err != nil {
		fmt.Println("upgrade err: %v", err)
	}
	// 一个请求,一个websocket 连接
	wsCon := &wsConnection{
		wsSocket: wsSocket,
		inChan:   make(chan *wsMessage, 1000),
		outChan:  make(chan *wsMessage, 1000),
	}

	go wsCon.wsReadLoop()
	go wsCon.wsWriteLoop()
	go wsCon.healthLoop()
	go wsCon.bizLoop()

}

func main() {
	http.HandleFunc("/ws", wsHandler)
	http.ListenAndServe(":8080", nil)
}
ws.html
html 复制代码
<!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://localhost:8080/ws");
                ws.onopen = function (evt) {
                    print("OPEN");
                }
                ws.onclose = function (evt) {
                    print("CLOSE");
                    ws = null;
                }
                ws.onmessage = function (evt) {
                    const reader = new FileReader()
                    reader.onload = function (event) {
                        print("RESPONSE: " + event.target.result)
                    }
                    reader.readAsText(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>
                <input id="input" type="text" value="Hello!">
                <button id="send">Send</button>
            </form>
        </td>
        <td valign="top" width="50%">
            <div id="output"></div>
        </td>
    </tr>
</table>
</body>
</html>
实现效果
相关推荐
爬山算法1 小时前
Redis(153)Redis的网络使用如何监控?
网络·redis·bootstrap
极地星光1 小时前
C++链式调用设计:打造优雅流式API
服务器·网络·c++
橘子真甜~2 小时前
C/C++ Linux网络编程8 - epoll + ET Reactor TCP服务器
linux·服务器·网络
贝塔实验室2 小时前
红外编解码彻底解析
网络·嵌入式硬件·信息与通信·信号处理·代码规范·基带工程·精益工程
就叫飞六吧3 小时前
“电子公章”:U盾(U-Key)实现身份认证、财务支付思路
网络·笔记
悠悠121383 小时前
NLB WebSocket 连接问题排查与解决方案
websocket·网络协议·php
wanderist.3 小时前
Linux使用经验——离线运行python脚本
linux·网络·python
4***17543 小时前
Spring Boot整合WebSocket
spring boot·后端·websocket
biter00884 小时前
Ubuntu 22.04 有线网络时好时坏?最终解决方案
linux·网络·ubuntu
Macbethad4 小时前
基于WPF的Ethernet/IP主站程序技术方案
网络协议·tcp/ip·wpf