go 系列实现websocket

news2024/11/18 3:22:12

一、简介

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

二、websocket demo

ws.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
<!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>
实现效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2075804.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows10企业版找不到微软商店以及微软商店打不开问题解决

目录 找不到微软商店解决方案重置缓存安装微软商店 Microsoft Store 无法打开问题 找不到微软商店解决方案 重置缓存 打开设置->应用和功能 找到Microsoft Store(如果没有则需要手动安装),点击高级选项,选择重置,或者管理员下命令行执行 wsreset 安装微软商店 打开 http…

大语言模型-PDF文档解析

PDF解析能够提升大语言模型系统的信息处理能力和应用范围&#xff0c;为用户提供更加便捷、高效、个性化的服务体验。本文介绍三种常用的pdf解析方式&#xff1a;Open Parse、pdfplumber、PyMuPD。 一、Open Parse Open Parse是一个能够直观地识别文档布局并有效地对其进行分…

ruoyi-app前端在缓存中添加nick_name和user_id属性值

需求 ruoyi-app原生自带只有avatar、name、roles、permissions&#xff1b;在显示中&#xff0c;我们大多数需要nick_name、user_id&#xff1b;当然获取方式也可以通过name去调用接口查询&#xff0c;但我想偷个懒。 代码 代码已经调好的&#xff0c;复制即用&#xff1b;至…

中国工商银行笔试2025届考什么?工行笔试备考|附真题库面试攻略

嘿&#xff0c;各位小伙伴们&#xff01;我是职小豚&#xff0c;今天咱们就来好好聊聊中国工商银行 2025 届秋招那些事儿。 一、中国工商银行公司介绍 中国工商银行&#xff0c;那可是金融界的巨擘&#xff01;1984 年 1 月 1 日&#xff0c;它横空出世&#xff0c;开启了辉煌…

浅谈【数据结构】树与二叉树之平衡二叉树

目录 1、平衡二叉树 2、平衡操作 谢谢帅气美丽且优秀的你看完我的文章还要点赞、收藏加关注 没错&#xff0c;说的就是你&#xff0c;不用再怀疑&#xff01;&#xff01;&#xff01; 希望我的文章内容能对你有帮助&#xff0c;一起努力吧&#xff01;&#xff01;&#xff…

【与C++的邂逅】--- C/C++内存管理

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 与C的邂逅 C中我们总是提到管理资源&#xff0c;资源可以从内存中申请&#xff0c;前提是我们得知道C对内存管理的布局&#xff0c;本节我们就来学习这块…

民宿管理平台系统

你好&#xff0c;我是计算机专业的毕业生&#xff0c;专注于民宿管理平台系统的研究与开发。如果你对本系统感兴趣或有任何疑问&#xff0c;欢迎随时联系我。 开发语言 Java 数据库 MySQL 技术 SpringBoot框架 工具 ECLIPSE开发环境、Tomcat服务器 系统展示 首页 用户…

联发科双频Wi-Fi 6芯片MT7976CN全景图

这周末,除非外面下钞票,否则谁也拦不住我玩《黑神话悟空》(附:两款可以玩转悟空的显卡推荐) 天玑助力联发科力压高通~探秘MTK 5G旗舰智能手机SoC芯片——MT6989(天玑9300) 联发科双频Wi-Fi 6芯片MT7976CN全景图 TPLink AX3000路由器 TPLin

论文解读Multi-Prompt Alignment for Multi-Source Unsupervised Domain Adaptation

Multi-Prompt Alignment for Multi-Source Unsupervised Domain Adaptation NeurlIPS 2023 摘要 大多数现有的无监督域适应( UDA )方法依赖于共享网络来提取领域不变特征。无论如何&#xff0c;当面对多个源域时&#xff0c;优化这样的网络涉及更新整个网络的参数&#xff0…

推动RISC-V CPU性能快速提升并向上打开更多的高价值市场

作者&#xff1a;Imagination Technologies 8月21-23日&#xff0c;2024年RISC-V中国峰会在杭州黄龙饭店举行。作为已推出多款Imagination Catapult系列RISC-V CPU半导体知识产权&#xff08;IP&#xff09;的提供商&#xff0c;以及全球领先的GPU和AI加速器IP厂商&#xff0c…

