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

NET7下用WebSocket做简易聊天室

NET7下用WebSocket做简易聊天室

步骤:

  1. 建立NET7的MVC视图模型控制器项目
  2. 创建websocket之间通信的JSON字符串对应的实体类
  3. 一个房间用同一个Websocket
  4. websocket集合类,N个房间
  5. 创建websocket中间件代码
  6. Program.cs中的核心代码,使用Websocket
  7. 聊天室HTML页面代码

参考文章:.NET Core中WebSocket的使用详解_.net websocket-CSDN博客

GIT源码地址:公开仓库 (里面还有以前做的.NET FRAMEWORK的websocket示例代码)

  1. 建立NET7的MVC视图模型控制器项目
  2. 创建websocket之间通信的JSON字符串对应的实体类
    namespace NetCore.WebSocketDemo.Models
    {/// <summary>/// websocket之间通信的JSON字符串转的实体类/// </summary>public class Message{/// <summary>/// websocket对应的ID/// </summary>public string SendClientId { set; get; }/// <summary>/// 加入房间join 发送消息send_to_room 离开房间levea/// </summary>public string action { set; get; }/// <summary>/// 房间号/// </summary>public string roomNo { set; get; }/// <summary>/// 昵称/// </summary>public string nick { set; get; }/// <summary>/// 发送的消息内容 /// </summary>public string msg { set; get; }}
    }

  3. 一个房间用同一个Websocket
    using System.Net.Sockets;
    using System.Net.WebSockets;
    using System.Text;namespace NetCore.WebSocketDemo.Models
    {/// <summary>/// 一个房间里的都用这个websocket/// </summary>public class WebsocketClient{public string Id { set; get; }public string RoomNo { set; get; }public WebSocket WebSocket { set; get; }public async Task SendMessageAsync(string text){var recvBytes = Encoding.UTF8.GetBytes(text);var sendBuffer = new ArraySegment<byte>(recvBytes);try{await WebSocket.SendAsync(sendBuffer, WebSocketMessageType.Text, true, CancellationToken.None);}catch (Exception ex){throw ex;} }}
    }
    

  4. websocket集合类,N个房间
    namespace NetCore.WebSocketDemo.Models
    {/// <summary>/// websocket集合类,N个房间/// </summary>public class WebsocketClientCollection{private static List<WebsocketClient> _clients = new List<WebsocketClient>();public static void Add(WebsocketClient client){_clients.Add(client);}public static void Remove(WebsocketClient client){_clients.Remove(client);}public static WebsocketClient Get(string clientId){var client = _clients.FirstOrDefault(c => c.Id == clientId);return client;}public static List<WebsocketClient> GetAll(){return _clients;}public static List<WebsocketClient> GetClientsByRoomNo(string roomNo){var client = _clients.Where(c => c.RoomNo == roomNo);return client.ToList();}}
    }
    

  5. 创建websocket中间件代码
    using Newtonsoft.Json;
    using System.Net.WebSockets;
    using System.Text;namespace NetCore.WebSocketDemo.Models
    {/// <summary>/// programe里用 app.UseWebsocketHandlerMiddleware();/// </summary>public static class WebsocketHandlerMiddlewareExtensions{public static IApplicationBuilder UseWebsocketHandlerMiddleware(this IApplicationBuilder builder){return builder.UseMiddleware<WebsocketHandlerMiddleware>();}}/// <summary>/// websocket中间件/// </summary>public class WebsocketHandlerMiddleware  {private readonly RequestDelegate _next;public WebsocketHandlerMiddleware(  RequestDelegate next){_next = next;} public async Task InvokeAsync(HttpContext context){if (context.Request.Path == "/ws"){//仅当网页执行new WebSocket("ws://localhost:5000/ws")时,后台会执行此逻辑if (context.WebSockets.IsWebSocketRequest){//后台成功接收到连接请求并建立连接后,前台的webSocket.onopen = function (event){}才执行WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();string clientId = Guid.NewGuid().ToString(); ;var wsClient = new WebsocketClient{Id = clientId,WebSocket = webSocket};try{await Handle(wsClient);}catch (Exception ex){await context.Response.WriteAsync("closed");}}else{context.Response.StatusCode = 404;}}else{await _next(context);}}private async Task Handle(WebsocketClient websocketClient){WebsocketClientCollection.Add(websocketClient);WebSocketReceiveResult clientData = null;do{var buffer = new byte[1024 * 1];//客户端与服务器成功建立连接后,服务器会循环异步接收客户端发送的消息,收到消息后就会执行Handle(WebsocketClient websocketClient)中的do{}while;直到客户端断开连接//不同的客户端向服务器发送消息后台执行do{}while;时,websocketClient实参是不同的,它与客户端一一对应//同一个客户端向服务器多次发送消息后台执行do{}while;时,websocketClient实参是相同的clientData = await websocketClient.WebSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);if (clientData.MessageType == WebSocketMessageType.Text && !clientData.CloseStatus.HasValue){var msgString = Encoding.UTF8.GetString(buffer);var message = JsonConvert.DeserializeObject<Message>(msgString);message.SendClientId = websocketClient.Id;HandleMessage(message);}} while (!clientData.CloseStatus.HasValue);//关掉使用WebSocket连接的网页/调用webSocket.close()后,与之对应的后台会跳出循环WebsocketClientCollection.Remove(websocketClient);}private void HandleMessage(Message message){var client = WebsocketClientCollection.Get(message.SendClientId);switch (message.action){case "join":client.RoomNo = message.roomNo;client.SendMessageAsync($"{message.nick} join room {client.RoomNo} success .");break;case "send_to_room":if (string.IsNullOrEmpty(client.RoomNo)){break;}var clients = WebsocketClientCollection.GetClientsByRoomNo(client.RoomNo);clients.ForEach(c =>{c.SendMessageAsync(message.nick + " : " + message.msg);});break;case "leave":#region 通过把连接的RoomNo置空模拟关闭连接var roomNo = client.RoomNo;client.RoomNo = "";#endregion#region 后台关闭连接//client.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);//WebsocketClientCollection.Remove(client); #endregionclient.SendMessageAsync($"{message.nick} leave room {roomNo} success .");break;default:break;}}}
    }
    

  6. Program.cs中的核心代码,使用Websocket
    var app = builder.Build();#region 配置中间件,使用websocketapp.UseWebSockets(new WebSocketOptions{KeepAliveInterval = TimeSpan.FromSeconds(60),ReceiveBufferSize = 1 * 1024});app.UseWebsocketHandlerMiddleware(); #endregion

  7. 聊天室HTML页面代码
