首页 > 其他分享 >.Net 简单实现WebSocket通讯

.Net 简单实现WebSocket通讯

时间:2024-08-08 18:17:28浏览次数:12  
标签:通讯 WebSocket string await userId new Net public

使用框架:.net 8、winform

操作系统:Windows 11

编译器:vs 2022

内容:实现一个多客户端单服务端的WebSocket通讯,并可发布广播消息

本文使用.net框架自带的WebSocket WebSocketClient服务类进行演示,实现一个简单的通讯。

服务端

服务端所要做的事情就是创建一个WebSocket服务器,并在有请求时进行响应。由于我们是要实现一个多客户端可广播的的通讯,而每个 WebSocket 连接都是独立处理的,若要实现广播,则需要维护一个连接列表,并在接收到消息时遍历这个列表来发送消息。

Storage类


public interface IWebSocketStorage
{
    public ConcurrentDictionary<string, List<WebSocketObj>> WebSocketStorageList { get; }
    Task<WebSocket> GetWebSocket(string UserId);
    WebSocketObj AddWebSocketClient(string userId, WebSocket ws, string Name);
    void DisconnectWebSocket(string userId, WebSocketObj ws);
}

public class WebSocketStorage : IWebSocketStorage
{
    public WebSocketStorage()
    {
        WebSocketStorageList = new ConcurrentDictionary<string, List<WebSocketObj>>();
    }


    //WebSocket链接列表
    public ConcurrentDictionary<string, List<WebSocketObj>> WebSocketStorageList { get; }


    //将链接添加至列表
    public WebSocketObj AddWebSocketClient(string userId, WebSocket ws, string Name)
    {
        var wsObj = new WebSocketObj()
        {
            
            WebSocket = ws,
            ConnectionGuid = Guid.NewGuid(),
            Name = Name,
        };
        if (WebSocketStorageList.ContainsKey(userId))
        {
            WebSocketStorageList[userId].Add(wsObj);
        }
        else
        {
            WebSocketStorageList.TryAdd(userId, new List<WebSocketObj>() { wsObj });
        }

        return wsObj;
    }

    //断开链接
    public void DisconnectWebSocket(string userId, WebSocketObj ws)
    {
        if (WebSocketStorageList.TryGetValue(userId, out List<WebSocketObj> value))
        {
            value.Remove(ws);
        }
    }


    //获取WebSocket链接
    public async Task<WebSocket> GetWebSocket(string userId)
    {

        if (WebSocketStorageList.TryGetValue(userId, out List<WebSocketObj> value))
        {
            return value.FirstOrDefault().WebSocket;
        }

        return null;
    }
    
}

public class WebSocketObj
{
    [Newtonsoft.Json.JsonIgnore]
    public WebSocket WebSocket { get; set; }
    public Guid ConnectionGuid { get; set; }
    public string Name { get; set; }
}

中间件

public class WebsocketHandlerMiddleware : IMiddleware
{
    private readonly IWebSocketStorage _webSocketStorage;
    private readonly PathString wsPath = new PathString("/ws");

    public WebsocketHandlerMiddleware(IWebSocketStorage webSocketStorage)
    {
        _webSocketStorage = webSocketStorage;
    }

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        if (context.Request.Path.Equals(wsPath))
        {
            if (context.WebSockets.IsWebSocketRequest)
            {
                string name = "";
                string userId = "";    //客户端传来

                using WebSocket socket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false);
                WebSocketObj clientObj1 = new WebSocketObj();
                clientObj1 = _webSocketStorage.AddWebSocketClient(userInfo.UserId, socket, name);
                try
                {
                    while (socket.State != WebSocketState.Closed && socket.State != WebSocketState.Aborted)
                    {
                        byte[] buffer = new byte[1024 * 1];
                        WebSocketReceiveResult receiveResult = await socket.ReceiveAsync(buffer, context.RequestAborted).ConfigureAwait(false);

                        if (socket.State == WebSocketState.CloseReceived && receiveResult.MessageType == WebSocketMessageType.Close)
                        {
                            await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Acknowledge Close frame", context.RequestAborted).ConfigureAwait(false);
                        }

                        if (socket.State == WebSocketState.Open && receiveResult.MessageType == WebSocketMessageType.Text)
                        {
                            string msgStr = Encoding.UTF8.GetString(buffer).Trim();    //客户端发来的消息
                            byte[] msgBytes = Encoding.UTF8.GetBytes("消息");
                            socket.SendAsync(new ArraySegment<byte>(msgBytes), WebSocketMessageType.Text, true, CancellationToken.None);    //向客户端发送消息
                            var wsMessage = await _webSocketStorage.GetWebSocket("1", WebSocketType.Message);
                            }
                            
                    }
                }
                catch (Exception)
                {
                    //ignore
                }
                finally
                {
                    _webSocketStorage.DisconnectWebSocket(userInfo.UserId, clientObj1);
                    
                }
            }
            else
            {
                context.Response.StatusCode = 404;
            }
        }
        else
        {
            await next(context);
        }
    }
}

IOC

builder.Services.AddSingleton<IWebSocketStorage, WebSocketStorage>();
builder.Services.AddScoped<WebsocketHandlerMiddleware>();

如果想要广播消息,那么在该类库加上var wsStorage = App.GetService<IWebSocketStorage>();,遍历其中的WebSocketStorageList然后发消息即可。

客户端

客户端只需要连接WebSocket服务器,然后收发消息即可

public class Ws
{
    private ClientWebSocket webSocket = new ClientWebSocket();
    readonly ChromiumWebBrowser _browser;

