FunGame-Core/Service/HTTPManager.cs
milimoe d23c6597d1
完善 HTTPClient;添加日志级别;添加匿名服务器模组;修复不同时间多客户端连接游戏模组时可能产生的线程安全问题 (#106)
* 完善 HTTPClient;添加日志级别;添加匿名服务器模组(此模组不强制要求登录、客户端安装)

* 添加参数

* 添加 null 检查

* 修复不同时间多客户端连接游戏模组时可能产生的线程安全问题
2025-01-17 18:58:51 +08:00

250 lines
9.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Net;
using System.Net.WebSockets;
using System.Text;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Library.Common.Network;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Library.Exception;
namespace Milimoe.FunGame.Core.Service
{
internal class HTTPManager
{
/// <summary>
/// 实际的 <see cref="System.Net.HttpListener"/> 监听实例 [ 单例 ]
/// </summary>
internal static HttpListener? HttpListener => _HttpListener;
private static HttpListener? _HttpListener = null;
/// <summary>
/// 开始监听
/// 当 <paramref name="address"/> = "*" 时,需要管理员权限
/// </summary>
/// <param name="address"></param>
/// <param name="port"></param>
/// <param name="subUrl"></param>
/// <param name="ssl"></param>
/// <returns></returns>
internal static HttpListener StartListening(string address = "*", int port = 22223, string subUrl = "ws", bool ssl = false)
{
_HttpListener = new();
_HttpListener.Prefixes.Add((ssl ? "https://" : "http://") + address + ":" + port + "/" + subUrl.Trim('/') + "/");
_HttpListener.Start();
return _HttpListener;
}
/// <summary>
/// 客户端连接远程 WebSocket 服务器
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
internal static async Task<ClientWebSocket?> Connect(Uri uri)
{
ClientWebSocket socket = new();
await socket.ConnectAsync(uri, CancellationToken.None);
if (socket.State == WebSocketState.Open)
{
return socket;
}
return null;
}
/// <summary>
/// 客户端向服务器发送消息
/// </summary>
/// <param name="socket"></param>
/// <param name="obj"></param>
/// <returns></returns>
internal static async Task<SocketResult> Send(ClientWebSocket socket, SocketObject obj)
{
if (socket != null)
{
try
{
await socket.SendAsync(new ArraySegment<byte>(General.DefaultEncoding.GetBytes(JsonManager.GetString(obj))), WebSocketMessageType.Text, true, CancellationToken.None);
return SocketResult.Success;
}
catch (Exception e)
{
TXTHelper.AppendErrorLog(e.GetErrorInfo());
return SocketResult.Fail;
}
}
return SocketResult.NotSent;
}
/// <summary>
/// 服务器向客户端发送消息
/// </summary>
/// <param name="socket"></param>
/// <param name="obj"></param>
/// <returns></returns>
internal static async Task<SocketResult> Send(WebSocket socket, SocketObject obj)
{
if (socket != null)
{
try
{
await socket.SendAsync(new ArraySegment<byte>(General.DefaultEncoding.GetBytes(JsonManager.GetString(obj))), WebSocketMessageType.Text, true, CancellationToken.None);
return SocketResult.Success;
}
catch (Exception e)
{
TXTHelper.AppendErrorLog(e.GetErrorInfo());
return SocketResult.Fail;
}
}
return SocketResult.NotSent;
}
/// <summary>
/// 服务器接受一个 HTTP 的 WebSocket 升级请求
/// </summary>
/// <returns>[0]客户端IP地址[1]客户端的WebSocket实例</returns>
internal static async Task<object[]> Accept()
{
if (HttpListener is null) return [];
try
{
HttpListenerContext context = await HttpListener.GetContextAsync();
if (context.Request.IsWebSocketRequest)
{
HttpListenerWebSocketContext socketContext = await context.AcceptWebSocketAsync(null);
WebSocket socket = socketContext.WebSocket;
string ip = context.Request.RemoteEndPoint.ToString();
return [ip, socket];
}
else
{
context.Response.StatusCode = 400;
context.Response.Close();
}
}
catch
{
HttpListener?.Close();
}
return [];
}
/// <summary>
/// 服务器接收客户端消息
/// </summary>
/// <param name="socket"></param>
/// <returns></returns>
internal static async Task<SocketObject[]> Receive(WebSocket socket)
{
try
{
List<SocketObject> objs = [];
if (socket != null)
{
byte[] buffer = new byte[General.SocketByteSize];
WebSocketReceiveResult result;
StringBuilder builder = new();
do
{
result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
builder.Append(General.DefaultEncoding.GetString(buffer, 0, result.Count).Replace("\0", "").Trim());
}
while (!result.EndOfMessage);
string msg = builder.ToString();
if (JsonManager.IsCompleteJson<SocketObject>(msg))
{
foreach (SocketObject obj in JsonManager.GetObjects<SocketObject>(msg))
{
objs.Add(obj);
}
}
}
return [.. objs];
}
catch (Exception e)
{
TXTHelper.AppendErrorLog(e.GetErrorInfo());
return [];
}
}
/// <summary>
/// 客户端接收服务器消息
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
internal static async Task<bool> ReceiveMessage(HTTPClient client)
{
if (client.WebSocket is null) return false;
byte[] buffer = new byte[General.SocketByteSize];
WebSocketReceiveResult result = await client.WebSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
string msg = General.DefaultEncoding.GetString(buffer).Replace("\0", "").Trim();
SocketObject[] objs = await GetSocketObjects(client.WebSocket, result, msg);
foreach (SocketObject obj in objs)
{
SocketManager.OnSocketReceive(obj);
if (obj.SocketType == SocketMessageType.Connect)
{
return true;
}
else if (obj.SocketType == SocketMessageType.Disconnect)
{
await client.WebSocket.CloseAsync(result.CloseStatus ?? WebSocketCloseStatus.NormalClosure, result.CloseStatusDescription, CancellationToken.None);
return true;
}
}
return true;
}
/// <summary>
/// 将收到的消息反序列为 <see cref="SocketObject"/>
/// </summary>
/// <param name="socket"></param>
/// <param name="result"></param>
/// <param name="msg"></param>
/// <returns></returns>
private static async Task<SocketObject[]> GetSocketObjects(WebSocket socket, WebSocketReceiveResult result, string msg)
{
List<SocketObject> objs = [];
if (JsonManager.IsCompleteJson<SocketObject>(msg))
{
foreach (SocketObject obj in JsonManager.GetObjects<SocketObject>(msg))
{
objs.Add(obj);
}
return [.. objs];
}
else
{
await Task.Delay(20);
while (true)
{
if (!result.CloseStatus.HasValue)
{
byte[] buffer = new byte[General.SocketByteSize];
result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
msg += General.DefaultEncoding.GetString(buffer).Replace("\0", "").Trim();
if (JsonManager.IsCompleteJson<SocketObject>(msg))
{
break;
}
await Task.Delay(20);
}
else break;
}
foreach (SocketObject obj in JsonManager.GetObjects<SocketObject>(msg))
{
objs.Add(obj);
}
}
return [.. objs];
}
}
}