<!DOCTYPE html>
<html>
<head><meta charset="utf-8" /><title>简易websocket聊天室应用</title>
</head>
<body><div style="margin-bottom:5px;">房间号: <input type="text" id="txtRoomNo" value="99999" /><button id="btnJoin">加入房间</button><button id="btnLeave">离开房间</button><button id="btnDisConnect">断开链接</button></div><div style="margin-bottom:5px;">我的昵称: <input type="text" id="txtNickName" value="niunan" /></div><div style="height:300px;width:600px"><textarea style="height:100%;width:100%" id="msgList"></textarea><div style="text-align: right"><input type="text" id="txtMsg" value="" placeholder="请输入您要发送的文本消息" />  <button id="btnSend">发送</button></div></div><script src="lib/jquery/dist/jquery.min.js"></script><script>var webSocket = new WebSocket("ws://localhost:5160/ws");//前台向后台发送连接请求,后台成功接收并建立连接后才会触发此事件webSocket.onopen = function (event) {console.log("Connection opened...");$("#msgList").val("WebSocket connection opened");};//后台向前台发送消息,前台成功接收后会触发此事件webSocket.onmessage = function (event) {console.log("Received message: " + event.data);if (event.data) {var content = $('#msgList').val();content = content + '\r\n' + event.data;$('#msgList').val(content);}};//后台关闭连接后/前台关闭连接后都会触发此事件webSocket.onclose = function (event) {console.log("Connection closed...");var content = $('#msgList').val();content = content + '\r\nWebSocket connection closed';$('#msgList').val(content);};$('#btnJoin').on('click', function () {var roomNo = $('#txtRoomNo').val();var nick = $('#txtNickName').val();if (!roomNo) {alert("请输入RoomNo");return;}var msg = {action: 'join',roomNo: roomNo,nick: nick};if (CheckWebSocketConnected(webSocket)) {webSocket.send(JSON.stringify(msg));}});$('#btnSend').on('click', function () {var message = $('#txtMsg').val();var nick = $('#txtNickName').val();if (!message) {alert("请输入发生的内容");return;}if (CheckWebSocketConnected(webSocket)) {webSocket.send(JSON.stringify({action: 'send_to_room',msg: message,nick: nick}));}});$('#btnLeave').on('click', function () {var nick = $('#txtNickName').val();var msg = {action: 'leave',roomNo: '',nick: nick};if (CheckWebSocketConnected(webSocket)) {webSocket.send(JSON.stringify(msg));}});$("#btnDisConnect").on("click", function () {if (CheckWebSocketConnected(webSocket)) {//部分浏览器调用close()方法关闭WebSocket时不支持传参//webSocket.close(001, "closeReason");webSocket.close();}});//判断当前websocket的状态/*CONNECTING:值为0,表示正在连接。OPEN:值为1,表示连接成功,可以通信。CLOSING:值为2,表示连接正在关闭。CLOSED:值为3,表示连接已经关闭,或者打开连接失败。*/function CheckWebSocketConnected(ws) {var b = false;switch (ws.readyState) {case WebSocket.CONNECTING:    // 也可以用0// do somethingbreak;case WebSocket.OPEN:            // 也可以用1// do somethingb = true;break;case WebSocket.CLOSING:        // 也可以用2// do somethingbreak;case WebSocket.CLOSED:        // 也可以用3// do somethingbreak;default:// this never happensbreak;}return b;}</script>
</body>
</html>

