TCP客户端模拟链接websocket服务端发送消息(二)

ops/2024/12/28 2:36:39/

兄弟们,我来填坑了,o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o,前几天写了个tcp模拟websocket客户端的以为完成,后面需要发送消息给服务端,以为简单不就是一个发送消息么,这不是一下的事情,诺 sslStrem.Write(bt,0,bt.length);这不就是么,结果结果啪啪啪打脸,脸都要肿了o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o。

错误信息:“xeaThe client frame was not masked but all client frames must be masked” 后面查找资料发现需要按照websocket协议来发送websocket结构体的消息帧。

如下图

WebSocket 消息帧结构

    FIN(1位): 表示是否是最后一帧。
    RSV1, RSV2, RSV3(各1位): 保留位,通常为 0。
    Opcode(4位): 帧的类型(例如:文本帧、二进制帧、关闭帧等)。
    Mask(1位): 表示负载是否被掩码。
    Payload length(7/7+16/7+64位): 表示负载数据的长度。
    Masking key(32位): 如果 Mask 为 1,则存在掩码键。
    Payload data: 实际传输的数据。

找了一大堆资料结果还是没成功,一想到有开源的websocket开源代码里面肯定是有发送消息的方法。得按照这个思路在websocket-sharp开源里找到了,然后在里面看啊看游啊游,头都要大了,最后还是仿写了一套相对简单的,后面一试成功了不容易啊。

