当前位置: 首页 > 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;模块之间通过规定的…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

JS设计模式(4):观察者模式

JS设计模式(4):观察者模式 一、引入 在开发中&#xff0c;我们经常会遇到这样的场景&#xff1a;一个对象的状态变化需要自动通知其他对象&#xff0c;比如&#xff1a; 电商平台中&#xff0c;商品库存变化时需要通知所有订阅该商品的用户&#xff1b;新闻网站中&#xff0…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

WebRTC从入门到实践 - 零基础教程

WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC&#xff1f; WebRTC&#xff08;Web Real-Time Communication&#xff09;是一个支持网页浏览器进行实时语音…...

认识CMake并使用CMake构建自己的第一个项目

1.CMake的作用和优势 跨平台支持&#xff1a;CMake支持多种操作系统和编译器&#xff0c;使用同一份构建配置可以在不同的环境中使用 简化配置&#xff1a;通过CMakeLists.txt文件&#xff0c;用户可以定义项目结构、依赖项、编译选项等&#xff0c;无需手动编写复杂的构建脚本…...