.Net Core 项目使用WebSocket即时通讯技术跟前端实现消息互动

725 阅读2分钟

一共分为四步:

一、定于消息内容基类

namespace ReadJson_.Net6._0.WebSockets
{
    /// <summary>
    /// 消息基类模型
    /// </summary>
    public class MessageBase
    {
        /// <summary>
        /// socket唯一ID
        /// </summary>
        public string Id { get; set; }

        /// <summary>
        /// 消息类型
        /// </summary>
        public int Type { get; set; }

        /// <summary>
        /// 消息信息
        /// </summary>
        public object Message { get; set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        public MessageBase(int type, object message)
        {
            Type = type;
            Message = message;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        public MessageBase(string id, int type, object message)
        {
            Id = id;
            Type = type;
            Message = message;
        }

    }
}

二、定义发送消息、监听消息 辅助类

using Newtonsoft.Json;
using ReadJson_.Net6._0.Model;
using System.Net.WebSockets;
using System.Text;

namespace ReadJson_.Net6._0.WebSockets
{
    public class WebsocketHandlerMiddleware
    {
        /// <summary>
        /// 消息事件委托
        /// </summary>
        /// <param name="message"></param>
        public delegate void MessageEvent(MessageBase message);
        /// <summary>
        /// 消息事件集合
        /// </summary>
        private static readonly Dictionary<MessageType, List<MessageEvent>> dic = new Dictionary<MessageType, List<MessageEvent>>();
        //存储所有的websocket连接
        public static Dictionary<string, WebSocket> keyValues = new Dictionary<string, WebSocket>();
        //读取管道配置
        private readonly RequestDelegate _next;

        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="next"></param>
        public WebsocketHandlerMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        /// <summary>
        /// 项目启动初始化
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task Invoke(HttpContext context)
        {
            string ip = context.Request.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
            //仅当网页执行new WebSocket("ws://自己电脑ip:5068/ws")时,后台会执行此逻辑
            if (context.WebSockets.IsWebSocketRequest)
            {
                //后台成功接收到连接请求并建立连接后,前台的webSocket.onopen = function (event){}才执行
                WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
                try
                {
                    #region 
                    //最大接收包 4M
                    byte[] bufferReceive = new byte[1024 * 4];
                    WebSocketReceiveResult clientData = await webSocket.ReceiveAsync(new ArraySegment<byte>(bufferReceive), CancellationToken.None);
                    //接收Socket消息
                    if (clientData.MessageType == WebSocketMessageType.Text)
                    {
                        //解读收到的消息
                        byte[] buffer = new byte[clientData.Count];
                        Array.Copy(bufferReceive, 0, buffer, 0, buffer.Length);
                        string msgString = Encoding.UTF8.GetString(buffer);
                        MessageBase message = JsonConvert.DeserializeObject<MessageBase>(msgString);
                        message.Id = Guid.NewGuid().ToString();
                        message.Message = "我是回应啊";
                        //判断消息添加消息连接
                        if (keyValues.ContainsKey(message.Id))
                        {
                            keyValues[message.Id] = webSocket;
                        }
                        else
                        {
                            keyValues.Add(message.Id, webSocket);
                        }
                        //发送消息
                        if (webSocket != null && webSocket.State == WebSocketState.Open)
                        {
                            string jsonStr = JsonConvert.SerializeObject(message);
                            byte[] _byte = Encoding.UTF8.GetBytes(jsonStr);
                            webSocket.SendAsync(_byte, WebSocketMessageType.Text, true, CancellationToken.None);
                        }
                        //开启一个此消息的线程
                        //作用:一直等待接收前端发送过来的消息,直到接收到前端发送的关闭消息连接,此线程结束
                        await Handle(webSocket, message.Id, ip);
                    }
                    #endregion
                }
                catch (Exception ex)
                {
                    //添加消息报错
                    //返给前端 关闭消息连接
                    ex.ToString();
                    MessageBase mb = new MessageBase
                    {
                        Id = "-1",
                        Type = MessageType.Closed,
                        Message = "closed"
                    };
                    string jsonStr = JsonConvert.SerializeObject(mb);
                    byte[] _byte = Encoding.UTF8.GetBytes(jsonStr);
                    await webSocket.SendAsync(_byte, WebSocketMessageType.Text, true, CancellationToken.None);
                    await context.Response.WriteAsync("closed");
                }
            }
            else
            {
                await _next(context);
            }
        }

