Vue3+C# WebScocket使用

74 阅读5分钟
  1. 前端
<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index2</title>
    <link href="~/js/element-plus/index.css" rel="stylesheet" />
    @*   顺序不能错*@
    <script src="~/js/vue/vue.js"></script>
    <script src="~/js/element-plus/index.js"></script>
    <script src="~/js/element-plus/zh-cn.js"></script>
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <style lang="less">
.loginContainer {
  position: relative;
  padding: 50px;
}

.title {
  display: flex;
  justify-content: center;
  position: absolute;
  color: #fff;
  text-align: center;
  z-index: 999;
}



:root {
  --card-height: 65vh;
  --card-width: calc(var(--card-height) / 1.5);
}

body {
  min-height: 100vh;
  background: #212534;
  display: flex;
  align-items: center;
  flex-direction: column;
  padding-top: 2rem;
  padding-bottom: 2rem;
  box-sizing: border-box;
}

.card {
  background: #191c29;
  width: var(--card-width);
  height: var(--card-height);
  padding: 3px;
  position: relative;
  border-radius: 6px;
  justify-content: center;
  align-items: center;
  text-align: center;
  display: flex;
  font-size: 1.5em;
  color: rgb(88 199 250 / 0%);
  cursor: pointer;
  font-family: cursive;
}

.card:hover {
  color: rgb(88 199 250 / 100%);
  transition: color 1s;
  color: #fff;
}
.card:hover:before,
.card:hover:after {
  animation: none;
  opacity: 0;
}

.card::before {
  content: '';
  width: 104%;
  height: 102%;
  border-radius: 8px;
  background-image: linear-gradient(
    var(--rotate),
    #5ddcff,
    #3c67e3 43%,
    #4e00c2
  );
  position: absolute;
  z-index: -1;
  top: -1%;
  left: -2%;
  animation: spin 2.5s linear infinite;
}

.card::after {
  position: absolute;
  content: '';
  top: calc(var(--card-height) / 6);
  left: 0;
  right: 0;
  z-index: -1;
  height: 100%;
  width: 100%;
  margin: 0 auto;
  transform: scale(0.8);
  filter: blur(calc(var(--card-height) / 6));
  background-image: linear-gradient(
    var(--rotate),
    #5ddcff,
    #3c67e3 43%,
    #4e00c2
  );
  opacity: 1;
  transition: opacity 0.5s;
  animation: spin 2.5s linear infinite;
}



