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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/113823.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NRK3301语音芯片在智能窗帘上的应用

窗帘是人们日常生活中所经常使用的家居产品&#xff0c;传统的窗帘大多都需要手动拉动窗帘使用&#xff1b;存在着拉拽费劲&#xff0c;挂钩容易掉落等问题。随着数字化转型的升级&#xff0c;推进了窗帘市场的高质量发展。智能窗帘也“适时出现”出现了&#xff0c;一款带有语…

[python 刷题] 287 Find the Duplicate Number

[python 刷题] 287 Find the Duplicate Number 题目&#xff1a; Given an array of integers nums containing n 1 integers where each integer is in the range [1, n] inclusive. There is only one repeated number in nums, return this repeated number. You must sol…

实现Traefik工具Dashboard远程访问:搭建便捷的远程管理平台

文章目录 前言1. Docker 部署 Trfɪk2. 本地访问traefik测试3. Linux 安装cpolar4. 配置Traefik公网访问地址5. 公网远程访问Traefik6. 固定Traefik公网地址 前言 Trfɪk 是一个云原生的新型的 HTTP 反向代理、负载均衡软件&#xff0c;能轻易的部署微服务。它支持多种后端 (D…

wireshark数据包内容查找功能详解

wireshark提供通过数据包特征值查找具体数据包的功能&#xff0c;具体查找功能如下&#xff0c; &#xff08;1&#xff09;选择查找目标区域&#xff08;也就是在哪里去匹配特征值&#xff09; 如下图&#xff0c;【分组列表】区域查找指的是在最上方的数据包列表区域查找&…

【Pillow库的内涵】01/3 进行基本图像操作

一、说明 Pillow 具有被 Python 社区广泛使用的优势&#xff0c;并且它不像其他一些图像处理库那样具有陡峭的学习曲线。应用PIL库的Image对象&#xff0c;益处很多&#xff0c;首先它可以处理网上URL文件&#xff0c;其次&#xff0c;图片可以方面转化成int32、64或float类型&…

自然语言处理---huggingface平台使用指南

1 huggingface介绍 Huggingface总部位于纽约&#xff0c;是一家专注于自然语言处理、人工智能和分布式系统的创业公司。他们所提供的聊天机器人技术一直颇受欢迎&#xff0c;但更出名的是他们在NLP开源社区上的贡献。Huggingface一直致力于自然语言处理NLP技术的平民化(democr…

设计模式:组合模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)

简介&#xff1a; 组合模式&#xff0c;它是一种用于处理树形结构、表示“部分-整体”层次结构的设计模式。它允许你将对象组合成树形结构&#xff0c;以表示部分和整体的关系。这种模式的主要目的是简化客户端代码&#xff0c;并使客户端以一致的方式处理单个对象和组合对象。…

Flyway Desktop updated

Flyway Desktop updated 为比较工件序列化和反序列化添加了额外的调试日志记录。 Flyway Desktop现在将记住以前用于创建项目和匹配克隆的位置。 新的脱机许可工作流现在已在Microsoft Windows上启用。 现在&#xff0c;在配置目标数据库列表时&#xff0c;环境ID是可见的。 现…

【虹科干货】Redis Enterprise vs ElastiCache——如何选择缓存解决方案?

使用Redis 或 Amazon ElastiCache 来作为缓存加速已经是业界主流的解决方案&#xff0c;二者各有什么优势&#xff1f;又有哪些区别呢&#xff1f; 文况速览&#xff1a; - Redis 是什么&#xff1f; - Redis Enterprise 是什么&#xff1f; - Amazon ElastiCache 是什么&…

tomcat动静分离

1.七层代理动静分离 nginx代理服务器&#xff1a;192.168.233.61 代理又是静态 tomcat1:192.168.233.71 tomcat2:192.168.233.72 全部关闭防火墙 在http模块里面 tomcat1&#xff0c;2 删除上面的hostname 148 配置 直接访问 http://192.168.66.17/index.jsp 2.四层七层动…

常见面试题-Redis专栏(二)