        /// <summary>
        /// 循环监控事件
        /// </summary>
        /// <param name="webSocket"></param>
        /// <param name="uid"></param>
        /// <param name="ip"></param>
        /// <returns></returns>
        private async Task Handle(WebSocket webSocket, string uid, string ip)
        {
            WebSocketReceiveResult clientData;
            int id = 1;
            do
            {
                //最大接收包 4M
                byte[] bufferReceive = new byte[1024 * 4];
                //在连接关闭之前 一直在这等待 前端给发消息 从而进行回复
                clientData = await webSocket.ReceiveAsync(new ArraySegment<byte>(bufferReceive), CancellationToken.None);
                if (clientData.CloseStatus != null && !clientData.CloseStatus.HasValue)
                {//WebSocket连接关闭
                    break;
                }

                //接收Socket消息
                if (clientData.MessageType == WebSocketMessageType.Text)
                {
                    byte[] buffer = new byte[clientData.Count];
                    Array.Copy(bufferReceive, 0, buffer, 0, buffer.Length);

                    string msgString = Encoding.UTF8.GetString(buffer);
                    MessageBase message = JsonConvert.DeserializeObject<MessageBase>(msgString);
                    message.Id = uid;
                    message.Message = "我是回应" + id;
                    HandleMessage(message);
                }
                else if (clientData.MessageType == WebSocketMessageType.Binary)
                {
                    byte[] allData = new byte[clientData.Count];
                    Array.Copy(bufferReceive, 0, allData, 0, allData.Length);
                    MessageBase message = new MessageBase()
                    {
                        Id = uid,
                        Type = MessageType.Monitor,
                        Message = new MemoryStream(allData)
                    };
                    HandleMessage(message);
                }
            } while (!clientData.CloseStatus.HasValue);

            //WebSocket连接关闭,关闭接受消息并移除连接
            WebsocketHandlerMiddleware.Remove(uid);
        }

        /// <summary>
        /// 事件派发
        /// </summary>
        /// <param name="message"></param>
        private void HandleMessage(MessageBase message)
        {
            if (!dic.ContainsKey(message.Type))
                return;

            foreach (MessageEvent messageEvent in dic[message.Type])
            {
                messageEvent(message);
            }
        }



        /// <summary>
        /// 删除连接
        /// </summary>
        /// <param name="Uid"></param>
        /// <param name="stEnum"></param>
        public static void Remove(string Uid)
        {
            if (keyValues.ContainsKey(Uid))
            {
                keyValues[Uid].Abort();
            }
            keyValues.Remove(Uid);
        }
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="message"></param>
        public static void SendMsg(MessageBase message)
        {
            foreach (var item in keyValues)
            {
                string jsonStr = JsonConvert.SerializeObject(message);
                byte[] _byte = Encoding.UTF8.GetBytes(jsonStr);
                item.Value.SendAsync(_byte, WebSocketMessageType.Text, true, CancellationToken.None);
            }
        } 
    }
}

三、在管道中配置WebSocket消息

image.png

四、在前端启动WebSocket

我这用的是vue3的代码结构

<script setup lang="ts">
 
 let ws = new WebSocket("ws://localhost:8080");
 
//给后端发送消息 开启连接
ws.onopen = function (e) {
    console.log("open:", e)
     var date ={
    Id : "111",
    Type : 2,
    Message : "开启连接"
 }
 console.log(ws)
    ws.send(JSON.stringify(date));
};
//监听后端发送过来的消息
ws.onmessage = function (e) {

    var date ={
    Id : "222",
    Type : 2,
    Message : "消息"
 }
 console.log(e.data);
 
        //处理消息,在页面展示内容(自由扩展)

    ws.send(JSON.stringify(date));//发送消息给后台
};
//给后端发消息连接异常
ws.onerror = function (e) {
    alert("网络连接错误!");
};
// 给后端发消息关闭连接
ws.onclose = function (e) {
    alert("服务器断开!");
};

</script>

如下就是我webSocket连接成功,监听到的消息内容了,打印了一下 image.png

备注: 这是我在接口中调用的发送消息方法 给前端发消息,大家也可以参考一下 image.png

WebSocket 的使用方法不止于此,我这只是自己对他的了解进行了一下封装,大家可以参考一下!!!