当前位置: 首页 > news >正文

基于事件驱动的websocket简单实现

websocket的实现

什么是websocket?

WebSocket 是一种网络通信协议,旨在为客户端和服务器之间提供全双工、实时的通信通道。它是在 HTML5 规范中引入的,可以让浏览器与服务器进行持久化连接,以便实现低延迟的数据交换。

WebSocket 的特点:

  1. 全双工通信:客户端和服务器可以同时发送和接收消息,而不必等待对方完成操作。
  2. 轻量级:相较于传统的 HTTP 协议,WebSocket 头部信息更小,这减少了网络开销。
  3. 持久连接:一旦建立连接,双方可以一直保持这个连接,直到主动关闭。这样避免了频繁建立和关闭连接带来的性能损耗。
  4. 实时性:适合需要即时数据更新的应用,如在线聊天、游戏、股票行情等

通信过程

websocket通信协议是基于http的,客户端首先发送连接请求request,在该request中包含了基本的HTTP头信息:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Version: 13

以上这些信息以字符串的形式发送至服务端的rbuffer里,当服务端识别到这些字符串信息后,需要发送相应response进行确认后才能建立websocket连接。确认信息的response应该如下:

接收到客户端的key->

key与“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”进行拼接,得到新的key-->

使用SHA1算法加密-->

再使用base64加密-->

加上http头信息,以字符串形式的发送至客户端。当客户端收到后,websocket建立。

int response_websock(struct conn *c){char* key_head = "Sec-WebSocket-Key";char* start = strstr(c->rbuffer, key_head);start += 19;char key[1024] = {0};int set = 0;while (*start != '='){key[set] = *start;start++;set++;}key[set] = '\0';char* result = strcat(key, GUID);unsigned char hash[SHA_DIGEST_LENGTH] = {0};SHA1((unsigned char*)result, strlen(result), hash);char* base = base64_encode(hash, SHA_DIGEST_LENGTH);//strcpy(c->wbuffer, base) ;snprintf(c->wbuffer, sizeof(c->wbuffer), "HTTP/1.1 101 Switching Protocols\r\n""Upgrade: websocket\r\n""Connection: Upgrade\r\n""Sec-WebSocket-Accept: %s\r\n\r\n", base);c->wlength = strlen(c->wbuffer);free(base);
}

建立websocket连接后,可以互发信息,但是信息是以websocket帧,字节流的形式传送的,所以需要进行编码和解码。

websocket帧结构如下:

发送信息(编码):

int encoding(struct conn *c){//写入rbufferint message_len = strlen(payload);int frame_len = 0;// 设置 FIN 位和 Opcode(文本帧)c->wbuffer[0] = 0x81; // FIN=1, Opcode=0x1(文本帧)// 设置 Payload Lengthif (message_len <= 125) {c->wbuffer[1] = message_len; // 不需要额外长度字段memcpy(&c->wbuffer[2], payload, message_len);frame_len = 2 + message_len;} else if(message_len <= 65535){c->wbuffer[1] = 126; // 16 位扩展长度c->wbuffer[2] = (message_len >> 8) & 0xFF; // 高字节c->wbuffer[3] = message_len & 0xFF;        // 低字节memcpy(&c->wbuffer[4], payload, message_len);frame_len = 4 + message_len;  }else{c->wbuffer[1] = 127; // 64 位扩展长度// 这里假设消息长度小于 2^32,因此高 4 字节为 0memset(&c->wbuffer[2], 0, 4);c->wbuffer[6] = (message_len >> 24) & 0xFF;c->wbuffer[7] = (message_len >> 16) & 0xFF;c->wbuffer[8] = (message_len >> 8) & 0xFF;c->wbuffer[9] = message_len & 0xFF;memcpy(&c->wbuffer[10], payload, message_len);frame_len = 10 + message_len;}
}

接收信息(解码):

int encoding(struct conn *c){int fin = (c->rbuffer[0] & 0X80) >> 7;int opcode = c->rbuffer[0] & 0x0F;              // 操作码int masked = (c->rbuffer[1] & 0x80) >> 7;       // 是否有掩码int payload_len = c->rbuffer[1] & 0x7F;unsigned char *mask = NULL;                 // 掩码键unsigned char *payload = NULL;              // 数据指针if (payload_len <= 125) {mask = &c->rbuffer[2];payload = &c->rbuffer[6];} else if (payload_len == 126) {payload_len = ntohs(*(uint16_t *)&c->rbuffer[2]);mask = &c->rbuffer[4];payload = &c->rbuffer[8];} else if (payload_len == 127) {payload_len = ntohl(*(uint64_t *)&c->rbuffer[2]);mask = &c->rbuffer[10];payload = &c->rbuffer[14];}for (int i = 0; i < payload_len; i++) {  //解析数据(去除掩码)payload[i] ^= mask[i % 4];}// 输出解码后的消息payload[payload_len] = '\0';printf("Message from client: %s\n", payload);
}