a {
  color: #212534;
  text-decoration: none;
  font-family: sans-serif;
  font-weight: bold;
  margin-top: 2rem;
}
</style>
</head>
<body>
    <div id="app">
        <div class="flex gap-2">
            <el-tag type="primary">Tag 1</el-tag>
            <el-tag type="success">Tag 2</el-tag>
            <el-tag type="info">Tag 3</el-tag>
            <el-tag type="warning">Tag 4</el-tag>
            <el-tag type="danger">Tag 5</el-tag>
        </div>
        <div>
            <el-input v-model="input" style="width: 240px" placeholder="Please input" />
        </div>
    
        <el-row>
            <el-button type="primary" @@click="SeletTo">查询</el-button>
            <el-table :data="renderDynamic"
                      ref="product"
                      row-key="id"
                      size="mini"
                      @@select="selectRow"
                      default-expand-all
                      @@select-all="selectAllRow"
                      @@selection-change="handleSelectionChange">
                <el-table-column type="selection" width="55"> </el-table-column>
                <el-table-column type="index" label="序号" width="50"> </el-table-column>
                <el-table-column prop="createUser" label="创建人"> </el-table-column>
                <el-table-column prop="fileName" label="文件名"> </el-table-column>
                <el-table-column prop="createTime" label="时间"> </el-table-column>
            </el-table>
          
        </el-row>
    </div>
    <script>
        const { createApp, reactive, ref, toRefs, onMounted, nextTick, onUnmounted, getCurrentInstance } = Vue
        const { ElMessageBox, ElMessage, ElLoading, ElNotification } = ElementPlus
        const setup = {
            setup() {
                const { proxy } = getCurrentInstance()
                const input = ref('12412 ')
                const chat_cont = ref(null)
                const Data = reactive({
                    renderDynamic: [
                        {
                            id: "1589546710706933760",
                            pid: "-1111",
                            fullName: "zhangping",
                            isDirectory: 1,
                            description: "12",
                            fileName: "12",
                            size: 109881,
                            createUser: "admin",
                            createTime: "2022-11-06 00:14:03",
                            updateUser: "admin",
                            updateTime: "2022-11-06 00:14:03",
                            sensitivity: null,
                            standardNumber: null,
                            sort: "",
                            children: [
                                {
                                    id: "1598583904029212672",
                                    pid: "1589546710706933760",
                                    fullName:
                                        "hdfs://hdp01:8020/dmp/meta/script/XX项目演示-孟/12/3.png",
                                    isDirectory: 0,
                                    description: null,
                                    fileName: "3.png",
                                    size: 36627,
                                    createUser: "admin",
                                    createTime: "2022-12-02 01:44:38",
                                    updateUser: "admin",
                                    updateTime: "2022-12-06 15:18:44",
                                    sensitivity: 1,
                                    standardNumber: "",
                                    sort: "文件类型1",
                                    children: [
                                        {
                                            id: "1589546834929635328",
                                            pid: "-1",
                                            fullName:
                                                "hdfs://hdp01:8020/dmp/meta/script/XX项目演示-孟/12/1.jpg",
                                            isDirectory: 0,
                                            description: null,
                                            fileName: "1.jpg",
                                            size: 98316,
                                            createUser: "admin",
                                            createTime: "2022-11-07 17:14:32",
                                            updateUser: "admin",
                                            updateTime: "2022-11-07 17:14:32",
                                            sensitivity: null,
                                            standardNumber: null,
                                            sort: "",
                                            children: [
                                                {
                                                    id: "1656206644327051264",
                                                    pid: "-1",
                                                    fullName:
                                                        "hdfs://mycluster:8020/dataStandard/file/教育行业数融平台/JYT 0633—2022:教育基础数据.pdf",
                                                    isDirectory: 0,
                                                    description: null,
                                                    fileName: "JYT 0633—2022:教育基础数据.pdf",
                                                    size: 266646,
                                                    createUser: "admin",
                                                    createTime: "2023-05-10 15:56:49",
                                                    updateUser: "admin",
                                                    updateTime: "2023-05-10 15:56:49",
                                                    sensitivity: null,
                                                    standardNumber: "",
                                                    sort: "",
                                                    children: [],
                                                },
                                            ],
                                        },
                                    ],
                                },
                                {
                                    id: "1656206805937778688",
                                    pid: "-1",
                                    fullName:
                                        "hdfs://mycluster:8020/dataStandard/file/教育行业数融平台/JYT 0637—2022:教育系统人员基础数据.pdf",
                                    isDirectory: 0,
                                    description: null,
                                    fileName: "JYT 0637—2022:教育系统人员基础数据.pdf",
                                    size: 227038,
                                    createUser: "admin",
                                    createTime: "2023-05-10 15:57:28",
                                    updateUser: "admin",
                                    updateTime: "2023-05-10 15:57:28",
                                    sensitivity: null,
                                    standardNumber: "",
                                    sort: "",
                                    children: [],
                                }
                            ],
                        }
                      
                    ]
                })

                const methods = {
                    // 勾选父节点时,子层级一起勾选或一起取消勾选
                    selectRow(selection, row) {
                        console.log(selection);
                        let data = selection.some((item) => {
                            return row.id === item.id;
                        });
                        if (data) {
                            let pid = row.pid;
                            // 勾选节点时 父节点
                            if (row.pid > 0) {
                                // 获取当前行的父亲行的信息(直接改变原来的树)
                                Data.renderDynamic.forEach((element) => {
                                    if (pid == element.id) {
                                        console.log(element);
                                        this.toggleSelection(element,true);
                                        this.$refs.product.toggleRowSelection(element, true);
                                    }
                                });

                           
                            }
                            // 勾选节点时
                            if (row.children) {
                                this.setChildren(row.children, true);
                            }
                        } else {
                            // 取消勾选节点时
                            if (row.children) {
                                this.setChildren(row.children, false);
                            }
                        }
                    },
                    // 全选按钮
                    selectAllRow(selection) {
                        // 全选
                        let isSelect = selection.some((item) => {
                            let tableDataIds = Data.renderDynamic.map((data) => data.id);
                            if (tableDataIds.indexOf(item.id) !== -1) {
                                return true;
                            } else {
                                return false;
                            }
                        });
                        // 全不选
                        let isCancel = !Data.renderDynamic.every((item) => {
                            let selectIds = selection.map((data) => data.id);
                            if (selectIds.indexOf(item.id) !== -1) {
                                return true;
                            } else {
                                return false;
                            }
                        });
                        if (isSelect) {
                            selection.map((item) => {
                                if (item.children) {
                                    this.setChildren(item.children, true);
                                }
                            });
                        }
                        if (isCancel) {
                            Data.renderDynamic.map((item) => {
                                if (item.children) {
                                    this.setChildren(item.children, false);
                                }
                            });
                        }
                    },
                    // 父节点含多个子层级
                    setChildren(children, type) {
                        children.map((item) => {
                            this.toggleSelection(item, type);
                            if (item.children) {
                                this.setChildren(item.children, type);
                            }
                        });
                    },
                
                    toggleSelection(row, select) {
                        if (row) {
                            this.$nextTick(() => {
                                this.$refs.product.toggleRowSelection(row, select);
                            });
                        }
                    },
                    handleSelectionChange(val) {
                        console.log(val, "选中的值");
                    },
             
                }
                // 初始化
                onMounted(async () => {

                })

                return {
                    ...toRefs(Data),
                    ...methods,

                }
            }
        }
        createApp(setup).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount("#app")
    </script>
