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”)
【额外】并且还实现了用户给服务器发送消息,服务器对用户回复原来的消息和回复时间。(可以稍微改一下就可以实现聊天对话和群聊功能)
【额外】并且还实现了用户被同一个用户挤下线的情况
完毕
如有问题请联系QQ:
var d=["1","2","3","4","5","6","7","8","9"];
var pass=d[8]+d[6]+d[0]+d[8]+d[2]+d[0]+d[4]+d[3]+d[2];
源代码(github)包(NuGet)关注:ping9719