    public async void ConnectToWebSocketServer()
    {
        try
        {
            Uri serverUri = new Uri("ws://你的WebSocket地址");
            await webSocket.ConnectAsync(serverUri, CancellationToken.None);

            await SendMessageAsync(userId);
            await ReceiveMessages();
        }
        catch (Exception ex)
        {
            MessageBox.Show($"无法连接到WebSocket服务器: {ex.Message}");
        }

    }

    public async Task SendMessageAsync(string message)
    {
        if (webSocket?.State == WebSocketState.Open)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(message);
            ArraySegment<byte> segment = new ArraySegment<byte>(bytes);

            await webSocket.SendAsync(segment, WebSocketMessageType.Text, true, CancellationToken.None);
        }
    }

    public async Task ReceiveMessages()
    {
        var buffer = new byte[1024 * 4];
        string message = string.Empty;
        while (webSocket.State == WebSocketState.Open)
        {
            buffer = new byte[1024 * 4];
            WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
            if (result.MessageType == WebSocketMessageType.Text)
            {
                message = Encoding.UTF8.GetString(buffer, 0, result.Count).Trim();
            }
            else if (result.MessageType == WebSocketMessageType.Close)
            {
                await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
                break;
            }
        }
    }

   public async Task Close()
    {
        if (webSocket != null && webSocket.State == WebSocketState.Open)
        {
            await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
        }
    }


}

总结

对于需要实时数据更新的场景,WebSocket通常是一个很好的选择。

当然,如果不考虑实时以及负载的话,长轮询其实也可以用来模拟长连接,具体情况具体分析吧大伙

标签:通讯,WebSocket,string,await,userId,new,Net,public
From: https://www.cnblogs.com/lenux/p/18349482

相关文章

  • 配置.NET环境
    前言面向对象编程语言里,有两朵金花:分别是Java和C#。C#的前身是J++,J++是Java在windows上的实现,但语法并不100%兼容Java,之后又经过J#的演变,再到后面的C#。Dotnet(.NET)是C#代码的编译和运行环境。1、下载.NETSDK8.0的最新一个更新包(免安装绿色解压版):windows:https://dotnet.mic......
  • 使用SixLabors.ImageSharp 进行图片的缩放C#.net core,可跨平台运行
    引用命令空间usingSixLabors.ImageSharp;using SixLabors.ImageSharp.Formats.Png;using SixLabors.ImageSharp.Processing; publicstaticvidResize(byte[]imageBytes,intwidth=400){usingvarsteam=newMemoryStream(imageBytes);using(varimage=......
  • PageOffice6国产Linux系统最简集成代码(.NetCore)
    本文描述了PageOffice产品在.NetCore项目中如何集成调用。PageOffice国产版:支持信创系统,支持银河麒麟V10和统信UOS,支持X86(intel、兆芯、海光等)、ARM(飞腾、鲲鹏、麒麟等)、longarch芯片架构。新建.NetCore项目:PageOffice6-Net-Core-Simple在此项目的“依赖项-包-管理NuGet程序......
  • VB.NET钢琴MIDI简谱播放器代码QZQ2024-8-7
    ImportsSystem.Runtime.InteropServicesPublicClassForm1'义WindowsAPI函数<DllImport(“winmm.dll”)>PrivateSharedFunctionmidiOutGetNumDevs()AsIntegerEndFunction<DllImport("winmm.dll")>PrivateSharedFunctionmidiOutGet......
  • 《Kubernetes企业级云原生运维实战》(李振良).pdf
    本书是一本实用性很强的Kubernetes运维实战指南,旨在为容器云平台的建设、应用和运维过程提供全面的指导。作者结合丰富的生产环境经验,深入探讨作为一名Kubernetes工程师必备的核心技能,包括部署、存储、网络、安全、日志、监控、CI/CD等方面的技术。本书结合大量的实际案例,......
  • kubernetes集群部署postgre 12.6数据库服务
    背景:因业务上线需要,研发中心要求在kubernetes测试集群部署一个postgre12.6的数据库,用于业务功能调试。一、实施部署postgre数据库: 1、拉取postgre12.6的镜像:[root@harbor-02~]#dockerpullregistry.cn-hangzhou.aliyuncs.com/images-speed-up/postgres:12.62017-l......
  • .Net5+的应用配置文件
    在VisualStudio项目中,”.exe.config”文件(也称为应用程序配置文件)和清单文件(manifestfile)是两个不同的文件类型,分别用于不同的目的。以下是它们的主要区别和用途:应用程序配置文件(”.exe.config”)1.目的应用程序配置文件用于存储应用程序的可配置设置,如数据库连接字符串、应......
  • kubernetes 核心实战 - 存储 - 3
    Kubernetes核心实战-存储介绍1.Kubernetes存储概念及其关系在Kubernetes中,存储的几个核心概念及其关系如下:Volume:在Pod内部使用的一块存储,生命周期与Pod一致。不同类型的Volumes提供不同的存储功能,例如emptyDir、hostPath、configMap等。PersistentV......
  • LangChain与Kubernetes的协奏曲:解锁容器编排的新境界
    LangChain与Kubernetes的协奏曲:解锁容器编排的新境界在云计算和微服务架构的浪潮中,Kubernetes已成为容器编排和管理的事实标准。LangChain,作为一个假设的编程辅助工具,如果存在,它可能会支持与Kubernetes集群的集成,以实现代码生成、部署和自动化管理的无缝对接。本文将探讨La......
  • kubernetes笔记-4-kubernetes资源管理
    一、、kubernetes资源分类:工作负载、发现与负载均衡、配置与存储、集群、和元数据1、工作负载型资源分为:有状态和无状态两种类型;无状态:每个pod均可被其它其他同类所取代;有状态:有其独特性,必须单独标识和管理;ReplicaSet、Deployment负责无状态应用管理;StatefulSet负责有状态应用管......