theme: cyanosis typora-copy-images-to: imgsRedisson 分布式锁&#xff1f;在项目中哪里使用&#xff1f;多久会进行释放&#xff1f;如何加强一个分布式锁&#xff1f; 答&#xff1a; 首先入门级别的分布式锁是通过 setnx 进行实现&#xff0c;使用 setnx 实现有四个注意…

中文编程开发语言工具应用案例:ps5体验馆计时收费管理系统软件

中文编程开发语言工具应用案例&#xff1a;ps5体验馆计时收费管理系统软件 软件部分功能&#xff1a; 1、计时计费功能&#xff1a;只需点开始计时即可&#xff0c;时间直观显示 2、商品管理功能&#xff1a;可以管理饮料等商品 3、会员管理功能&#xff1a;支持只用手机号作…

Arcgis 数据操作

在进行数据操作的时候&#xff0c;需要注意坐标系要一致&#xff0c;这是前提。 数据类型 文件地理数据库&#xff1a;gbd 个人地理数据库&#xff1a;mdb &#xff08;Mircosoft Access&#xff09; 矢量数据&#xff1a;shp 推荐使用gbd数据&#xff0c;效率会更高。 采…

【912.排序数组】

目录 一、题目描述二、算法原理2.1快速排序2.2归并排序 三、代码实现3.1快排代码实现3.2归并代码实现 一、题目描述 二、算法原理 2.1快速排序 2.2归并排序 三、代码实现 3.1快排代码实现 class Solution { public:int getRandom(int left,int right,vector<int>&…

[翻译]理解Postgres的IOPS:为什么数据即使都在内存,IOPS也非常重要

理解Postgres的IOPS&#xff1a;为什么数据即使都在内存&#xff0c;IOPS也非常重要 磁盘IOPS&#xff08;每秒输入/输出操作数&#xff09;是衡量磁盘系统性能的关键指标。代表每秒可以执行的读写操作数量。对于严重依赖于磁盘访问的PG来说&#xff0c;了解和优化磁盘IOPS对实…

Ubuntu系统下使用docker容器配置nginx并部署前端项目

1.下载 Nginx 镜像 命令 描述 docker pull nginx 下载最新版 Nginx 镜像 :2. 创建要挂载的宿主机目录 启动前需要先创建 Nginx 外部挂载的配置文件&#xff08; /home/nginx/conf/nginx.conf&#xff09; 之所以要先创建 , 是因为 Nginx 本身容器只存在 / etc/nginx 目录 ,…

iOS 13以下系统,使用iOS QQ 登录 SDK 崩溃问题

最近用iPhone 6p 系统&#xff1a;12.5.4 调用QQ三方登录&#xff0c;出现崩溃到初始化QQ SDK的位置 在询问了QQ官方客服后&#xff0c;得到了答复&#xff0c;可以放弃治疗了

2.IDEA的安装使用指南

学习Java的第二步应该是从IDEA下手&#xff0c;这篇博文介绍了它的安装及使用&#xff0c;希望大家看完后可以独立安装 ~ 文章目录 一、下载安装包二、安装 IDEA三、IDEA 初步上手 一、下载安装包 安装包可以从官网下载&#xff0c;也可以直接私信我拿取。这里主要介绍如何在官…

Swingbench 压力测试(超详细)

目录 前提需要有配置好的oracle哦 1、环境准备 2、安装Swingbench 3、造数据 4、压测 前提需要有配置好的oracle哦 1、环境准备 启动监听 lsnrctl start 启动数据库 sqlplus / as sysdba startup 创建表 CREATE TABLESPACE soe DATAFILE /u01/app/oracle/oradata/or…

2.卷积神经网络(CNN)

一句话引入&#xff1a; 如果我们要做图像识别&#xff0c;用的是一个200x200的图片&#xff0c;那么BP神经网络的输入层就需要40000个神经元&#xff0c;因为是全连接&#xff0c;所以整个BP神经网络的参数量就是160亿个&#xff0c;显然不能这样来训练网络&#xff0c;所以我…