Net Core Web 使用 WebSocket 实现监控在线人数

Net Core Web 使用 WebSocket 实现链接人数

Net Core Web 使用 WebSocket 实现监控人数

 

点我,跳转到最后看实现效果

 

在 Startup.cs  文件中的方法 Configure() 中加入如下中间件(尽量往前面放,尽量在 app.UseCors()的后面(如果有的话)):

            app.UseWebSockets();
            app.Use(async (context, next) =>
            {
                //是否为WebSocket请求
                if (context.WebSockets.IsWebSocketRequest)
                {
                    using (IServiceScope scope = app.ApplicationServices.CreateScope())
                    {
                        //执行...
                        WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
                        await new WebSocketFilter().Echo(context, webSocket);
                    }
                }
                else
                {
                    //下一个中间件...
                    await next();
                }
            });

  

创建一个文件 WebSocketFilter.cs 拷贝如下代码:

 

using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace YGNT.DtSchoolAdmin.Filter
{
    public class WebSocketFilter
    {
        //用户连接池
        private static Dictionary<string, WebSocket> UserPool = new Dictionary<string, WebSocket>();
        //监控连接池(全部)
        private static Dictionary<string, WebSocket> MonitoringPool1 = new Dictionary<string, WebSocket>();

        /// <summary>
        /// webSocket的处理
        /// </summary>
        public async Task Echo(HttpContext context, WebSocket webSocket)
        {
            try
            {
                //链接的唯一标识符
                var ConnectionId = context.Connection.Id;

                //用户={user_id}
                string user = context.Request.Query["user"];
                //监控={1全部,2机构,3企业}
                string monitoring = context.Request.Query["monitoring"];

                if (!string.IsNullOrWhiteSpace(user))
                {
                    //第一次open时,添加到连接池中
                    if (!UserPool.ContainsKey(user))
                    {
                        UserPool.Add(user, webSocket);

                        foreach (var item in MonitoringPool1)
                            SendTextAsync(item.Value, $"人数为{UserPool.Count}");
                    }
                    else
                    {
                        await UserPool[user].CloseAsync(WebSocketCloseStatus.NormalClosure, "此用户被占领,强制关闭", CancellationToken.None);
                        UserPool[user] = webSocket;
                    }
                }
                else if (!string.IsNullOrWhiteSpace(monitoring))
                {
                    if (monitoring == "1")
                    {
                        if (!MonitoringPool1.ContainsKey(ConnectionId))
                            MonitoringPool1.Add(ConnectionId, webSocket);

                        await SendTextAsync(webSocket, $"人数为{UserPool.Count}");
                    }
                }


                //全部消息容器
                List<byte> bs = new List<byte>();
                //缓冲区
                var buffer = new byte[1024 * 4];
                //监听Socket信息
                WebSocketReceiveResult result = await webSocket.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);
                            var reply = $"服务器收到你的信息为({userMsg})【{DateTime.Now}】";

                            await SendTextAsync(webSocket, reply);

                            //清空消息容器
                            bs = new List<byte>();
                        }
                    }

                    //继续监听Socket信息
                    result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                }
                //关闭WebSocket(客户端发起)
                await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);

                if (!string.IsNullOrWhiteSpace(user))
                    if (UserPool.ContainsKey(user))
                        UserPool.Remove(user);
                if (!string.IsNullOrWhiteSpace(ConnectionId))
                    if (MonitoringPool1.ContainsKey(ConnectionId))
                        MonitoringPool1.Remove(ConnectionId);

                foreach (var item in MonitoringPool1)
                    SendTextAsync(item.Value, $"人数为{UserPool.Count}");
            }
            catch (Exception ex)
            {
                await SendTextAsync(webSocket, $"服务器发生错误,正在关闭WebSocket。错误堆载为【{ex}】");
                //关闭WebSocket(服务端发起)
                await webSocket.CloseAsync(WebSocketCloseStatus.InternalServerError, ex.Message, CancellationToken.None);
            }
        }

        /// <summary>
        /// 发送文本消息
        /// </summary>
        /// <param name="webSocket"></param>
        /// <param name="mess"></param>
        /// <returns></returns>
        private static async Task SendTextAsync(WebSocket webSocket, string mess)
        {
            var replyMess = Encoding.UTF8.GetBytes(mess);

            if (webSocket != null && webSocket.State == WebSocketState.Open)
                //发送消息
                await webSocket.SendAsync(new ArraySegment<byte>(replyMess), WebSocketMessageType.Text, true, CancellationToken.None);
        }

    }
}

  

在 Startup.cs  文件中的方法 Configure() 中加入如下中间件(可以放在 app.UseWebSockets() 的前面):

 

app.UseStaticFiles();

  

在web项目中创建文件夹 wwwroot  (如果没有的话)(注:vs会自动改变样式),并在下面创建一个文件名为WebSocket.html 的文件,如图:

 

 

并将如下代码拷贝在 WebSocket.html 文件中:

 

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>WebSocket示例应用程序</title>
    <style>
        body {
            background-color: #DDDDDD;
        }

        table {
            border: 0
        }

        .connection-status {
            color: orangered;
        }

        .connection-status2 {
            color: blueviolet;
        }

        .commslog-data {
            font-family: Consolas, Courier New, Courier, monospace;
        }

        .commslog-server {
            background-color: red;
            color: white
        }

        .commslog-client {
            background-color: green;
            color: white
        }
    </style>