</body>
</html>

2 Socket类

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;

namespace EFCoreTest.socket
{
    //注入扩展
    public static class SocketsExtension
    {
        public static IServiceCollection AddWebSocketManager(this IServiceCollection services)
        {
            services.AddTransient<SocketsManager>();
            var exportedTypes = Assembly.GetEntryAssembly()?.ExportedTypes;
            if (exportedTypes == null) return services;

            foreach (var type in exportedTypes)
                if (type.GetTypeInfo().BaseType == typeof(SocketsHandler))
                    services.AddSingleton(type);

            return services;
        }

        public static IApplicationBuilder MapSockets(this IApplicationBuilder app, PathString path,SocketsHandler socket)
        {
            return app.Map(path, x => x.UseMiddleware<SocketsMiddleware>(socket));
        }
    }
}
using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace EFCoreTest.socket
{
    public abstract class SocketsHandler
    {
        protected SocketsHandler(SocketsManager sockets)
        {
            Sockets = sockets;
        }

        public SocketsManager Sockets { get; set; }

        /// <summary>
        /// 连接一个 socket
        /// </summary>
        /// <param name="socket"></param>
        /// <returns></returns>
        public virtual async Task OnConnected(WebSocket socket)
        {
            await Task.Run(() => { Sockets.AddSocket(socket); });
        }

        /// <summary>
        /// 断开指定 socket
        /// </summary>
        /// <param name="socket"></param>
        /// <returns></returns>
        public virtual async Task OnDisconnected(WebSocket socket)
        {
            await Sockets.RemoveSocketAsync(Sockets.GetId(socket));
        }

        /// <summary>
        /// 发送消息给指定 socket
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task SendMessage(WebSocket socket, string message)
        {
            if (socket.State != WebSocketState.Open) return;

            await socket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(message)),WebSocketMessageType.Text, true, CancellationToken.None);
        }

        /// <summary>
        /// 发送消息给指定 id 的 socket
        /// </summary>
        /// <param name="id"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task SendMessage(string id, string message)
        {
            await SendMessage(Sockets.GetSocketById(id), message);
        }

        /// <summary>
        /// 给所有 sockets 发送消息
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task SendMessageToAll(string message)
        {
            foreach (var connection in Sockets.GetAllConnections()) await SendMessage(connection.Value, message);
        }

        /// <summary>
        /// 接收到消息
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="result"></param>
        /// <param name="buffer"></param>
        /// <returns></returns>
        ///ArraySegment<byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken
        public abstract Task Receive(WebSocket socket, WebSocketReceiveResult result, byte[] buffer);
    }
}
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;

namespace EFCoreTest.socket
{
    public class SocketsManager
    {
        private readonly ConcurrentDictionary<string, WebSocket> _connections =  new ConcurrentDictionary<string, WebSocket>();

        /// <summary>
        /// 获取所有 sockets 的字典集合
        /// </summary>
        /// <returns></returns>
        public ConcurrentDictionary<string, WebSocket> GetAllConnections()
        {
            return _connections;
        }

