探索WebSocket在ASP.NET Core中的实时通信应用与实现策略

news2024/12/24 6:56:38

文章目录

  • 前言
  • 一、创建 ASP.NET Core 项目
  • 二、配置中间件以支持 WebSocket
    • 1.启动类Program.cs
    • 2.WebSocket连接管理器
    • 3.WebSocket事件管理器
    • 4.WebSocket连接入口
  • 三、客户端实现
  • 总结


前言

    在 ASP.NET Core 中集成 WebSocket 是一种实现实时通信的有效方式。WebSocket 提供了一个在单个长时间运行的连接上进行全双工通信的渠道。这意味着服务器和客户端都可以在任何时候开始发送数据。


一、创建 ASP.NET Core 项目

    首先,你需要有一个 ASP.NET Core 项目。你可以使用命令行工具(如 dotnet CLI)来创建一个新的项目:

dotnet new web -n WebSocketDemo  
cd WebSocketDemo

二、配置中间件以支持 WebSocket

1.启动类Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        builder.Services.AddControllers();
        ConfigureServices(builder.Services);
        // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
        builder.Services.AddEndpointsApiExplorer();
        builder.Services.AddSwaggerGen();
        var app = builder.Build();var webSocketOptions = new WebSocketOptions
        {
            KeepAliveInterval = TimeSpan.FromSeconds(5)
        };
        //启动WebSockets
        app.UseWebSockets(webSocketOptions);
      
        app.UseAuthorization();
        app.MapControllers();
        app.Run();
    }public static void ConfigureServices(IServiceCollection services)
    {
        // 注册自定义服务  
        services.AddSingleton<WebSocketConnectionManager, WebSocketConnectionManager>();
        services.AddScoped<WebSocketHandler, WebSocketHandler>();
    }
}

    一定要添加app.UseWebSockets(webSocketOptions);,代表使用WebSocket协议。

2.WebSocket连接管理器

/// <summary>
/// WebSocket连接管理器
/// </summary>
public class WebSocketConnectionManager
{#region Constructorpublic WebSocketConnectionManager() 
    {
        _sockets = new ConcurrentDictionary<string, WebSocket>();
        _clientSendCommands = new ConcurrentDictionary<string, ConcurrentBag<string>>();
    }#endregion#region Property/// <summary>
    /// WebSocket连接池
    /// </summary>
    private readonly ConcurrentDictionary<string, WebSocket> _sockets;/// <summary>
    /// 客户端发送的命令池
    /// </summary>
    private readonly ConcurrentDictionary<string, ConcurrentBag<string>> _clientSendCommands;#endregion/// <summary>
    /// 添加WebSocket连接
    /// </summary>
    /// <param name="webSocket"></param>
    /// <returns></returns>
    public string AddSocket(string id,WebSocket webSocket) 
    {
        var socketId = id;
        if(string.IsNullOrWhiteSpace(id))
            socketId = Guid.NewGuid().ToString();
        if (_sockets.ContainsKey(id))
            _sockets.Remove(id,out _);
        _sockets.TryAdd(socketId, webSocket);
        return socketId;
    }/// <summary>
    /// 获取WebSocket
    /// </summary>
    /// <param name="socketId"></param>
    /// <returns></returns>
    public WebSocket? GetSocket(string socketId)
    {
        _sockets.TryGetValue(socketId, out var socket);
        return socket;
    }/// <summary>
    /// 根据Id 获取WebSocket
    /// </summary>
    /// <param name="socket"></param>
    /// <returns></returns>
    public string GetSocketId(WebSocket socket)
    {
        foreach (var (key, value) in _sockets)
        {
            if (value == socket)
            {
                return key;
            }
        }
        return string.Empty;
    }/// <summary>
    /// 根据Id移除WebSocket
    /// </summary>
    /// <param name="socketId"></param>
    public void RemoveSocket(string socketId)
    {
        _sockets.TryRemove(socketId, out _);
    }/// <summary>
    /// 移除WebSocket
    /// </summary>
    /// <param name="socket"></param>
    public void RemoveSocket(WebSocket socket)
    {
        var socketId = GetSocketId(socket);
        if(!string.IsNullOrWhiteSpace(socketId))
        {
            RemoveSocket(socketId);
        }
    }/// <summary>
    /// 获取所有的WebSocket
    /// </summary>
    /// <returns></returns>
    public List<WebSocket> GetAllSockets() 
    {
        var webSocketList = new List<WebSocket>();
        var keys = _sockets.Keys;
        foreach (var key in keys)
        {
            var socket = _sockets[key];
            webSocketList.Add(socket);
        }
        return webSocketList;
    }}