相关文章:

NET7下用WebSocket做简易聊天室

NET7下用WebSocket做简易聊天室 步骤&#xff1a; 建立NET7的MVC视图模型控制器项目创建websocket之间通信的JSON字符串对应的实体类一个房间用同一个Websocketwebsocket集合类&#xff0c;N个房间创建websocket中间件代码Program.cs中的核心代码&#xff0c;使用Websocket聊…...

详解API基础知识

目录 什么是API: API 的设计原则包括&#xff1a; API 的开发流程包括以下几个步骤&#xff1a; API 的使用场景包括&#xff1a; API 的优势包括&#xff1a; 然而&#xff0c;API 也存在一些挑战和问题&#xff0c;例如&#xff1a; 什么是API: API&#xff08;应用程…...

b树和b+树

二叉树和平衡二叉树 二叉树&#xff0c;每个节点支持两个分支的树结构&#xff0c;相比于单向链表&#xff0c;多了一个分支。 二叉查找树&#xff0c;在二叉树的基础上增加了一个规则&#xff0c;左子树的所有节点的值都小于它的根 节点&#xff0c;右子树的所有子节点都大于它…...

Linux 下 Java 安装字体方法

因上线访问图字体乱码了&#xff0c;因为在windows下设置的微软雅黑&#xff0c;linux默认是没有的&#xff0c;所以需要给jdk安装一个微软雅黑字体。按照步骤来&#xff0c;so easy&#xff01; 1&#xff09;首先找到windows下面的字体&#xff0c;不用去其他地方下了&#…...

敏捷开发的实施要素和实现敏捷的实际改进

​ 敏捷开发的实施要素如下&#xff1a; 个体和交互&#xff1a;胜过过程和工具。可以工作的软件&#xff1a;胜过面面俱到的文档。客户合作&#xff1a;胜过合同谈判。响应变化&#xff1a;胜过遵循计划。 敏捷开发过程是一个增量的、迭代的过程&#xff0c;责任人、开发人…...

学会使用Pandas进行数据清洗

大家好&#xff0c;如果你对数据科学感兴趣&#xff0c;那么数据清洗可能对你来说是一个熟悉的术语&#xff0c;本文将向你介绍使用Pandas进行数据清洗的过程。我们的数据通常来自多个资源&#xff0c;而且并不干净&#xff0c;它可能包含缺失值、重复值、错误或不需要的格式等…...

Stable Diffusion WebUI扩展a1111-sd-webui-tagcomplete之Booru风格Tag自动补全功能详细介绍

安装地址 直接附上地址先: Ranting8323 / A1111 Sd Webui Tagcomplete GitCodeGitCode——开源代码托管平台,独立第三方开源社区,Git/Github/Gitlabhttps://gitcode.net/ranting8323/a1111-sd-webui-tagcomplete.git上面是GitCode的地址,下面是GitHub的地址,根据自身情…...

Linux中iostat命令

iostat命令是IO性能分析的常用工具&#xff0c;其是input/output statistics的缩写。 一、安装 yum install sysstat -y二、参数说明 -c: 显示CPU使用情况-d: 显示磁盘使用情况--dec{ 0 | 1 | 2 }: 指定要使用的小数位数&#xff0c;默认为 2-g GROUP_NAME { DEVICE [...] | A…...

Pandas数据处理分析系列3-数据如何预览