【经验分享】CANOPEN协议驱动移植(基于CANfestival源码架构)

【经验分享】CANOPEN协议驱动移植(基于CANfestival源码架构&#xff09; 前言一、CANOPEN整体实现原理二、CANOPEN驱动收发三、Timer定时器四、Object Dictionary对象字典五、CANOPEN应用层接口六、CANOPEN 驱动移植经验总结 前言 本次CANOPEN移植基于CANfestival开源代码&…

开关电源中“黑箱”的考虑

在初设计阶段&#xff0c;首先要考虑开关电源的一些主要参数&#xff0c;这有助于设计者确 定自己所选的拓扑是否正确&#xff0c;也便于提前预定实验板所需的元器件。同时可以知 道接下来的设计所需的一些非常重 要的参数。关于如何对“黑箱”进 行估计&#xff0c;设计者只要…

MIPI联盟D-PHYv1.2规范阅读笔记

本文阅读自eetop.cn_mipi_D-PHY_specification_v1-2.pdf MIPI简介 MIPI 联盟成立至今制定了多种用于不同终端设备的接口标准&#xff0c;其中包括 用于摄像头的 CSI-2。 用于显示器的 DSI。 用于射频的 DigRF 。 用于麦克风的SLIMbus等接口协议。 MIPI CSI-2 协议简介 两…

书生大模型实战营(第三期闯关大挑战)- 进阶岛 第五关 茴香豆:企业级知识库问答工具

茴香豆本地标准版搭建 茴香豆介绍 茴香豆 是由书生浦语团队开发的一款开源、专门针对国内企业级使用场景设计并优化的知识问答工具。在基础 RAG 课程中我们了解到&#xff0c;RAG 可以有效的帮助提高 LLM 知识检索的相关性、实时性&#xff0c;同时避免 LLM 训练带来的巨大成…

8周流水6700美元Dropshipping运营全流程曝光丨出海笔记

&#xff08;之前删掉补发系列&#xff09; 之前分享了一个案例《净赚4000多美元&#xff01;个人卖家Dropshipping卖30天太阳镜&#xff0c;究竟如何做到的》&#xff0c;不少小伙伴觉得实操性很强&#xff0c;纷纷反馈意犹未尽&#xff0c;所以船长继续去找之前的Dropshippin…

房产系统技术功能解析

房产系统的功能设计旨在提高房地产行业的运作效率、优化资源分配&#xff0c;并为用户提供更便捷高效的服务体验。以下是房产系统关键技术功能的详细解析&#xff1a; 一、房源管理 房源信息录入与编辑&#xff1a;支持全面的房源信息录入&#xff0c;包括房屋位置、面积、户型…

C++第四十一弹---C++11新特性深度解析:让你的代码更现代、更高效(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1. C11简介 2. 统一的列表初始化 2.1 &#xff5b;&#xff5d;初始化 2.2 std::initializer_list 3. 声明 3.1 auto 3.2 decltype 3.3 nu…

arcgis依据字段分组

脚本代码&#xff1a; UniqueDict {} def isDuplicateIndex(inValue): UniqueDict.setdefault(inValue,0) UniqueDict[inValue] 1 return UniqueDict[inValue] 输出值 isDuplicateIndex( !地块编号! )

临床试验中缺失数据的问题讨论

一、数据缺失的原因&#xff1a; &#xff08;1&#xff09;AE或疗效退出&#xff1b; &#xff08;2&#xff09;结局变量不适用&#xff08;无法获得结局变量&#xff09;&#xff1b; &#xff08;3&#xff09;失访&#xff1b; &#xff08;4&#xff09;数据采集失误&am…

云安全已经很好,但如何让它更好呢

尽管云计算很安全&#xff0c;但并不能完全避免数据泄露。随着云计算逐渐成为IT的重要部分&#xff0c;现在企业必须更认真地考虑如何加强云服务提供商默认安全基础设施的安全性。 传统云服务提供商都在努力为其客户提供强大的安全措施&#xff0c;他们通常会提供服务器端加密…