3.WebSocket事件管理器

public class WebSocketHandler
{#region Constructorpublic WebSocketHandler(
        WebSocketConnectionManager connectionManager,
        ILogger<WebSocketHandler> logger
) 
    {
        _connectionManager = connectionManager;
        _logger = logger;
    }#endregion#region Propertyprivate readonly WebSocketConnectionManager _connectionManager;
    private readonly ILogger<WebSocketHandler> _logger;#endregion/// <summary>
    /// 处理WebSocket
    /// </summary>
    /// <param name="context"></param>
    /// <param name="webSocket"></param>
    /// <param name="id"></param>
    /// <returns></returns>
    public async Task HandleWebSocket(HttpContext context, WebSocket webSocket,string id)
    {
        var socketId = _connectionManager.AddSocket(id,webSocket);
​
        _logger.LogInformation($"WebSocket connection established with ID {socketId}");
        try
        {
            while (webSocket.State == WebSocketState.Open)
            {
                var message = await ReceiveMessageAsync(webSocket);
                if (message != null)
                {
                    _logger.LogInformation($"Received message from ID {socketId}: {message}");
                    await BroadcastMessageAsync(socketId,message);
                }
            }
​
            _connectionManager.RemoveSocket(socketId);
            _logger.LogInformation($"WebSocket connection closed with ID {socketId}");
        }
        catch (WebSocketException ex)
        {
            _logger.LogError($"WebSocket error: {ex.Message}");
            _connectionManager.RemoveSocket(socketId);
        }
        catch (Exception ex)
        {
            // 处理其他可能的异常  
            _logger.LogError($"An error occurred: {ex.Message}");
            _connectionManager.RemoveSocket(socketId);
        }
    }private async Task<string?> ReceiveMessageAsync(WebSocket webSocket)
    {
        var buffer = new byte[1024];
        var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);if (result.CloseStatus.HasValue)
        {
            return null;
        }return Encoding.UTF8.GetString(buffer, 0, result.Count);
    }public async Task BroadcastMessageAsync(string socketId,string message)
    {
        var webSocket = _connectionManager.GetSocket(socketId);
        if (webSocket != null) 
        {
            if (webSocket.State == WebSocketState.Open)
            {
                await webSocket.SendAsync(Encoding.UTF8.GetBytes(message), WebSocketMessageType.Text, true, CancellationToken.None);
            }
            else if (webSocket.State == WebSocketState.Closed)
            {
                _connectionManager.RemoveSocket(socketId);
            }
        }
    }}

4.WebSocket连接入口

public class WebSocketController : ControllerBase
{#region Constructorpublic WebSocketController(
        ILogger<WebSocketController> logger,
        WebSocketConnectionManager webSocketConnectionManager,
        WebSocketHandler webSocketHandler)
    {
        _logger = logger;
        _webSocketConnectionManager = webSocketConnectionManager;
        _webSocketHandler = webSocketHandler;
    }#endregion#region Propertyprivate readonly WebSocketConnectionManager _webSocketConnectionManager;private readonly ILogger<WebSocketController> _logger;private readonly WebSocketHandler _webSocketHandler;#endregion[Route("/connect")]
    public async Task Get(string id)
    {
        if (HttpContext.WebSockets.IsWebSocketRequest)
        {
            var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
            await  _webSocketHandler.HandleWebSocket(HttpContext, webSocket, id);
        }
        else
        {
            HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
        }
    }[HttpGet("/send")]
    public async Task SendMessage(string id,string message) 
    {
        await _webSocketHandler.BroadcastMessageAsync(id,message);
    }
}

     以上添加了一个WebSocketConnectionManager 连接管理器,是用于管理所有连接的WebSocket,另外添加了WebSocketHandler事件处理器,用于发送和接收消息。

三、客户端实现

     在客户端,你可以使用 JavaScript 的 WebSocket API 来建立连接并发送/接收数据。

<!DOCTYPE html>  
<html>  
<body>  
  