Pandas-数据预览 Pandas 导入数据后,我们通常需要对数据进行预览,以便更好的进行数据分析。常见数据预览的方法如下: ①head() 方法 功能:读取数据的前几行,默认显示前5行 语法结构:df.head(行数) df1=pd.read_excel("销售表.xlsx",sheet_name="手机销…...

【汇编语言-王爽】第二章:寄存器

知识点 &#xff08;一&#xff09;寄存器 一个典型的CPU由运算器、控制器、寄存器等器件构成&#xff0c;这些器件靠内部总线相连。8086CPU有14个寄存器&#xff1a;AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW。其中AX、BX、CX、DX为通用寄存器&#xff0c;可…...

5G学习笔记之5G频谱

参考&#xff1a;《5G NR通信标准》1. 5G频谱 1G和2G移动业务的频段主要在800MHz~900MHz&#xff0c;存在少数在更高或者更低频段&#xff1b;3G和4G的频段主要在450MHz ~ 6GHz&#xff1b;5G主要是410MHz ~ 6GHz&#xff0c;以及24GHz ~ 52GHz。 5G频谱跨度较大&#xff0c;可…...

CSS 浮动布局

本文参考 https://blog.csdn.net/ZhangJiWei_2019/article/details/114669722 文档流简介 正常文档流 正常文档流&#xff0c;又称为“普通文档流”或“普通流”&#xff0c;也就是W3C标准所说的“normal flow”。 我们先来看一下正常文档流的简单定义&#xff1a;正常文档…...

CentOS 系统安装和使用Docker服务

系统环境 使用下面的命令&#xff0c;可以查看CentOS系统的版本。 lsb_release -a结果&#xff1a; 说明我的系统是7.9.2009版本的 安装Docker服务 依次执行下面的指令&#xff1a; yum install -y yum-utilsyum install -y docker即可安装docker服务 如果这样安装不成功…...

Docker-镜像的备份迁移及私有仓库的搭建

一、Docker-备份与迁移 A服务器系统配置 B服务器系统配置 1.用命令将容器保存为镜像。 案例&#xff0c;将A服务器的Docker容器迁移到另外一台服务器B&#xff0c;A服务器的容器配置过对应的文件&#xff0c;不想在B服务器重新搭建&#xff0c;可以使用该案例。 docker c…...

SQL数据库管理工具RazorSQL mac中文版特点与功能

RazorSQL mac是一款功能强大的SQL数据库管理工具&#xff0c;它支持多种数据库&#xff0c;包括MySQL、Oracle、Microsoft SQL Server、SQLite、PostgreSQL等。 RazorSQL mac 软件特点和功能 多种数据库支持&#xff1a;RazorSQL支持多种数据库&#xff0c;用户可以通过一个工…...

Unigui可以使用WebSocket进行客户端之间的实时互相发消息

Unigui可以使用WebSocket进行客户端之间的实时互相发消息。WebSocket是一种支持双向通信的网络协议&#xff0c;可以使客户端和服务器之间实时地进行数据交换。 实现步骤: 1. 在Unigui项目中添加WebSocket组件。 2. 在WebModule的OnCreate事件中开启WebSocket服务。 proced…...

Win32 简单日志实现

简单实现日志保存, 支持设置日志文件数量, 单个日志文件大小上限, 自动超时保存日志, 日志缓存超限保存 CLogUtils.h #pragma once#include <string> #include <windows.h> #include <vector> #include <map> #include <mutex> #include <tc…...

保姆级阿里云ESC服务器安装nodejs或Linux安装nodejs

1. 创建node文件夹 默认 /opt 下边 /opt/node 也可建到其他地方&#xff0c;如/usr/local/node 等 创建后切换到文件夹下 cd /opt/node cd /opt/node2. 下载node并解压 使用命令下载node wget https://nodejs.org/dist/v18.12.0/node-v18.12.0-linux-x64.tar.xz wget https…...

《动手学深度学习 Pytorch版》 9.3 深度循环神经网络

将多层循环神经网络堆叠在一起&#xff0c;通过对几个简单层的组合&#xff0c;产生一个灵活的机制。其中的数据可能与不同层的堆叠有关。 9.3.1 函数依赖关系 将深度架构中的函数依赖关系形式化&#xff0c;第 l l l 个隐藏层的隐状态表达式为&#xff1a; H t ( l ) ϕ l …...

2023-10-19 LeetCode每日一题(同积元组)

2023-10-19每日一题 一、题目编号 1726. 同积元组二、题目链接 点击跳转到题目位置 三、题目描述 给你一个由 不同 正整数组成的数组 nums &#xff0c;请你返回满足 a * b c * d 的元组 (a, b, c, d) 的数量。其中 a、b、c 和 d 都是 nums 中的元素&#xff0c;且 a ! b…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

Kafka主题运维全指南:从基础配置到故障处理

#作者&#xff1a;张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1&#xff1a;主题删除失败。常见错误2&#xff1a;__consumer_offsets占用太多的磁盘。 主题日常管理 …...

深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙

WebGL&#xff1a;在浏览器中解锁3D世界的魔法钥匙 引言&#xff1a;网页的边界正在消失 在数字化浪潮的推动下&#xff0c;网页早已不再是静态信息的展示窗口。如今&#xff0c;我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室&#xff0c;甚至沉浸式的V…...

【java面试】微服务篇

【java面试】微服务篇 一、总体框架二、Springcloud&#xff08;一&#xff09;Springcloud五大组件&#xff08;二&#xff09;服务注册和发现1、Eureka2、Nacos &#xff08;三&#xff09;负载均衡1、Ribbon负载均衡流程2、Ribbon负载均衡策略3、自定义负载均衡策略4、总结 …...