NET7下用WebSocket做简易聊天室

news2024/11/16 17:36:38

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); 
                        #endregion
    
                        client.SendMessageAsync($"{message.nick} leave room {roomNo} success .");
                        break;
                    default:
                        break;
                }
            }
        }
    }
    

  6. Program.cs中的核心代码,使用Websocket
    var app = builder.Build();
    
                #region 配置中间件,使用websocket
                app.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 something
                    break;
                case WebSocket.OPEN:            // 也可以用1
                    // do something
                    b = true;
                    break;
                case WebSocket.CLOSING:        // 也可以用2
                    // do something
                    break;
                case WebSocket.CLOSED:        // 也可以用3
                    // do something
                    break;
                default:
                    // this never happens
                    break;
            }
            return b;
        }
    </script>
</body>
</html>

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

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

相关文章

C语言利用已知公式估算e的近似值

编写一个函数&#xff0c;由公式e11/1&#xff01;1/2! 1/3!…&#xff0c;计算不同精确度下e的近似值。要求能够用键盘揄入指定的精确度&#xff0c;并输出该精确度下的e的近似值 例如&#xff1a;输入精确度为10e-6&#xff0c;则输出结果&#xff1a;2.718279。 #include &…

原来你根本不会找资源~

作为一名合格的传媒工作者&#xff0c;我不允许还有人不会找资源&#xff0c;下面这个四个网站必须码住&#xff0c;百分之八十的素材都来源于这些&#xff01;一、XDown【xdown.chuangzuoniu.com】 在线视频下载器&#xff0c;支持1000主流在线视频网站&#xff0c;一键下载多…

iOS原生、Android 原生, flutter 三种方式给照片流添加文字(水印)