<script>  
    var ws = new WebSocket("ws://localhost:5000/connect?id=123");  
  
    ws.onopen = function(event) {  
        console.log("Connection open ...");  
        ws.send("Hello Server!");  
    };  
  
    ws.onmessage = function(event) {  
        console.log("Received from server: " + event.data);  
    };  
  
    ws.onclose = function(event) {  
        console.log("Connection closed.");  
    };  
</script>  
  
</body>  
</html>

总结

效果:
1.连接服务器,创建WebSocket连接
在这里插入图片描述
在这里插入图片描述
2.发送消息
在这里插入图片描述
3.连接服务器端接收到消息
在这里插入图片描述

“笑对人生,智慧同行!博客新文出炉,微信订阅号更新更实时,等你笑纳~”
在这里插入图片描述

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

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

相关文章

什么是TCP三次握手和四次挥手,TCP协议详细解析!零基础入门到精通,收藏这一篇就够了

TCP是TCP/IP协议族中一个最核心的协议&#xff0c;它向下使用网络层IP协议&#xff0c;向上为应用层HTTP、FTP、SMTP、POP3、SSH、Telnet等协议提供支持。本文给出TCP报文格式的详细说明&#xff0c;介绍网络数据包传递中如何进行地址解析、建立TCP连接的三次握手过程以及断开T…

【docker】使用github action来自动发布项目到dockerhub

本文首发于 ❄️慕雪的寒舍 使用github action来自动发布项目到dockerhub。参考 https://msdemt.github.io/p/github-action-build-docker/ 博客 1.准备工作 1.1 dockerhub token https://hub.docker.com/settings/security 登录dockerhub&#xff0c;在用户的account settin…

【具体数学 Concrete Mathematics】1.1 递归问题 讲义

【具体数学 Concrete Mathematics】1.1 递归问题 导入 本节(1.1、1.1.1-1.1.3)主要围绕《具体数学》第一章 递归问题&#xff08;Recurrent Problems&#xff09;讲义部分的三个问题展开&#xff0c;分别是汉诺塔、平面上的直线以及约瑟夫问题。下面简单介绍一下递归问题和数学…

复数的处理

复数的处理 复数 V V V 定义为在 ( x , y ) (x,y) (x,y)-平面中实数对的有序集合。在这方面&#xff0c;复数可以被视为原点 ( 0 , 0 ) (0,0) (0,0) 上的向量。从这个角度看&#xff0c;复数的加法类似于 ( x , y ) (x,y) (x,y)-平面中向量的加法。 然而&#xff0c;乘法…

Vue3 国际化i18n语言库 网站多语言切换

介绍 在 Vue 3 项目中&#xff0c;国际化&#xff08;i18n&#xff09;是一个常见的需求&#xff0c;它允许你的应用支持多种语言&#xff0c;并根据用户的语言偏好显示相应的内容。为了实现国际化&#xff0c;你可以使用 vue-i18n 这个库&#xff0c;它是 Vue 官方推荐的国际…

vue常见**MS题 [2]

vue问题及理解 1、介绍一下vue2和vue3的区别 ‌Vue2和Vue3的主要区别体现在双向数据绑定原理、生命周期钩子函数、API、多根节点、性能和体积等方面。‌‌双向数据绑定原理‌&#xff1a;Vue2使用Object.defineProperty实现双向数据绑定&#xff0c;而Vue3则利用ES6的Proxy特性…

赚大钱和赚小钱,哪个更累?

最近一直在质疑我在做的项目&#xff0c;虽然有同行做到了很好的成绩&#xff0c;但是我还是质疑。 因为一直在赚小钱&#xff0c;接触到的也是新手、底层客户、墨迹客户。 越是钱少的生意&#xff0c;越不好做&#xff0c;客户越挑剔。 而且赚小钱会消磨人的心智。 前几年…

解决前端访问IIS服务器发生跨域请求报错的方法

现在WEB开发都是前后端分离的模式了&#xff0c;当前端代码访问后端WEB服务器时&#xff0c;经常会发生跨域请求报错的问题。   如果是IIS服务器&#xff0c;可以通过下面的方式轻松解决。   由于出现跨域问题是因为服务器返回的页面在返回头中没有设置“Access-Control-Al…

Servlet---Web会话跟踪 ▎token令牌

▍为什么要进行Web会话跟踪? http请求是无状态的,不携带用户信息的,当用户登录成功后,之后在于服务器交互时,服务器并不知道是哪个用户发送的请求 ▍Web会话跟踪 解决方法:在用户成功登录后,后端向前端响应token令牌(token令牌:用户信息),前端保存token令牌每次访问后端都先…