代码如下:

   public void Send2(string data){byte[] bytes = Encoding.UTF8.GetBytes(data);Stream sourceStream = new MemoryStream(bytes);try{var len = sourceStream.Length;if (len == 0){bool sent3 = send2(0x1, 0x1, new byte[0], false);return;}var quo = len / 1016;var rem = (int)(len % 1016);byte[] buff = null;if (quo == 0){buff = new byte[rem];bool sent2 = sourceStream.Read(buff, 0, rem) == rem && send2(0x1, 0x1, buff, false);return;}if (quo == 1 && rem == 0){buff = new byte[1016];bool sent2 = sourceStream.Read(buff, 0, 1016) == 1016 && send2(0x1, 0x1, buff, false);return;}/* Send fragments */// Beginbuff = new byte[1016];var sent = sourceStream.Read(buff, 0, 1016) == 1016 && send2(0x0, 0x1, buff, false);if (!sent)return;// Continuevar n = rem == 0 ? quo - 2 : quo - 1;for (long i = 0; i < n; i++){sent = sourceStream.Read(buff, 0, 1016) == 1016 && send2(0x0, 0x0, buff, false);if (!sent)return;}// Endif (rem == 0)rem = 1016;elsebuff = new byte[rem];sent = sourceStream.Read(buff, 0, rem) == rem && send2(0x1, 0x0, buff, false);return;}catch{}finally{sourceStream.Dispose();}}private bool send2(byte fin, byte opcode, byte[] data, bool compressed){byte _payloadLength;byte _rsv1;byte _rsv2;byte _rsv3;byte[] _extPayloadLength;byte[] _maskingKey;byte _mask;//PayloadData _payloadData = new PayloadData(data);_rsv1 = compressed ? (byte)0x1 : (byte)0x0;_rsv2 = 0x0;_rsv3 = 0x0;var len = data.Length;if (len < 126){_payloadLength = (byte)len;_extPayloadLength = new byte[0];}else if (len < 0x010000){_payloadLength = (byte)126;var ret = BitConverter.GetBytes((ushort)len);Array.Reverse(ret);_extPayloadLength = ret;//_extPayloadLength = ((ushort)len).ToByteArray(ByteOrder.Big);}else{_payloadLength = (byte)127;var ret = BitConverter.GetBytes((ulong)len);Array.Reverse(ret);_extPayloadLength = ret;//_extPayloadLength = ((ulong)len).ToByteArray(ByteOrder.Big);}if (true){_mask = 0x1;var key = new byte[4];RNGCryptoServiceProvider RandomNumber = new RNGCryptoServiceProvider();RandomNumber.GetBytes(key);_maskingKey = key;for (long i = 0; i < data.Length; i++){data[i] = (byte)(data[i] ^ key[i % 4]);}}using (var buff = new MemoryStream()){var header = (int)fin;header = (header << 1) + (int)_rsv1;header = (header << 1) + (int)_rsv2;header = (header << 1) + (int)_rsv3;header = (header << 4) + (int)opcode;header = (header << 1) + (int)_mask;header = (header << 7) + (int)_payloadLength;var uint16Header = (ushort)header;var ret = BitConverter.GetBytes(uint16Header);Array.Reverse(ret);var rawHeader = ret;buff.Write(rawHeader, 0, 2);if (_payloadLength >= 126)buff.Write(_extPayloadLength, 0, _extPayloadLength.Length);if (_mask == 0x1)buff.Write(_maskingKey, 0, 4);if (_payloadLength > 0){var bytes = data;if (_payloadLength > 126){using (var src = new MemoryStream(bytes))src.CopyTo(buff, 1024);}elsebuff.Write(bytes, 0, bytes.Length);}buff.Close();var rawFrame = buff.ToArray();try{sslStream.Write(rawFrame, 0, rawFrame.Length);}catch{return false;}return true;}}

https://download.csdn.net/download/qq_35319925/90187478icon-default.png?t=O83Ahttps://download.csdn.net/download/qq_35319925/90187478 上面提供了代码链接下载地址。获取电脑本地的音频设备并实时传输音频数据到后台。这个demo是可以跑通的。方便大家使用


http://www.ppmy.cn/ops/145545.html

相关文章

音视频入门基础:AAC专题(13)——FFmpeg源码中,获取ADTS格式的AAC裸流音频信息的实现

音视频入门基础&#xff1a;AAC专题系列文章&#xff1a; 音视频入门基础&#xff1a;AAC专题&#xff08;1&#xff09;——AAC官方文档下载 音视频入门基础&#xff1a;AAC专题&#xff08;2&#xff09;——使用FFmpeg命令生成AAC裸流文件 音视频入门基础&#xff1a;AAC…

SQL 实战—递归 SQL:层级结构查询与处理树形数据

在数据库中&#xff0c;树形或层级结构的数据非常常见&#xff0c;如组织架构、分类目录、评论回复等。SQL 提供了递归查询的能力&#xff0c;通过递归通用表表达式&#xff08;CTE&#xff09;&#xff0c;可以高效地查询和处理树形数据。本文将通过实际案例详细讲解递归 SQL …

低代码开源项目Joget的研究——Joget7社区版安装部署

大纲 环境准备安装必要软件配置Java配置JAVA_HOME配置Java软链安装三方库 获取源码配置MySql数据库创建用户创建数据库导入初始数据 配置数据库连接配置sessionFactory编译下载tomcat启动下载aspectjweaver移动jw.war文件编写脚本运行 测试参考资料 Joget&#xff0c;作为一款开…

【VScode】第三方GPT编程工具-CodeMoss安装教程

一、CodeMoss是什么&#xff1f; CodeMoss是一款集编程、学习和办公于一体的高效工具。它兼容多种主流平台&#xff0c;包括VSCode、IDER、Chrome插件、Web和APP等&#xff0c;支持插件安装&#xff0c;尤其在VSCode和IDER上的表现尤为出色。无论你是编程新手还是资深开发者&a…

webpack3 webpack4 webpack5 有什么区别

性能优化 Webpack 3 性能优化主要依赖开发者手动配置各种插件。例如&#xff0c;在代码分割方面&#xff0c;需要通过CommonsChunkPlugin来实现公共模块的提取&#xff0c;其配置相对复杂。如果配置不当&#xff0c;可能会导致模块重复打包等问题&#xff0c;影响构建效率和最终…

Linux-Ubuntu之按键中断实验

Linux-Ubuntu之按键中断实验 一&#xff0c; 汇编对中断进行设置二&#xff0c;C语言模块1.中断配置2.GPIO口配置3.按键配置4.主函数 三&#xff0c;总结 一&#xff0c; 汇编对中断进行设置 列出对中断向量表&#xff0c;主要用的是IRQ中断和复位中断服务函数&#xff0c;复位…

什么是ondelete cascade以及使用sqlite演示ondelete cascade使用案例

什么是ondelete cascade ‌ON DELETE CASCADE是数据库中的一种约束&#xff0c;用于自动删除相关的记录‌。具体来说&#xff0c;当一个表中的记录&#xff08;父表&#xff09;被删除时&#xff0c;与其相关的其他表&#xff08;子表&#xff09;中的记录也会被自动删除&…

[python SQLAlchemy数据库操作入门]-11.面向对象方式操作股票数据

哈喽,大家好,我是木头左! 通过ORM,开发者可以使用Python类来表示数据库表,从而使得数据库操作更加直观和易于维护。本文将介绍如何使用SQLAlchemy ORM来操作股票数据。 安装 SQLAlchemy 需要安装SQLAlchemy库。可以使用pip命令进行安装: pip install sqlalchemy定义股票…