NET7下用WebSocket做简易聊天室
NET7下用WebSocket做简易聊天室
步骤:
- 建立NET7的MVC视图模型控制器项目
- 创建websocket之间通信的JSON字符串对应的实体类
- 一个房间用同一个Websocket
- websocket集合类,N个房间
- 创建websocket中间件代码
- Program.cs中的核心代码,使用Websocket
- 聊天室HTML页面代码
参考文章:.NET Core中WebSocket的使用详解_.net websocket-CSDN博客
GIT源码地址:公开仓库 (里面还有以前做的.NET FRAMEWORK的websocket示例代码)

- 建立NET7的MVC视图模型控制器项目
- 创建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; }} } - 一个房间用同一个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;} }} } - 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();}} } - 创建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;}}} } - Program.cs中的核心代码,使用Websocket
var app = builder.Build();#region 配置中间件,使用websocketapp.UseWebSockets(new WebSocketOptions{KeepAliveInterval = TimeSpan.FromSeconds(60),ReceiveBufferSize = 1 * 1024});app.UseWebsocketHandlerMiddleware(); #endregion - 聊天室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做简易聊天室 步骤: 建立NET7的MVC视图模型控制器项目创建websocket之间通信的JSON字符串对应的实体类一个房间用同一个Websocketwebsocket集合类,N个房间创建websocket中间件代码Program.cs中的核心代码,使用Websocket聊…...
详解API基础知识
目录 什么是API: API 的设计原则包括: API 的开发流程包括以下几个步骤: API 的使用场景包括: API 的优势包括: 然而,API 也存在一些挑战和问题,例如: 什么是API: API(应用程…...
b树和b+树
二叉树和平衡二叉树 二叉树,每个节点支持两个分支的树结构,相比于单向链表,多了一个分支。 二叉查找树,在二叉树的基础上增加了一个规则,左子树的所有节点的值都小于它的根 节点,右子树的所有子节点都大于它…...
Linux 下 Java 安装字体方法
因上线访问图字体乱码了,因为在windows下设置的微软雅黑,linux默认是没有的,所以需要给jdk安装一个微软雅黑字体。按照步骤来,so easy! 1)首先找到windows下面的字体,不用去其他地方下了&#…...
敏捷开发的实施要素和实现敏捷的实际改进
敏捷开发的实施要素如下: 个体和交互:胜过过程和工具。可以工作的软件:胜过面面俱到的文档。客户合作:胜过合同谈判。响应变化:胜过遵循计划。 敏捷开发过程是一个增量的、迭代的过程,责任人、开发人…...
学会使用Pandas进行数据清洗
大家好,如果你对数据科学感兴趣,那么数据清洗可能对你来说是一个熟悉的术语,本文将向你介绍使用Pandas进行数据清洗的过程。我们的数据通常来自多个资源,而且并不干净,它可能包含缺失值、重复值、错误或不需要的格式等…...
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性能分析的常用工具,其是input/output statistics的缩写。 一、安装 yum install sysstat -y二、参数说明 -c: 显示CPU使用情况-d: 显示磁盘使用情况--dec{ 0 | 1 | 2 }: 指定要使用的小数位数,默认为 2-g GROUP_NAME { DEVICE [...] | A…...
Pandas数据处理分析系列3-数据如何预览
Pandas-数据预览 Pandas 导入数据后,我们通常需要对数据进行预览,以便更好的进行数据分析。常见数据预览的方法如下: ①head() 方法 功能:读取数据的前几行,默认显示前5行 语法结构:df.head(行数) df1=pd.read_excel("销售表.xlsx",sheet_name="手机销…...
【汇编语言-王爽】第二章:寄存器
知识点 (一)寄存器 一个典型的CPU由运算器、控制器、寄存器等器件构成,这些器件靠内部总线相连。8086CPU有14个寄存器:AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW。其中AX、BX、CX、DX为通用寄存器,可…...
5G学习笔记之5G频谱
参考:《5G NR通信标准》1. 5G频谱 1G和2G移动业务的频段主要在800MHz~900MHz,存在少数在更高或者更低频段;3G和4G的频段主要在450MHz ~ 6GHz;5G主要是410MHz ~ 6GHz,以及24GHz ~ 52GHz。 5G频谱跨度较大,可…...
CSS 浮动布局
本文参考 https://blog.csdn.net/ZhangJiWei_2019/article/details/114669722 文档流简介 正常文档流 正常文档流,又称为“普通文档流”或“普通流”,也就是W3C标准所说的“normal flow”。 我们先来看一下正常文档流的简单定义:正常文档…...
CentOS 系统安装和使用Docker服务
系统环境 使用下面的命令,可以查看CentOS系统的版本。 lsb_release -a结果: 说明我的系统是7.9.2009版本的 安装Docker服务 依次执行下面的指令: yum install -y yum-utilsyum install -y docker即可安装docker服务 如果这样安装不成功…...
Docker-镜像的备份迁移及私有仓库的搭建
一、Docker-备份与迁移 A服务器系统配置 B服务器系统配置 1.用命令将容器保存为镜像。 案例,将A服务器的Docker容器迁移到另外一台服务器B,A服务器的容器配置过对应的文件,不想在B服务器重新搭建,可以使用该案例。 docker c…...
SQL数据库管理工具RazorSQL mac中文版特点与功能
RazorSQL mac是一款功能强大的SQL数据库管理工具,它支持多种数据库,包括MySQL、Oracle、Microsoft SQL Server、SQLite、PostgreSQL等。 RazorSQL mac 软件特点和功能 多种数据库支持:RazorSQL支持多种数据库,用户可以通过一个工…...
Unigui可以使用WebSocket进行客户端之间的实时互相发消息
Unigui可以使用WebSocket进行客户端之间的实时互相发消息。WebSocket是一种支持双向通信的网络协议,可以使客户端和服务器之间实时地进行数据交换。 实现步骤: 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 也可建到其他地方,如/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 深度循环神经网络
将多层循环神经网络堆叠在一起,通过对几个简单层的组合,产生一个灵活的机制。其中的数据可能与不同层的堆叠有关。 9.3.1 函数依赖关系 将深度架构中的函数依赖关系形式化,第 l l l 个隐藏层的隐状态表达式为: H t ( l ) ϕ l …...
2023-10-19 LeetCode每日一题(同积元组)
2023-10-19每日一题 一、题目编号 1726. 同积元组二、题目链接 点击跳转到题目位置 三、题目描述 给你一个由 不同 正整数组成的数组 nums ,请你返回满足 a * b c * d 的元组 (a, b, c, d) 的数量。其中 a、b、c 和 d 都是 nums 中的元素,且 a ! b…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...