</head>
<body>
    <h1>WebSocket示例应用程序</h1>

    <p>
        当前状态:
        <label id="stateLabel" class="connection-status">准备连接</label>
    </p>

    <div>
        <label>可用参数:</label>
        <label class="connection-status2">?user=1&monitoring=1</label>
    </div>
    <div>
        <label for="connectionUrl">WebSocket服务器URL:</label>
        <input id="connectionUrl" />
        <button id="connectButton" type="submit">连接</button>
    </div>
    <br />
    <div>
        <label for="sendMessage">发送消息:</label>
        <input id="sendMessage" disabled />
        <button id="sendButton" type="submit" disabled>发送</button>
        <button id="closeButton" disabled>关闭WebSocket</button>
        <button id="emptyButton">清空消息</button>
    </div>

    <h2>通信记录</h2>
    <table style="width: 800px">
        <thead>
            <tr>
                <td style="width: 100px">发送者</td>
                <td style="width: 100px">接收者</td>
                <td>消息</td>
            </tr>
        </thead>
        <tbody id="commsLog">
        </tbody>
    </table>

    <script>
        var connectionUrl = document.getElementById("connectionUrl");
        var connectButton = document.getElementById("connectButton");
        var stateLabel = document.getElementById("stateLabel");
        var sendMessage = document.getElementById("sendMessage");
        var sendButton = document.getElementById("sendButton");
        var commsLog = document.getElementById("commsLog");
        var closeButton = document.getElementById("closeButton");
        var emptyButton = document.getElementById("emptyButton");
        var socket;

        var scheme = document.location.protocol === "https:" ? "wss" : "ws";
        var port = document.location.port ? (":" + document.location.port) : "";

        //connectionUrl.value = scheme + "://" + document.location.hostname + port + "/ws";
        connectionUrl.value = scheme + "://" + document.location.hostname + port;

        function updateState() {
            function disable() {
                sendMessage.disabled = true;
                sendButton.disabled = true;
                closeButton.disabled = true;
            }
            function enable() {
                sendMessage.disabled = false;
                sendButton.disabled = false;
                closeButton.disabled = false;
            }

            connectionUrl.disabled = true;
            connectButton.disabled = true;

            if (!socket) {
                disable();
            } else {
                switch (socket.readyState) {
                    case WebSocket.CLOSED:
                        stateLabel.innerHTML = "关闭";
                        disable();
                        connectionUrl.disabled = false;
                        connectButton.disabled = false;
                        break;
                    case WebSocket.CLOSING:
                        stateLabel.innerHTML = "关闭中...";
                        disable();
                        break;
                    case WebSocket.CONNECTING:
                        stateLabel.innerHTML = "连接中...";
                        disable();
                        break;
                    case WebSocket.OPEN:
                        stateLabel.innerHTML = "打开";
                        enable();
                        break;
                    default:
                        stateLabel.innerHTML = "未知的WebSocket状态: " + htmlEscape(socket.readyState);
                        disable();
                        break;
                }
            }
        }

        closeButton.onclick = function () {
            if (!socket || socket.readyState !== WebSocket.OPEN) {
                alert("没有连接Socket");
            }
            socket.close(1000, "从客户端关闭");
        };
        emptyButton.onclick = function () {
            commsLog.innerHTML = ""
        };

        sendButton.onclick = function () {
            if (!socket || socket.readyState !== WebSocket.OPEN) {
                alert("没有连接Socket");
            }
            var data = sendMessage.value;
            socket.send(data);
            commsLog.innerHTML += '<tr>' +
                '<td class="commslog-client">客户端</td>' +
                '<td class="commslog-server">服务端</td>' +
                '<td class="commslog-data">' + htmlEscape(data) + '</td></tr>';
        };

        connectButton.onclick = function () {
            stateLabel.innerHTML = "连接中";
            socket = new WebSocket(connectionUrl.value);
            socket.onopen = function (event) {
                updateState();
                commsLog.innerHTML += '<tr>' +
                    '<td colspan="3" class="commslog-data">连接已打开</td>' +
                    '</tr>';
            };
            socket.onclose = function (event) {
                updateState();
                commsLog.innerHTML += '<tr>' +
                    '<td colspan="3" class="commslog-data">连接关闭了。错误码: ' + htmlEscape(event.code) + '。原因:' + htmlEscape(event.reason) + '</td>' +
                    '</tr>';
            };
            socket.onerror = updateState;
            socket.onmessage = function (event) {
                commsLog.innerHTML += '<tr>' +
                    '<td class="commslog-server">服务端</td>' +
                    '<td class="commslog-client">客户端</td>' +
                    '<td class="commslog-data">' + htmlEscape(event.data) + '</td></tr>';
            };
        };

        function htmlEscape(str) {
            return str.toString()
                .replace(/&/g, '&')
                .replace(/"/g, '"')
                .replace(/'/g, ''')
                .replace(/</g, '<')
                .replace(/>/g, '>');
        }
    </script>
</body>
</html>

  

 

 

完成,运行项目,跳转到我们的测试页面(如我的为:https://localhost:5001/WebSocket.html):

 

 

 

 

我们链接一个观察者的身份:

 

 

 

 然后登陆一个用户A:(可以看到监控者收到了一条消息“人数为1”)

 

 

 

 然后登陆一个用户B:(可以看到监控者收到了一条消息“人数为2”)

 

 

 

 然后关闭用户A的浏览器:(可以看到监控者收到了一条消息“人数为1”) 

 

 

【额外】并且还实现了用户给服务器发送消息,服务器对用户回复原来的消息和回复时间。(可以稍微改一下就可以实现聊天对话和群聊功能)

 

 

【额外】并且还实现了用户被同一个用户挤下线的情况

 

 

完毕

 

posted @ 2020-08-28 16:03  爱恋的红尘  阅读(1943)  评论(0编辑  收藏  举报