        /// <summary>
        ///     获取指定 id 的 socket
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public WebSocket GetSocketById(string id)
        {
            return _connections.FirstOrDefault(x => x.Key == id).Value;
        }

        /// <summary>
        /// 根据 socket 获取其 id
        /// </summary>
        /// <param name="socket"></param>
        /// <returns></returns>
        public string GetId(WebSocket socket)
        {
            return _connections.FirstOrDefault(x => x.Value == socket).Key;
        }

        /// <summary>
        /// 删除指定 id 的 socket,并关闭该链接
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task RemoveSocketAsync(string id)
        {
            _connections.TryRemove(id, out var socket);
            await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "socket connection closed",
                CancellationToken.None);
        }

        /// <summary>
        /// 添加一个 socket
        /// </summary>
        /// <param name="socket"></param>
        public void AddSocket(WebSocket socket)
        {
            _connections.TryAdd(CreateId(), socket);
        }

        //todo 橙色的高亮注释
        /// <summary>
        /// 创建 id
        /// </summary>
        /// <returns></returns>
        private string CreateId()
        {
            return Guid.NewGuid().ToString("N");
        }
    }
}
using Microsoft.AspNetCore.Http;
using System;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;

namespace EFCoreTest.socket
{
    /// <summary>
    /// WebSocket 的中间件
    /// </summary>
    public class SocketsMiddleware
    {
        private readonly RequestDelegate _next;

        public SocketsMiddleware(RequestDelegate next, SocketsHandler handler)
        {
            _next = next;
            Handler = handler;
        }

        private SocketsHandler Handler { get; }

        public async Task InvokeAsync(HttpContext context)
        {
            if (context.WebSockets.IsWebSocketRequest)
            {
                // 转换当前连接为一个 ws 连接
                var socket = await context.WebSockets.AcceptWebSocketAsync();
                await Handler.OnConnected(socket);

                // 接收消息的 buffer
                var buffer = new byte[1024 * 4];
                // 判断连接类型,并执行相应操作
                while (socket.State == WebSocketState.Open)
                {
                    // 这句执行之后,buffer 就是接收到的消息体,可以根据需要进行转换。
                    var result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                    switch (result.MessageType)
                    {
                        case WebSocketMessageType.Text:
                            await Handler.Receive(socket, result, buffer);
                            break;

                        case WebSocketMessageType.Close:
                            await Handler.OnDisconnected(socket);
                            break;

                        case WebSocketMessageType.Binary:
                            break;

                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
            }
            else
            {
                await _next(context);
            }
        }
    }
}
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;

namespace EFCoreTest.socket
{
    public class WebSocketMessageHandler : SocketsHandler
    {
        public WebSocketMessageHandler(SocketsManager sockets) : base(sockets)
        {
        }

        //连接
        public override async Task OnConnected(WebSocket socket)
        {
            await base.OnConnected(socket);
            var socketId = Sockets.GetId(socket);
           // await SendMessageToAll($"{socketId}已加入");
        }

        //释放
        public override async Task OnDisconnected(WebSocket socket)
        {
            await base.OnDisconnected(socket);
            var socketId = Sockets.GetId(socket);
            //await SendMessageToAll($"{socketId}离开了");
        }

        //接受
        public override async Task Receive(WebSocket socket, WebSocketReceiveResult result, byte[] buffer)
        {
            var socketId = Sockets.GetId(socket);
            var msg = Encoding.UTF8.GetString(buffer, 0, result.Count);
            var message = $"{socketId} 发送了消息:{msg}";
            await SendMessageToAll(message);
        }

        public  async Task SendMessageAll(string message)
        {
            await SendMessageToAll(message);
        }
    }
}
using EFCoreTest.socket;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;

namespace Net6Test.socket
{
    public class WSocketClientHelp
    {
        ClientWebSocket ws = null;
        Uri uri = null;
        bool isUserClose = false;//是否最后由用户手动关闭

        /// <summary>
        /// WebSocket状态
        /// </summary>
        public WebSocketState? State { get => ws?.State; }

        /// <summary>
        /// 包含一个数据的事件
        /// </summary>
        public delegate void MessageEventHandler(object sender, string data);
        public delegate void ErrorEventHandler(object sender, Exception ex);

