2024-10-10 10:45:53 +08:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
|
|
using Serein.Library.Attributes;
|
|
|
|
|
|
using Serein.Library.Network.WebSocketCommunication.Handle;
|
2024-10-07 15:15:18 +08:00
|
|
|
|
using System;
|
2024-10-15 21:56:09 +08:00
|
|
|
|
using System.Collections.Concurrent;
|
2024-10-10 10:45:53 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
2024-10-07 15:15:18 +08:00
|
|
|
|
using System.Net;
|
|
|
|
|
|
using System.Net.WebSockets;
|
2024-10-10 10:45:53 +08:00
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
using System.Security.Cryptography;
|
2024-10-07 15:15:18 +08:00
|
|
|
|
using System.Text;
|
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Serein.Library.Network.WebSocketCommunication
|
|
|
|
|
|
{
|
2024-10-15 21:56:09 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// WebSocket JSON 消息授权管理
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class WebSocketAuthorizedHelper
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// WebSocket JSON 消息授权管理
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public WebSocketAuthorizedHelper(string addresPort,string token, Func<dynamic, Task<bool>> inspectionAuthorizedFunc)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.AddresPort = addresPort;
|
|
|
|
|
|
this.TokenKey = token;
|
|
|
|
|
|
this.InspectionAuthorizedFunc = inspectionAuthorizedFunc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 客户端地址
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string AddresPort { get; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 是否已经鉴权
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool IsAuthorized { get => isAuthorized; } //set => isAuthorized = value;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 是否已经鉴权
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private bool isAuthorized;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 授权字段
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private readonly string TokenKey;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理消息授权事件
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private readonly Func<dynamic, Task<bool>> InspectionAuthorizedFunc;
|
|
|
|
|
|
|
|
|
|
|
|
private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理消息授权
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="message"></param>
|
|
|
|
|
|
public async Task HandleAuthorized(string message)
|
|
|
|
|
|
{
|
|
|
|
|
|
if(!isAuthorized && semaphoreSlim is null) // 需要重新授权
|
|
|
|
|
|
{
|
|
|
|
|
|
semaphoreSlim = new SemaphoreSlim(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
await semaphoreSlim.WaitAsync(1);
|
|
|
|
|
|
if(isAuthorized) // 授权通过,无须再次检查授权
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
JObject json = JObject.Parse(message);
|
|
|
|
|
|
if(json.TryGetValue(TokenKey,out var token))
|
|
|
|
|
|
{
|
|
|
|
|
|
// 交给之前定义的授权方法进行判断
|
|
|
|
|
|
isAuthorized = await InspectionAuthorizedFunc?.Invoke(token);
|
|
|
|
|
|
if (isAuthorized)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 授权通过,释放资源
|
|
|
|
|
|
semaphoreSlim.Release();
|
|
|
|
|
|
semaphoreSlim.Dispose();
|
|
|
|
|
|
semaphoreSlim = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
isAuthorized = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-15 10:55:41 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// WebSocket服务类
|
|
|
|
|
|
/// </summary>
|
2024-10-10 20:52:19 +08:00
|
|
|
|
[AutoRegister]
|
2024-10-07 15:15:18 +08:00
|
|
|
|
public class WebSocketServer
|
|
|
|
|
|
{
|
2024-10-15 10:55:41 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 消息处理
|
|
|
|
|
|
/// </summary>
|
2024-10-10 20:52:19 +08:00
|
|
|
|
public WebSocketMsgHandleHelper MsgHandleHelper { get; } = new WebSocketMsgHandleHelper();
|
2024-10-10 10:45:53 +08:00
|
|
|
|
|
2024-10-10 20:52:19 +08:00
|
|
|
|
private HttpListener listener;
|
2024-10-15 10:55:41 +08:00
|
|
|
|
|
2024-10-15 21:56:09 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 创建无须授权验证的WebSocket服务端
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public WebSocketServer()
|
|
|
|
|
|
{
|
|
|
|
|
|
this.AuthorizedClients = new ConcurrentDictionary<string, WebSocketAuthorizedHelper>();
|
|
|
|
|
|
this.InspectionAuthorizedFunc = (tokenObj) => Task.FromResult(true);
|
|
|
|
|
|
this.IsNeedInspectionAuthorized = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 创建需要授权验证的WebSocket服务端
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="tokenKey">token 字段</param>
|
|
|
|
|
|
/// <param name="inspectionAuthorizedFunc">验证token的方法</param>
|
|
|
|
|
|
public WebSocketServer(string tokenKey, Func<dynamic, Task<bool>> inspectionAuthorizedFunc)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.TokenKey = tokenKey;
|
|
|
|
|
|
this.AuthorizedClients = new ConcurrentDictionary<string, WebSocketAuthorizedHelper>();
|
|
|
|
|
|
this.InspectionAuthorizedFunc = inspectionAuthorizedFunc;
|
|
|
|
|
|
this.IsNeedInspectionAuthorized = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 授权
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public ConcurrentDictionary<string, WebSocketAuthorizedHelper> AuthorizedClients;
|
|
|
|
|
|
private readonly string TokenKey;
|
|
|
|
|
|
private readonly Func<dynamic, Task<bool>> InspectionAuthorizedFunc;
|
|
|
|
|
|
private bool IsNeedInspectionAuthorized = false;
|
2024-10-15 10:55:41 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 进行监听服务
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="url"></param>
|
|
|
|
|
|
/// <returns></returns>
|
2024-10-07 15:15:18 +08:00
|
|
|
|
public async Task StartAsync(string url)
|
|
|
|
|
|
{
|
2024-10-10 10:45:53 +08:00
|
|
|
|
listener = new HttpListener();
|
2024-10-07 15:15:18 +08:00
|
|
|
|
listener.Prefixes.Add(url);
|
2024-10-15 21:56:09 +08:00
|
|
|
|
await Console.Out.WriteLineAsync($"WebSocket消息处理已启动[{url}]");
|
2024-10-11 16:46:16 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
listener.Start();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
await Console.Out.WriteLineAsync(ex.Message);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2024-10-07 15:15:18 +08:00
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
|
{
|
2024-10-10 10:45:53 +08:00
|
|
|
|
try
|
2024-10-07 15:15:18 +08:00
|
|
|
|
{
|
2024-10-10 10:45:53 +08:00
|
|
|
|
var context = await listener.GetContextAsync();
|
|
|
|
|
|
string clientPoint = context.Request.RemoteEndPoint?.ToString();
|
|
|
|
|
|
|
|
|
|
|
|
await Console.Out.WriteLineAsync($"新的连接加入:{clientPoint}");
|
2024-10-15 21:56:09 +08:00
|
|
|
|
|
2024-10-10 10:45:53 +08:00
|
|
|
|
if (context.Request.IsWebSocketRequest)
|
|
|
|
|
|
{
|
2024-10-15 21:56:09 +08:00
|
|
|
|
WebSocketAuthorizedHelper authorizedHelper = null;
|
|
|
|
|
|
if (IsNeedInspectionAuthorized)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (AuthorizedClients.TryAdd(clientPoint, new WebSocketAuthorizedHelper(clientPoint, TokenKey, InspectionAuthorizedFunc)))
|
|
|
|
|
|
{
|
|
|
|
|
|
AuthorizedClients.TryGetValue(clientPoint, out authorizedHelper);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-10-10 10:45:53 +08:00
|
|
|
|
|
2024-10-15 21:56:09 +08:00
|
|
|
|
var webSocketContext = await context.AcceptWebSocketAsync(null); //新连接
|
|
|
|
|
|
_ = HandleWebSocketAsync(webSocketContext.WebSocket, authorizedHelper); // 处理消息
|
2024-10-10 10:45:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
await Console.Out.WriteLineAsync(ex.Message);
|
|
|
|
|
|
break;
|
2024-10-07 15:15:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-15 10:55:41 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 停止监听服务
|
|
|
|
|
|
/// </summary>
|
2024-10-10 10:45:53 +08:00
|
|
|
|
public void Stop()
|
|
|
|
|
|
{
|
|
|
|
|
|
listener?.Stop();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-15 21:56:09 +08:00
|
|
|
|
private async Task HandleWebSocketAsync(WebSocket webSocket, WebSocketAuthorizedHelper authorizedHelper)
|
2024-10-07 15:15:18 +08:00
|
|
|
|
{
|
2024-10-15 21:56:09 +08:00
|
|
|
|
// 需要授权,却没有成功创建授权类,关闭连接
|
|
|
|
|
|
if (IsNeedInspectionAuthorized && authorizedHelper is null)
|
|
|
|
|
|
{
|
|
|
|
|
|
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Func<string, Task> SendAsync = async (text) =>
|
2024-10-10 10:45:53 +08:00
|
|
|
|
{
|
|
|
|
|
|
await WebSocketServer.SendAsync(webSocket, text);
|
|
|
|
|
|
};
|
2024-10-15 21:56:09 +08:00
|
|
|
|
|
2024-10-07 15:15:18 +08:00
|
|
|
|
var buffer = new byte[1024];
|
|
|
|
|
|
while (webSocket.State == WebSocketState.Open)
|
|
|
|
|
|
{
|
|
|
|
|
|
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
|
|
|
|
|
if (result.MessageType == WebSocketMessageType.Close)
|
|
|
|
|
|
{
|
2024-10-10 10:45:53 +08:00
|
|
|
|
SendAsync = null;
|
2024-10-07 15:15:18 +08:00
|
|
|
|
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
|
2024-10-15 21:56:09 +08:00
|
|
|
|
if (IsNeedInspectionAuthorized)
|
|
|
|
|
|
{
|
|
|
|
|
|
AuthorizedClients.TryRemove(authorizedHelper.AddresPort, out var _);
|
|
|
|
|
|
}
|
2024-10-07 15:15:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2024-10-15 21:56:09 +08:00
|
|
|
|
var message = Encoding.UTF8.GetString(buffer, 0, result.Count); // 序列为文本
|
|
|
|
|
|
if(!IsNeedInspectionAuthorized)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 无须授权
|
|
|
|
|
|
_ = MsgHandleHelper.HandleMsgAsync(SendAsync, message); // 处理消息
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 需要授权
|
|
|
|
|
|
if (!authorizedHelper.IsAuthorized)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 该连接尚未验证授权,尝试检测授权
|
|
|
|
|
|
_ = SendAsync("正在授权");
|
|
|
|
|
|
await authorizedHelper.HandleAuthorized(message);
|
|
|
|
|
|
}
|
2024-10-07 15:15:18 +08:00
|
|
|
|
|
2024-10-15 21:56:09 +08:00
|
|
|
|
|
|
|
|
|
|
if (authorizedHelper.IsAuthorized)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 该连接通过了验证
|
|
|
|
|
|
_ = SendAsync("授权成功");
|
|
|
|
|
|
_ = MsgHandleHelper.HandleMsgAsync(SendAsync, message); // 处理消息
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
_ = SendAsync("授权失败");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-07 15:15:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-10-10 10:45:53 +08:00
|
|
|
|
|
2024-10-15 10:55:41 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 发送消息
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="webSocket"></param>
|
|
|
|
|
|
/// <param name="message"></param>
|
|
|
|
|
|
/// <returns></returns>
|
2024-10-10 10:45:53 +08:00
|
|
|
|
public static async Task SendAsync(WebSocket webSocket, string message)
|
|
|
|
|
|
{
|
|
|
|
|
|
var buffer = Encoding.UTF8.GetBytes(message);
|
|
|
|
|
|
await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-07 15:15:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|