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

浙公网安备 33010602011771号