流程总结

由于在建立连接阶段和通信阶段发送的数据形式不同,所以需要在结构体中引入状态机,用于记录是哪种请求,根据不同的状态机,做出不同的response。

int ws_request(struct conn *c){//判断建立请求连接还是数据帧if (strstr(c->rbuffer, "Sec-WebSocket-Key") != NULL) {printf("HTTP handshake request detected.\n");printf("request: %s", c->rbuffer);c->wlength = 0;c->status = 0;} else {printf("WebSocket frame detected.\n");c->wlength = 0;c->status = 1;}return 0;
}

客户端发送request -> 服务端读取数据,判断是请求连接还是发送websocket帧 ->根据不同status做出相应反应

int ws_response(struct conn *c){//返回建立连接if(c->status == 0){response_websock(xxx);}else if (c->status == 1){//解码encoding(xxx);//编码decoding(xxx);}return c->wlength;
}

整体流程如下:

conn_list数组相当于一个用户和内核的中介,用来存放内核建立的连接以及用于拷贝内核接收到的数据。在websocket时,还额外引入了status的状态。

这个图可以清晰的显示出reactor的优点,即将业务和网络io管理分开。websocket用来实现业务,reactor用来实现网络io的管理。

课程地址:www.github.com/0voice

相关文章:

基于事件驱动的websocket简单实现

websocket的实现 什么是websocket&#xff1f; WebSocket 是一种网络通信协议&#xff0c;旨在为客户端和服务器之间提供全双工、实时的通信通道。它是在 HTML5 规范中引入的&#xff0c;可以让浏览器与服务器进行持久化连接&#xff0c;以便实现低延迟的数据交换。 WebSock…...

【leetcode100】反转链表

1、题目描述 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 2、初始思路 2.1 思路 # Definition for singly-linked list. # class ListNode: # …...

禅道Bug的一次迁移

一、场景 平时工作记录在公司禅道上的问题想备份一份到本地&#xff0c;但是又没有公司禅道的数据库信息&#xff0c;有时候出测试报告想批量调整数据方便截图很困难&#xff0c;同时也为了学习禅道数据流转过程&#xff0c;所以有了把缺陷保存到本地一份的想法。 实际上禅道支…...

c段和旁站讲解(附查询网址)

1. C段&#xff08;C类子网段&#xff09; C段就是一个IP地址的小范围。比如&#xff0c;假设你有一个家庭Wi-Fi网络&#xff0c;Wi-Fi会分配给你一组IP地址&#xff08;每个设备一个IP地址&#xff09;。如果你的网络分配的是类似 192.168.1.0 这样的IP地址&#xff0c;那么这…...

Linux编译Kernel时的文件zImage、文件dtb(dtbs)、核心模块分别是什么东西?

zImage文件的介绍 在编译Linux内核时&#xff0c;zImage 是一种内核映像文件&#xff0c;它是内核的压缩版本&#xff0c;通常用于引导嵌入式设备或其他资源有限的环境。 zImage 的具体含义 zImage 是 “Compressed Kernel Image” 的缩写。它是通过压缩原始的内核映像&…...

【深度学习】深刻理解“变形金刚”——Transformer

Transformer 是一种用于处理序列数据的深度学习模型架构&#xff0c;最初由 Vaswani 等人在 2017 年的论文《Attention is All You Need》中提出。它彻底改变了自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;成为许多高级任务&#xff08;如机器翻译、文本生成、问答…...

75_pandas.DataFrame 中查看和复制

75_pandas.DataFrame 中查看和复制 与pandas的DataFrame与NumPy数组ndarray类似&#xff0c;也有视图&#xff08;view&#xff09;和拷贝&#xff08;copy&#xff09;。 当使用loc[]或iloc[]等选择DataFrame的一部分以生成新的DataFrame时&#xff0c;与原对象共享内存的对…...

打电话玩手机识别-支持YOLO,COCO,VOC格式的标记,超高识别率可检测到手持打电话, 非接触式打电话,玩手机自拍等

打电话玩手机识别-支持YOLO&#xff0c;COCO&#xff0c;VOC格式的标记&#xff0c;超高识别率可检测到手持打电话&#xff0c; 非接触式打电话&#xff0c;玩手机自拍等1275个图片。 手持打电话&#xff1a; 非接触打电话 玩手机 数据集下载 yolov11:https://download.csdn…...

生产慎用之调试日志对空间矢量数据批量插入的性能影响-以MybatisPlus为例