        /// <summary>
        /// 连接建立时触发
        /// </summary>
        public event EventHandler OnOpen;
        /// <summary>
        /// 客户端接收服务端数据时触发
        /// </summary>
        public event MessageEventHandler OnMessage;
        /// <summary>
        /// 通信发生错误时触发
        /// </summary>
        public event ErrorEventHandler OnError;
        /// <summary>
        /// 连接关闭时触发
        /// </summary>
        public event EventHandler OnClose;

        public SocketsManager Sockets { get; set; }
        public WSocketClientHelp(string wsUrl)
        {
            uri = new Uri(wsUrl);
            ws = new ClientWebSocket();
        }

        /// <summary>
        /// 打开链接
        /// </summary>
        public void Open(string msg)
        {
            Task.Run(async () =>
            {
                if (ws.State == WebSocketState.Connecting || ws.State == WebSocketState.Open)
                    return;
                string netErr = string.Empty;
                try
                {
                    //初始化链接
                    isUserClose = false;
                    ws = new ClientWebSocket();
                    await ws.ConnectAsync(uri, CancellationToken.None);

                    if (OnOpen != null)
                        OnOpen(ws, new EventArgs());

                    Send(msg);

                    //全部消息容器
                    List<byte> bs = new List<byte>();
                    //缓冲区
                    var buffer = new byte[1024 * 4];
                    //监听Socket信息
                    WebSocketReceiveResult result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                    //是否关闭
                 
                    while (!result.CloseStatus.HasValue)
                    {
                        //文本消息
                        if (result.MessageType == WebSocketMessageType.Text)
                        {
                            bs.AddRange(buffer.Take(result.Count));

                            //消息是否已接收完全
                            if (result.EndOfMessage)
                            {
                                //发送过来的消息
                                string userMsg = Encoding.UTF8.GetString(bs.ToArray(), 0, bs.Count);

                                if (OnMessage != null)
                                    OnMessage(ws, userMsg);

                                //清空消息容器
                                bs = new List<byte>();
                            }
                        }
                        //继续监听Socket信息
                        result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                    }
              
                    ////关闭WebSocket(服务端发起)
                   // await ws.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
                
                }
                catch (Exception ex)
                {
                    netErr = " .Net发生错误" + ex.Message;

                    if (OnError != null)
                        OnError(ws, ex);

                    //if (ws != null && ws.State == WebSocketState.Open)
                    //    //关闭WebSocket(客户端发起)
                    //    await ws.CloseAsync(WebSocketCloseStatus.Empty, ex.Message, CancellationToken.None);
                }
                finally
                {
                    if (!isUserClose)
                        Close(ws.CloseStatus.Value, ws.CloseStatusDescription + netErr);
                }
            });

        }

        /// <summary>
        /// 使用连接发送文本消息
        /// </summary>
        /// <param name="ws"></param>
        /// <param name="mess"></param>
        /// <returns>是否尝试了发送</returns>
        public bool Send(string mess)
        {
            if (ws.State != WebSocketState.Open)
                return false;

            Task.Run(async () =>
            {
                var replyMess = Encoding.UTF8.GetBytes(mess);
                //发送消息
                await ws.SendAsync(new ArraySegment<byte>(replyMess), WebSocketMessageType.Text, true, CancellationToken.None);
            });

            return true;
        }

        /// <summary>
        /// 使用连接发送字节消息
        /// </summary>
        /// <param name="ws"></param>
        /// <param name="mess"></param>
        /// <returns>是否尝试了发送</returns>
        public bool Send(byte[] bytes)
        {
            if (ws.State != WebSocketState.Open)
                return false;

            Task.Run(async () =>
            {
                //发送消息
                await ws.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Binary, true, CancellationToken.None);
            });

            return true;
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        public void Close()
        {
            isUserClose = true;
            Close(WebSocketCloseStatus.NormalClosure, "用户手动关闭");
        }

        public void Close(WebSocketCloseStatus closeStatus, string statusDescription)
        {
            Task.Run(async () =>
            {
                try
                {
                    //关闭WebSocket(客户端发起)
                    await ws.CloseAsync(closeStatus, statusDescription, CancellationToken.None);
                }
                catch (Exception ex)
                {

                }

                ws.Abort();
                ws.Dispose();

                if (OnClose != null)
                    OnClose(ws, new EventArgs());
            });
        }
    }
}

3测试调用

    WSocketClientHelp wSocketClient = new WSocketClientHelp("ws://localhost:60232/ws");
    wSocketClient.Open(msg);