效果图:三中代码实现的效果差不多 Swift:代码 import UIKitclass ImageWatermarking: NSObject {static func textToImage(drawText text: String, inImage initImage: UIImage, atPoint point: CGPoint) -> UIImage {let textColor = UIColor.whitelet textFont = UIFon…

如何选择一款好的护眼台灯?双十一必入护眼台灯推荐

随着科技的不断发展&#xff0c;工作时使用电子设备越来越普遍,如何保护我们的眼睛不受蓝光、频闪等危害就变得极其重要了。护眼台灯&#xff0c;顾名思义就是保护眼睛的台灯&#xff0c;其工作原理是在光源处使用特殊的防蓝光灯珠&#xff0c;并通过控制电流的稳定性来达到防频…

easyrecovery2024数据恢复软件最新版本下载

easyrecovery是PC上数据恢复领域相当给力的应用软件之一&#xff0c;它具有操作安全&#xff0c;价格便宜&#xff0c;支持用户自主操作等特点&#xff0c;能支持从各种存储介质恢复删除、格式化或者丢失的文件&#xff0c;从任何存储介质设备上的损坏&#xff0c;删除&#xf…

最简网卡驱动

在内核注册自定义的网卡驱动&#xff0c;并通过打印用户空间和内核的交互数据&#xff0c;可以更深层次的理解网络协议。 驱动代码&#xff1a; #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/netdevi…

每日一题 2525. 根据规则将箱子分类 (简单)

简单题&#xff0c;直接分类就好 class Solution:def categorizeBox(self, length: int, width: int, height: int, mass: int) -> str:if length > 10**4 or width > 10**4 or height > 10**4 or length*width*height > 10**9:return "Both" if mas…

短链服务如何定制域名

短链接不仅方便推广&#xff0c;而且还能能够保护原来的落地域名&#xff0c;为落地域名提供一个屏障&#xff0c;那么短链接的自定义域名怎么绑定呢&#xff1f;接下来就为大家带来详细的绑定步骤&#xff0c;需要的伙伴可以来看看。 域名绑定步骤 首先您或您的公司团队需要拥…

易点易动:一周完成数万件固定资产管理盘点的解决方案

在现代商业环境中&#xff0c;固定资产管理盘点是企业不可或缺的重要环节。然而&#xff0c;传统的手工盘点方法往往效率低下、耗时费力&#xff0c;且容易出现错误。为了解决这一难题&#xff0c;易点易动固定资产管理系统提供了一种高效、准确的解决方案&#xff0c;使企业能…

【代码随想录】算法训练营 第六天 第三章 哈希表 Part 1

目录 哈希表理论基础 242. 有效的字母异位词 题目 思路 代码 349. 两个数组的交集 题目 思路 代码 202. 快乐数 题目 思路 代码 1. 两数之和 题目 思路 代码 无序集合解法 map解法 哈希表理论基础 哈希表就是之前在数据结构中学过的散列表&#xff0c;通过哈…

2023年中国工业气体行业研究报告

第一章 行业概况 1.1 定义 工业气体行业是一个不可或缺的产业领域&#xff0c;它为多种行业提供关键的产品和服务。工业气体&#xff0c;包括氧气、氮气、氩气、二氧化碳、氦气、氢气及特种气体等&#xff0c;是现代工业生产和科学研究的基础。这些气体在不同的领域具有广泛的…

配置PPPoE拨号双链路上行备份示例

组网图形 图1 设备作为PPPoE Client双链路上行备份组网图 组网需求配置思路操作步骤配置文件 组网需求 如图1所示&#xff0c;设备下行通过GE1/0/0连接局域网用户&#xff0c;上行通过GE2/0/0接入PPPoE Server1作为主链路&#xff0c;通过GE3/0/0接入PPPoE Server2作为备份…

网站管家机器人在为企业获客方面起什么作用?

随着科技的不断进步和人们对便捷服务的需求增加&#xff0c;网站管家机器人成为了现代企业获客的重要工具。作为一种基于人工智能技术的在线助手&#xff0c;网站管家机器人可以与访问企业网站的用户进行智能对话&#xff0c;并提供即时的帮助和解答。 网站管家机器人在为企业获…

图吧工具箱2023,供大家学习研究参考!

1.修复Thaiphoon异常关闭的问题。 2.修复<下载皮肤编辑器>无效的问题。 3.移除OCCT。 4.添加Steam官方下载页的快捷方式。 5.移除老旧的兼容模式。 下载&#xff1a;https://download.csdn.net/download/weixin_43097956/88449046

Si24R2|2.4G单发射芯片 +7dBm可调功率 校讯通

Si24R2是一种通用、低功耗、高性能的2.4GHz无线射频发射芯片&#xff0c;主要用于单向通信系统&#xff0c;以降低系统成B&#xff0c;在运行中与si24r1兼容。 Si24r2具有低功耗和低成B。 它主要用于单向低功率传输系统&#xff0c;如无线控制系统、无线数据采集系统等。 Si2…

8.对象贴地

愿你出走半生,归来仍是少年&#xff01; 在场景中&#xff0c;有时候需要对地物&#xff08;房屋、楼宇&#xff09;进行贴地处理&#xff0c;或者说相对地面高度&#xff08;井盖、井室&#xff09;进行设置。 通过自定义的Terrain切片以及影像瓦片构建的三维场景应该如何获取…

Optuna:带仪表盘的可视化的超参数优化

1、引言 Optuna是一个由日本东京大学开发的自动化超参数优化库&#xff0c;用于机器学习和人工智能。它可用于自动优化神经网络、随机森林等模型的超参数&#xff0c;以改善模型的性能和准确性。Optuna还可以与其他流行的机器学习框架&#xff08;如TensorFlow和PyTorch&#…

企业数据泄密的场景有哪些?怎样斩断员工泄密风险?

企业数据泄露事件频频发生、屡禁不止&#xff0c;根据美国威瑞森通信公司&#xff08;Verizon&#xff09;《2023 Data Breach Investigations Report》&#xff0c;由外部攻击导致的数据泄露事件中&#xff0c;95%的外部攻击是以金钱利益为驱使的。同样&#xff0c;闪捷发布的…

开发者视角下的直播实时美颜SDK:集成和自定义的方法

在今天的数字时代&#xff0c;视频直播已经成为了一种强大的社交工具和娱乐方式。为了让直播内容更吸引人&#xff0c;实时美颜技术已经成为直播应用的重要组成部分。本文将从开发者的角度探讨如何集成和自定义直播实时美颜SDK&#xff0c;以满足用户的美颜需求。 一、什么是直…