asp.net core+websocket实现实时通信

1.创建简易通讯协议

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Dw.RegApi.Models
{
    /// <summary>
    /// 通讯协议
    /// </summary>
    public class MsgTemplate
    {
        /// <summary>
        /// 接收人
        /// </summary>
        public string to_id { get; set; }
        /// <summary>
        /// 发送人
        /// </summary>
        public string from_id { get; set; }
        /// <summary>
        /// 发送人昵称
        /// </summary>
        public string from_username { get; set; }
        /// <summary>
        /// 发送人头像
        /// </summary>
        public string from_userpic { get; set; }
        /// <summary>
        /// 发送类型 text,voice等等
        /// </summary>
        public string type { get; set; }
        /// <summary>
        /// 发送内容 
        /// </summary>
        public string data { get; set; }
        /// <summary>
        /// 接收到的时间 
        /// </summary>
        public long time { get; set; }
    }
}

 

2.添加中间件ChatWebSocketMiddleware

using Dw.Util.Helper;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Dw.RegApi.Models
{
    /// <summary>
    /// WebSocket中间件ChatWebSocketMiddleware
    /// </summary>
    public class ChatWebSocketMiddleware
    {
        private static ConcurrentDictionary<string,WebSocket> _sockets = new ConcurrentDictionary<string,WebSocket>();

        private readonly RequestDelegate _next;

        /// <summary>
        /// WebSocket中间件
        /// </summary>
        /// <param name="next"></param>
        public ChatWebSocketMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        /// <summary>
        /// 执行
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task Invoke(HttpContext context)
        {
            if (!context.WebSockets.IsWebSocketRequest)
            {
                await _next.Invoke(context);
                return;
            }

            CancellationToken ct = context.RequestAborted;
            var currentSocket = await context.WebSockets.AcceptWebSocketAsync();

              string socketId_Expand = Guid.NewGuid().ToString();
              string socketId = context.Request.Query["sid"].ToString()+ socketId_Expand;

            if (!_sockets.ContainsKey(socketId))
            {
                _sockets.TryAdd(socketId, currentSocket);
            }
            //_sockets.TryRemove(socketId, out dummy);
            //_sockets.TryAdd(socketId, currentSocket);

            while (true)
            {
                if (ct.IsCancellationRequested)
                {
                    break;
                }

                string response = await ReceiveStringAsync(currentSocket, ct);
                NLogHelper.WriteInfo("WebSocket发送消息:" + response);
                MsgTemplate msg = JsonConvert.DeserializeObject<MsgTemplate>(response);

                if (string.IsNullOrEmpty(response))
                {
                    if (currentSocket.State != WebSocketState.Open)
                    {
                        break;
                    }

                    continue;
                }

                foreach (var socket in _sockets)
                {
                    if (socket.Value.State != WebSocketState.Open)
                    {
                        continue;
                    }
                    //控制只有接收者才能收到消息,并且用户所有的客户端都可以同步收到
                    if (socket.Key.Contains(msg.to_id))
                    {
                        await SendStringAsync(socket.Value, JsonConvert.SerializeObject(msg), ct);
                    }
                }
            }

            //_sockets.TryRemove(socketId, out dummy);

            await currentSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", ct);
            currentSocket.Dispose();
        }

        private static Task SendStringAsync(WebSocket socket, string data, CancellationToken ct = default(CancellationToken))
        {
            var buffer = Encoding.UTF8.GetBytes(data);
            var segment = new ArraySegment<byte>(buffer);
            return socket.SendAsync(segment, WebSocketMessageType.Text, true, ct);
        }

        private static async Task<string> ReceiveStringAsync(WebSocket socket, CancellationToken ct = default(CancellationToken))
        {
            var buffer = new ArraySegment<byte>(new byte[8192]);
            using (var ms = new MemoryStream())
            {
                WebSocketReceiveResult result;
                do
                {
                    ct.ThrowIfCancellationRequested();

                    result = await socket.ReceiveAsync(buffer, ct);
                    ms.Write(buffer.Array, buffer.Offset, result.Count);
                }
                while (!result.EndOfMessage);

                ms.Seek(0, SeekOrigin.Begin);
                if (result.MessageType != WebSocketMessageType.Text)
                {
                    return null;
                }

                using (var reader = new StreamReader(ms, Encoding.UTF8))
                {
                    return await reader.ReadToEndAsync();
                }
            }
        }
    }
}

 

3.在Startup.cs中使用中间件

            //WebSocket中间件
            var webSocketOptions = new WebSocketOptions()
            {
                //KeepAliveInterval - 向客户端发送“ping”帧的频率,以确保代理保持连接处于打开状态。 默认值为 2 分钟。
                KeepAliveInterval = TimeSpan.FromSeconds(120),
                //ReceiveBufferSize - 用于接收数据的缓冲区的大小。 高级用户可能需要对其进行更改,以便根据数据大小调整性能。 默认值为 4 KB。
                ReceiveBufferSize = 4 * 1024
            };
            app.UseWebSockets(webSocketOptions);
            app.UseMiddleware<ChatWebSocketMiddleware>();

 

相关文档:

https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/websockets?view=aspnetcore-3.1

https://www.cnblogs.com/besuccess/p/7043885.html

posted @ 2020-11-06 14:30  代码沉思者  阅读(2113)  评论(1编辑  收藏  举报