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…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