目录 前言 一、一些缘由 1、性能分析 二、插入方式调整 1、批量插入的实现 2、MP的批量插入实现 3、日志的配置 三、默认处理方式 1、基础程序代码 2、执行情况 四、提升调试日志等级 1、在logback中进行设置 2、提升后的效果 五、总结 前言 在现代软件开发中&#xff0c;性能优…...

单片机:实现倒计时(附带源码)

使用单片机实现倒计时功能是一个常见的嵌入式应用&#xff0c;它能帮助你更好地理解如何进行时间控制和如何通过定时器实现精确的倒计时。通过该项目&#xff0c;你将学习如何使用单片机的定时器来进行时间计算&#xff0c;并通过LED或LCD显示倒计时的结果。 1. 项目概述 倒计…...

什么是多线程中的上下文切换

什么是多线程中的上下文切换 回答 上下文切换是指CPU从一个线程转到另一个线程时&#xff0c;需要保存当前线程的上下文状态&#xff0c;恢复另一个线程的上下文状态&#xff0c;以便于下一次恢复执行该线程时能够正确地运行。 在多线程编程中&#xff0c;上下文切换是一种常…...

如何在windwos批量拉取go mod

golang go-zero微服务开发,分的rpc项目太多了,变更了公共包,需要手动去拉取,直接一键拉取就好了,创建一个windwos脚本文件 文件名 tidy_all_go_mod.ps1 代码 # 辅助工具拉取go mod tidy # 根目录v99main执行 ./tidy_all_go_mod.ps1 # 定义项目的根目录 $RootDir Get-Locat…...

【Three.js基础学习】29.Hologram Shader

前言 three.js 通过着色器如何实现全息影像&#xff0c;以及一些动态的效果。 一些难点的思维&#xff0c;代码目录 下面图是摄像机视角观看影响上的时候&#xff0c;如何实现光影的渐变&#xff0c;透视以及叠加等。 一、代码 1.index.html <!DOCTYPE html> <html …...

文件包含进阶玩法以及绕过姿态

前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文整理文件包含漏洞的进阶玩法与绕过姿态 不涉及基础原理了 特殊玩法汇总 本地包含 文件包含上传文件 原理: php的文件包含有着把其他文件类型当做php代码执行的功效&#xff0c;文件上传一般会限制后缀&am…...

Markdown编辑器工具--Typora

下载链接...

PyTorch 的 torch.unbind 函数详解与进阶应用:中英双语

中文版 PyTorch 的 torch.unbind 函数详解与进阶应用 在深度学习中&#xff0c;张量的维度操作是基础又重要的内容。PyTorch 提供了许多方便的工具来完成这些操作&#xff0c;其中之一便是 torch.unbind。与常见的堆叠函数&#xff08;如 torch.stack&#xff09;相辅相成&am…...

四十六:如何使用Wireshark解密TLS/SSL报文?

TLS/SSL是保护网络通信的重要协议&#xff0c;其加密机制可以有效地防止敏感信息被窃取。然而&#xff0c;在调试网络应用或分析安全问题时&#xff0c;解密TLS/SSL流量是不可避免的需求。本文将介绍如何使用Wireshark解密TLS/SSL报文。 前提条件 在解密TLS/SSL报文之前&…...

【人工智能】OpenAI O1模型:超越GPT-4的长上下文RAG性能详解与优化指南

在人工智能&#xff08;AI&#xff09;领域&#xff0c;长上下文生成与检索&#xff08;RAG&#xff09; 已成为提升自然语言处理&#xff08;NLP&#xff09;模型性能的关键技术之一。随着数据规模与应用场景的不断扩展&#xff0c;如何高效地处理海量上下文信息&#xff0c;成…...

Ubuntu22.04搭建FTP服务器保姆级教程

在网络环境中&#xff0c;文件传输是一项至关重要的任务。FTP&#xff08;文件传输协议&#xff09;是一种基于客户端/服务器模式的协议&#xff0c;广泛用于在互联网上传输文件。Ubuntu作为一款流行的Linux发行版&#xff0c;因其稳定性和易用性而广受开发者和系统管理员的喜爱…...

操作系统(4)操作系统的结构

一、无序结构&#xff08;整体结构或模块组合结构&#xff09; 1.特点&#xff1a; 以大型表格和队列为中心&#xff0c;操作系统的各部分程序围绕着这些表格进行。操作系统由许多标准的、可兼容的基本单位&#xff08;称为模块&#xff09;构成&#xff0c;模块之间通过规定的…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看

文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008&#xff1a;是version产品的大版本。 R006&#xff1a;是release产品特性版本。 C009&#xff1a;是通用版 B0014&#xff1a;是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存&#xff1a;1GB 以上 硬盘&#xf…...

Ubuntu系统多网卡多相机IP设置方法

目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机&#xff0c;交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息&#xff0c;系统版本&#xff1a;Ubuntu22.04.5 LTS&#xff1b;内核版本…...