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 小时前
firewalld封禁IP或IP段
linux·网络
sec0nd_1 小时前
1网络安全的基本概念
网络·安全·web安全
青柠视频云2 小时前
青柠视频云——视频丢包(卡顿、花屏、绿屏)排查
服务器·网络·音视频
网安CILLE2 小时前
2024年某大厂HW蓝队面试题分享
网络·安全·web安全
沐风ya2 小时前
Reactor介绍,如何从简易版本的epoll修改成Reactor模型(demo版本代码+详细介绍)
网络
SUGERBOOM2 小时前
【网络安全】网络基础第一阶段——第一节:网络协议基础---- OSI与TCP/IP协议
网络·网络协议·web安全
petaexpress3 小时前
常用的k8s容器网络模式有哪些?
网络·容器·kubernetes
m0_609000425 小时前
向日葵好用吗?4款稳定的远程控制软件推荐。
运维·服务器·网络·人工智能·远程工作
suifen_8 小时前
RK3229_Android9.0_Box 4G模块EC200A调试
网络
铁松溜达py8 小时前
编译器/工具链环境:GCC vs LLVM/Clang,MSVCRT vs UCRT
开发语言·网络