药店药品进销存管理系统药品出库药品入库药品销售-社区医院药品管理-基于JAVA+vue开发

2.2 业务流程分析 在进行业务流程分析时&#xff0c;需要按照原有信息流动过程&#xff0c;逐个地调查分析所有环节的处理业务、处理内容、处理顺序和对处理时间的要求&#xff0c;弄清各个环节需要的信息、信息来源、流经去向、处理方法、计算方法、提供信息的时间和信息形态…

Hadoop 中的大数据技术:Zookeeper安装 (2)

目录 下载地址 本地模式安装 1&#xff09;安装前准备 2&#xff09;配置修改 3&#xff09;操作 Zookeeper 配置参数解读 Zookeeper 集群操作 集群规划 解压安装 配置服务器编号 配置 zoo.cfg 文件 集群操作 Zookeeper 集群启动停止脚本 创建脚本 增加脚本执行权限 …

EmguCV学习笔记 C# 6.1 边缘检测

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

【云原生】MySQL的源码编译

1、实验环境 &#xff08;1&#xff09;虚拟机版本&#xff1a;RHEL7.9 &#xff08;2&#xff09;主机 主机名称IP地址mysql-node1172.25.254.10mysql-node2172.25.254.20 2、实验步骤 注意&#xff1a;我们的两台主机都要进行MySQL源码编译&#xff0c;并且操作相同&…

二手物品交易boot代码

TOC springboot548二手物品交易boot代码--论文pf 第1章 绪论 1.1 课题背景 二十一世纪互联网的出现&#xff0c;改变了几千年以来人们的生活&#xff0c;不仅仅是生活物资的丰富&#xff0c;还有精神层次的丰富。在互联网诞生之前&#xff0c;地域位置往往是人们思想上不可…

Leaflet+Leaflet-Geoman绘制天地图

安装所需依赖 yarn add leaflet geoman-io/leaflet-geoman-free文档地址 https://leafletjs.cn/ https://geoman.io/ <template><div id"map"></div><div class"handle"><button click"drawMap">绘制区块</b…

什么是生信分析?深入探讨生物信息学的技术、方法与广泛应用

介绍 生物信息学分析&#xff0c;简称生信分析&#xff0c;是一个结合了生物学、计算机科学、信息学和统计学的多学科领域&#xff0c;旨在处理、分析和解释海量的生物数据。随着现代生物技术的发展&#xff0c;尤其是高通量测序&#xff08;Next-Generation Sequencing, NGS&…

ArcGIS热点分析 (Getis-Ord Gi*)——七普地级市人口普查数据的热点与冷点分析

先了解什么是热点分析 ? 热点分析 (Getis-Ord Gi*) 是一种用于空间数据分析的技术&#xff0c;主要用于识别地理空间数据中值的聚集模式&#xff0c;可以帮助我们理解哪些区域存在高值或低值的聚集&#xff0c;这些聚集通常被称为“热点”或“冷点”&#xff0c;Gi* 统计量为…

LSI-9361阵列卡笔记

背景 要将raid0更改为JBOD直通模式 注意的点是要先将raid模式调整为JBOD之后重启机器&#xff0c;即可 备注&#xff1a;转换过程中硬盘中的数据未丢失。 步骤贴图 refer https://zhiliao.h3c.com/questions/dispcont/123250 https://blog.csdn.net/GreapFruit_J/article/…

Debian12安装jdk8环境

下载JDK8 下载页面&#xff1a;https://repo.huaweicloud.com:8443/artifactory/java-local/jdk/ 笔者下载的是8u202&#xff1a; #wget https://repo.huaweicloud.com:8443/artifactory/java-local/jdk/8u202-b08/jdk-8u202-linux-x64.tar.gz 解压安装 1、使用命令将压缩包复…

Golang | Leetcode Golang题解之第354题俄罗斯套娃信封问题

题目&#xff1a; 题解&#xff1a; func maxEnvelopes(envelopes [][]int) int {n : len(envelopes)if n 0 {return 0}sort.Slice(envelopes, func(i, j int) bool {a, b : envelopes[i], envelopes[j]return a[0] < b[0] || a[0] b[0] && a[1] > b[1]})f : …