添加HTTP相关(WebSocket) (#74)

* 添加HTTP相关(WebSocket)

* 添加WebDataRequest
This commit is contained in:
milimoe 2024-05-11 13:14:08 +08:00 committed by GitHub
parent 1f39603039
commit 7bc244ff49
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 757 additions and 74 deletions

View File

@ -41,11 +41,11 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
/// <summary>
/// 私有的实现类
/// </summary>
private readonly Request Worker;
private readonly SocketRequest Worker;
/// <summary>
/// 基于本地已连接的Socket创建新的数据请求
/// 使用RunTimeModel中的NewDataRequest创建一个新的请求
/// 基于本地已连接的 <see cref="Socket"/> 创建新的数据请求<para/>
/// 使用 <see cref="RunTimeController"/> 中的 <see cref="RunTimeController.NewDataRequest"/> 创建一个新的请求
/// </summary>
/// <param name="Socket"></param>
/// <param name="RequestType"></param>
@ -54,7 +54,7 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
{
Worker = new(Socket, RequestType, IsLongRunning);
}
/// <summary>
/// 添加数据
/// </summary>
@ -105,18 +105,17 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
return GetHashtableJsonObject<T>(Worker.ResultData, key);
}
private class Request : SocketHandlerController
private class SocketRequest(Socket? Socket, DataRequestType RequestType, bool IsLongRunning = false) : SocketHandlerController(Socket)
{
public Hashtable RequestData { get; } = new();
public Hashtable RequestData { get; } = [];
public Hashtable ResultData => _ResultData;
public RequestResult Result => _Result;
public string Error => _Error;
private readonly Socket? Socket;
private readonly DataRequestType RequestType;
private readonly bool _IsLongRunning;
private Hashtable _ResultData = new();
private readonly Socket? Socket = Socket;
private readonly DataRequestType RequestType = RequestType;
private readonly bool _IsLongRunning = IsLongRunning;
private Hashtable _ResultData = [];
private RequestResult _Result = RequestResult.Missing;
private string _Error = "";
@ -158,13 +157,6 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
}
}
public Request(Socket? Socket, DataRequestType RequestType, bool IsLongRunning = false) : base(Socket)
{
this.Socket = Socket;
this.RequestType = RequestType;
_IsLongRunning = IsLongRunning;
}
public override void SocketHandler(SocketObject SocketObject)
{
try
@ -177,7 +169,7 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
if (type == RequestType)
{
if (!_IsLongRunning) Dispose();
_ResultData = SocketObject.GetParam<Hashtable>(1) ?? new();
_ResultData = SocketObject.GetParam<Hashtable>(1) ?? [];
_Result = RequestResult.Success;
}
}

View File

@ -0,0 +1,136 @@
using System.Collections;
using Milimoe.FunGame.Core.Library.Common.Network;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Library.Exception;
namespace Milimoe.FunGame.Core.Api.Transmittal
{
public class WebDataRequest
{
/// <summary>
/// 数据请求结果
/// </summary>
public RequestResult Result => Worker.Result;
/// <summary>
/// 详细错误信息
/// </summary>
public string Error => Worker.Error;
// 获取ResultData中key值对应的Json字符串
// -- 此索引器仅返回Json字符串对象类型请使用反序列化方法GetResult<T>() --
// -- 当然也可以自己反序列化 --
// -- 基本类型可能有效,但仍建议使用反序列化方法 --
public object? this[string key]
{
get
{
return Worker.ResultData[key];
}
set
{
AddRequestData(key, value);
}
}
/// <summary>
/// 私有的实现类
/// </summary>
private readonly WebSocketRequest Worker;
/// <summary>
/// 基于本地已连接的 <see cref="HTTPClient"/> 创建新的数据请求<para/>
/// </summary>
/// <param name="Socket"></param>
/// <param name="RequestType"></param>
internal WebDataRequest(HTTPClient Socket, DataRequestType RequestType)
{
Worker = new(Socket, RequestType);
}
/// <summary>
/// 添加数据
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddRequestData(string key, object? value)
{
if (Worker.RequestData.ContainsKey(key)) Worker.RequestData[key] = value;
else Worker.RequestData.Add(key, value);
}
/// <summary>
/// 向服务器发送数据请求
/// </summary>
/// <returns></returns>
public async Task<RequestResult> SendRequest()
{
await Worker.SendRequestAsync();
return Result;
}
/// <summary>
/// 异步向服务器发送数据请求
/// </summary>
/// <returns></returns>
public async Task<RequestResult> SendRequestAsync()
{
await Worker.SendRequestAsync();
return Result;
}
/// <summary>
/// 获取指定key对应的反序列化对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <returns></returns>
public T? GetResult<T>(string key)
{
return GetHashtableJsonObject<T>(Worker.ResultData, key);
}
private class WebSocketRequest(HTTPClient? Socket, DataRequestType RequestType)
{
public Hashtable RequestData { get; } = [];
public Hashtable ResultData => _ResultData;
public RequestResult Result => _Result;
public string Error => _Error;
private readonly HTTPClient? Socket = Socket;
private readonly DataRequestType RequestType = RequestType;
private readonly Hashtable _ResultData = [];
private RequestResult _Result = RequestResult.Missing;
private string _Error = "";
public async Task SendRequestAsync()
{
try
{
if (Socket != null)
{
await Socket.Send(SocketMessageType.DataRequest, RequestType, RequestData);
}
else throw new ConnectFailedException();
}
catch (Exception e)
{
_Result = RequestResult.Fail;
_Error = e.GetErrorInfo();
}
}
}
/// <summary>
/// 反序列化Hashtable中的Json对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="hashtable"></param>
/// <param name="key"></param>
/// <returns></returns>
public static T? GetHashtableJsonObject<T>(Hashtable hashtable, string key)
{
return Service.JsonManager.GetObject<T>(hashtable, key);
}
}
}

View File

@ -85,7 +85,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
}
return default;
}
/// <summary>
/// 添加一个配置如果已存在key会覆盖
/// </summary>

View File

@ -1,6 +1,6 @@
using System.Collections;
using Milimoe.FunGame.Core.Api.Transmittal;
using Milimoe.FunGame.Core.Interface;
using Milimoe.FunGame.Core.Interface.Addons;
using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Controller
@ -78,7 +78,7 @@ namespace Milimoe.FunGame.Core.Controller
}
private void DefaultPrint(string msg) => Console.Write("\r" + msg + "\n\r> ");
private void DefaultPrint(Exception e) => DefaultPrint(e.ToString());
private DataRequest DefaultNewDataRequest(DataRequestType type)

View File

@ -57,7 +57,7 @@ namespace Milimoe.FunGame.Core.Controller
return result;
}
/// <summary>
/// 发送结束游戏反馈
/// </summary>

View File

@ -1,4 +1,4 @@
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Interface.Sockets;
using Milimoe.FunGame.Core.Library.Common.Architecture;
using Milimoe.FunGame.Core.Library.Common.Network;
using Milimoe.FunGame.Core.Service;

View File

@ -27,8 +27,8 @@
</member>
<member name="M:Milimoe.FunGame.Core.Api.Transmittal.DataRequest.#ctor(Milimoe.FunGame.Core.Library.Common.Network.Socket,Milimoe.FunGame.Core.Library.Constant.DataRequestType,System.Boolean)">
<summary>
基于本地已连接的Socket创建新的数据请求
使用RunTimeModel中的NewDataRequest创建一个新的请求
基于本地已连接的 <see cref="T:Milimoe.FunGame.Core.Library.Common.Network.Socket"/> 创建新的数据请求<para/>
使用 <see cref="T:Milimoe.FunGame.Core.Controller.RunTimeController"/> 中的 <see cref="M:Milimoe.FunGame.Core.Controller.RunTimeController.NewDataRequest(Milimoe.FunGame.Core.Library.Constant.DataRequestType)"/> 创建一个新的请求
</summary>
<param name="Socket"></param>
<param name="RequestType"></param>
@ -204,6 +204,64 @@
回滚事务
</summary>
</member>
<member name="P:Milimoe.FunGame.Core.Api.Transmittal.WebDataRequest.Result">
<summary>
数据请求结果
</summary>
</member>
<member name="P:Milimoe.FunGame.Core.Api.Transmittal.WebDataRequest.Error">
<summary>
详细错误信息
</summary>
</member>
<member name="F:Milimoe.FunGame.Core.Api.Transmittal.WebDataRequest.Worker">
<summary>
私有的实现类
</summary>
</member>
<member name="M:Milimoe.FunGame.Core.Api.Transmittal.WebDataRequest.#ctor(Milimoe.FunGame.Core.Library.Common.Network.HTTPClient,Milimoe.FunGame.Core.Library.Constant.DataRequestType)">
<summary>
基于本地已连接的 <see cref="T:Milimoe.FunGame.Core.Library.Common.Network.HTTPClient"/> 创建新的数据请求<para/>
</summary>
<param name="Socket"></param>
<param name="RequestType"></param>
</member>
<member name="M:Milimoe.FunGame.Core.Api.Transmittal.WebDataRequest.AddRequestData(System.String,System.Object)">
<summary>
添加数据
</summary>
<param name="key"></param>
<param name="value"></param>
</member>
<member name="M:Milimoe.FunGame.Core.Api.Transmittal.WebDataRequest.SendRequest">
<summary>
向服务器发送数据请求
</summary>
<returns></returns>
</member>
<member name="M:Milimoe.FunGame.Core.Api.Transmittal.WebDataRequest.SendRequestAsync">
<summary>
异步向服务器发送数据请求
</summary>
<returns></returns>
</member>
<member name="M:Milimoe.FunGame.Core.Api.Transmittal.WebDataRequest.GetResult``1(System.String)">
<summary>
获取指定key对应的反序列化对象
</summary>
<typeparam name="T"></typeparam>
<param name="key"></param>
<returns></returns>
</member>
<member name="M:Milimoe.FunGame.Core.Api.Transmittal.WebDataRequest.GetHashtableJsonObject``1(System.Collections.Hashtable,System.String)">
<summary>
反序列化Hashtable中的Json对象
</summary>
<typeparam name="T"></typeparam>
<param name="hashtable"></param>
<param name="key"></param>
<returns></returns>
</member>
<member name="M:Milimoe.FunGame.Core.Api.Utility.Factory.GetCharacter">
<summary>
获取角色实例
@ -1138,7 +1196,7 @@
<param name="type"></param>
<returns></returns>
</member>
<member name="M:Milimoe.FunGame.Core.Controller.AddonController.#ctor(Milimoe.FunGame.Core.Interface.IAddon,System.Collections.Hashtable)">
<member name="M:Milimoe.FunGame.Core.Controller.AddonController.#ctor(Milimoe.FunGame.Core.Interface.Addons.IAddon,System.Collections.Hashtable)">
<summary>
新建一个AddonController
</summary>

View File

@ -1,6 +1,4 @@
using Milimoe.FunGame.Core.Controller;
namespace Milimoe.FunGame.Core.Interface
namespace Milimoe.FunGame.Core.Interface.Addons
{
public interface IAddon
{

View File

@ -1,6 +1,6 @@
using Milimoe.FunGame.Core.Controller;
namespace Milimoe.FunGame.Core.Interface.Base
namespace Milimoe.FunGame.Core.Interface.Addons
{
public interface IAddonController
{

View File

@ -1,4 +1,4 @@
namespace Milimoe.FunGame.Core.Interface
namespace Milimoe.FunGame.Core.Interface.Addons
{
public interface IGameMap : IAddon
{

View File

@ -1,6 +1,4 @@
using Milimoe.FunGame.Core.Interface.Base;
namespace Milimoe.FunGame.Core.Interface
namespace Milimoe.FunGame.Core.Interface.Addons
{
public interface IGameMode : IAddon, IAddonController, IGamingConnectEventHandler, IGamingDisconnectEventHandler, IGamingReconnectEventHandler, IGamingBanCharacterEventHandler, IGamingPickCharacterEventHandler,
IGamingRandomEventHandler, IGamingRoundEventHandler, IGamingLevelUpEventHandler, IGamingMoveEventHandler, IGamingAttackEventHandler, IGamingSkillEventHandler, IGamingItemEventHandler, IGamingMagicEventHandler,

View File

@ -3,7 +3,7 @@ using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Interface
namespace Milimoe.FunGame.Core.Interface.Addons
{
public interface IGameModeServer : IAddon, IAddonController
{

View File

@ -1,6 +1,4 @@
using Milimoe.FunGame.Core.Interface.Base;
namespace Milimoe.FunGame.Core.Interface
namespace Milimoe.FunGame.Core.Interface.Addons
{
public interface IPlugin : IAddon, IAddonController, IConnectEventHandler, IDisconnectEventHandler, ILoginEventHandler, ILogoutEventHandler, IRegEventHandler, IIntoRoomEventHandler, ISendTalkEventHandler,
ICreateRoomEventHandler, IQuitRoomEventHandler, IChangeRoomSettingEventHandler, IStartMatchEventHandler, IStartGameEventHandler, IChangeProfileEventHandler, IChangeAccountSettingEventHandler,

View File

@ -0,0 +1,12 @@
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Library.Common.Network;
using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Interface.HTTP
{
public interface IHTTPClient : IBaseSocket
{
public Task<SocketResult> Send(SocketMessageType type, params object[] objs);
public SocketObject SocketObject_Handler(SocketObject objs);
}
}

View File

@ -0,0 +1,12 @@
using System.Net;
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Library.Common.Network;
namespace Milimoe.FunGame.Core.Interface.HTTP
{
public interface IHTTPListener : IBaseSocket
{
public HttpListener Instance { get; }
public SocketObject SocketObject_Handler(SocketObject objs);
}
}

View File

@ -2,16 +2,14 @@
namespace Milimoe.FunGame.Core.Interface.Base
{
public interface ISocket
public interface IBaseSocket
{
public System.Net.Sockets.Socket Instance { get; }
public SocketRuntimeType Runtime { get; }
public Guid Token { get; }
public string ServerAddress { get; }
public int ServerPort { get; }
public string ServerName { get; }
public string ServerNotice { get; }
public bool Connected => Instance != null && Instance.Connected;
public void Close();
}
}

View File

@ -1,6 +1,6 @@
using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Interface.Base
namespace Milimoe.FunGame.Core.Interface.Sockets
{
public interface IClientSocket : ISocket
{

View File

@ -0,0 +1,10 @@
using Milimoe.FunGame.Core.Interface.Base;
namespace Milimoe.FunGame.Core.Interface.Sockets
{
public interface ISocket : IBaseSocket
{
public System.Net.Sockets.Socket Instance { get; }
public bool Connected => Instance != null && Instance.Connected;
}
}

View File

@ -1,6 +1,6 @@
using Milimoe.FunGame.Core.Library.Common.Network;
namespace Milimoe.FunGame.Core.Interface.Base
namespace Milimoe.FunGame.Core.Interface.Sockets
{
public interface ISocketHandler
{

View File

@ -1,4 +1,4 @@
namespace Milimoe.FunGame.Core.Interface.Base
namespace Milimoe.FunGame.Core.Interface.Sockets
{
public interface ISocketHeartBeat
{

View File

@ -1,5 +1,5 @@
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface;
using Milimoe.FunGame.Core.Interface.Addons;
namespace Milimoe.FunGame.Core.Library.Common.Addon
{

View File

@ -1,4 +1,4 @@
using Milimoe.FunGame.Core.Interface;
using Milimoe.FunGame.Core.Interface.Addons;
namespace Milimoe.FunGame.Core.Library.Common.Addon
{

View File

@ -1,6 +1,7 @@
using System.Collections;
using Milimoe.FunGame.Core.Controller;
using Milimoe.FunGame.Core.Interface;
using Milimoe.FunGame.Core.Interface.Addons;
using Milimoe.FunGame.Core.Library.Common.Event;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model;

View File

@ -1,7 +1,7 @@
using System.Collections;
using Milimoe.FunGame.Core.Controller;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface;
using Milimoe.FunGame.Core.Interface.Addons;
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Library.Constant;

View File

@ -1,5 +1,5 @@
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface;
using Milimoe.FunGame.Core.Interface.Addons;
namespace Milimoe.FunGame.Core.Library.Common.Addon
{

View File

@ -1,8 +1,7 @@
using Milimoe.FunGame.Core.Api.Transmittal;
using Milimoe.FunGame.Core.Controller;
using Milimoe.FunGame.Core.Controller;
using Milimoe.FunGame.Core.Interface;
using Milimoe.FunGame.Core.Interface.Addons;
using Milimoe.FunGame.Core.Library.Common.Event;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model;
namespace Milimoe.FunGame.Core.Library.Common.Addon

View File

@ -1,5 +1,5 @@
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface;
using Milimoe.FunGame.Core.Interface.Addons;
namespace Milimoe.FunGame.Core.Library.Common.Addon
{

View File

@ -0,0 +1,91 @@
using Milimoe.FunGame.Core.Interface.Sockets;
using Milimoe.FunGame.Core.Library.Common.Network;
using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Library.Common.Architecture
{
public class HeartBeat : ISocketHeartBeat
{
public TransmittalType TransmittalType { get; } = TransmittalType.Socket;
public bool SendingHeartBeat => _SendingHeartBeat;
public int HeartBeatFaileds => _HeartBeatFaileds;
private Task? SendingHeartBeatTask;
private bool _SendingHeartBeat = false;
private int _HeartBeatFaileds = 0;
private readonly Socket? _Socket = null;
private readonly HTTPClient? _HTTPClient = null;
public HeartBeat(Socket socket)
{
_Socket = socket;
this.TransmittalType = TransmittalType.Socket;
}
public HeartBeat(HTTPClient client)
{
_HTTPClient = client;
this.TransmittalType = TransmittalType.WebSocket;
}
public void StartSendingHeartBeat()
{
if (!FunGameInfo.FunGame_DebugMode)
{
_SendingHeartBeat = true;
SendingHeartBeatTask = Task.Factory.StartNew(SendHeartBeat);
}
}
public void StopSendingHeartBeat()
{
_SendingHeartBeat = false;
SendingHeartBeatTask?.Wait(1);
SendingHeartBeatTask = null;
}
private async Task SendHeartBeat()
{
await Task.Delay(100);
if (_Socket != null)
{
while (_Socket.Connected)
{
if (!SendingHeartBeat) _SendingHeartBeat = true;
// 发送心跳包
if (_Socket.Send(SocketMessageType.HeartBeat) == SocketResult.Success)
{
await Task.Delay(4 * 000);
AddHeartBeatFaileds();
}
else AddHeartBeatFaileds();
await Task.Delay(20 * 000);
}
}
else if (_HTTPClient != null)
{
while (_HTTPClient.Instance?.State == System.Net.WebSockets.WebSocketState.Open)
{
if (!SendingHeartBeat) _SendingHeartBeat = true;
// 发送心跳包
if (await _HTTPClient.Send(SocketMessageType.HeartBeat) == SocketResult.Success)
{
await Task.Delay(4 * 000);
AddHeartBeatFaileds();
}
else AddHeartBeatFaileds();
await Task.Delay(20 * 000);
}
}
_SendingHeartBeat = false;
}
private void AddHeartBeatFaileds()
{
// 超过三次没回应心跳,服务器连接失败。
if (_HeartBeatFaileds++ >= 3)
throw new LostConnectException();
}
}
}

View File

@ -1,19 +1,19 @@
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Interface.Sockets;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Service;
namespace Milimoe.FunGame.Core.Library.Common.Network
{
public class ClientSocket : IClientSocket
public class ClientSocket(System.Net.Sockets.Socket Instance, int ServerPort, string ClientIP, string ClientName, Guid Token) : IClientSocket
{
public System.Net.Sockets.Socket Instance { get; }
public System.Net.Sockets.Socket Instance { get; } = Instance;
public SocketRuntimeType Runtime => SocketRuntimeType.Server;
public Guid Token { get; } = Guid.Empty;
public Guid Token { get; } = Token;
public string ServerAddress { get; } = "";
public int ServerPort { get; } = 0;
public int ServerPort { get; } = ServerPort;
public string ServerName { get; } = "";
public string ServerNotice { get; } = "";
public string ClientIP { get; } = "";
public string ClientIP { get; } = ClientIP;
public string ClientName => _ClientName;
public bool Connected => Instance != null && Instance.Connected;
public bool Receiving => _Receiving;
@ -21,16 +21,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
private Task? ReceivingTask;
private bool _Receiving;
private string _ClientName;
public ClientSocket(System.Net.Sockets.Socket Instance, int ServerPort, string ClientIP, string ClientName, Guid Token)
{
this.Instance = Instance;
this.ServerPort = ServerPort;
this.ClientIP = ClientIP;
this._ClientName = ClientName;
this.Token = Token;
}
private readonly string _ClientName = ClientName;
public void Close()
{

View File

@ -0,0 +1,97 @@
using System.Net.WebSockets;
using Milimoe.FunGame.Core.Interface.HTTP;
using Milimoe.FunGame.Core.Library.Common.Architecture;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Library.Exception;
using Milimoe.FunGame.Core.Service;
namespace Milimoe.FunGame.Core.Library.Common.Network
{
public class HTTPClient : IHTTPClient
{
public ClientWebSocket? Instance { get; } = null;
public HeartBeat HeartBeat { get; }
public SocketRuntimeType Runtime => SocketRuntimeType.Client;
public Guid Token { get; } = Guid.Empty;
public string ServerAddress { get; } = "";
public int ServerPort { get; } = 0;
public string ServerName { get; } = "";
public string ServerNotice { get; } = "";
private bool _Listening = false;
private HTTPClient(ClientWebSocket Instance, string ServerAddress, int ServerPort, params object[] args)
{
this.Instance = Instance;
this.ServerAddress = ServerAddress;
this.ServerPort = ServerPort;
HeartBeat = new(this);
HeartBeat.StartSendingHeartBeat();
Task.Factory.StartNew(async () => await StartListening(args));
}
public static async Task<HTTPClient> Connect(string ServerAddress, int ServerPort, bool SSL, string SubDirectory = "", params object[] args)
{
string ServerIP = Api.Utility.NetworkUtility.GetIPAddress(ServerAddress);
Uri uri = new((SSL ? "wss://" : "ws://") + ServerIP + ":" + ServerPort + "/" + SubDirectory);
ClientWebSocket? socket = await HTTPManager.Connect(uri);
if (socket != null && socket.State == WebSocketState.Open)
{
HTTPClient client = new(socket, ServerAddress, ServerPort, args);
return client;
}
throw new CanNotConnectException();
}
private async Task StartListening(params object[] args)
{
if (Instance != null && Instance.State == WebSocketState.Open)
{
if (await HTTPManager.Send(Instance, new(SocketMessageType.Connect, Guid.Empty, args)) == SocketResult.Success && await HTTPManager.ReceiveMessage(this))
{
_Listening = true;
await Receive();
}
}
}
public async Task Receive()
{
while (_Listening)
{
try
{
await HTTPManager.ReceiveMessage(this);
}
catch (System.Exception e)
{
Close();
Api.Utility.TXTHelper.AppendErrorLog(e.GetErrorInfo());
throw new SocketWrongInfoException();
}
}
}
public async Task<SocketResult> Send(SocketMessageType type, params object[] objs)
{
if (Instance != null)
{
return await HTTPManager.Send(Instance, new(type, Token, objs));
}
return SocketResult.NotSent;
}
public virtual SocketObject SocketObject_Handler(SocketObject objs)
{
return new(SocketMessageType.Unknown, Guid.Empty);
}
public void Close()
{
_Listening = false;
HeartBeat.StopSendingHeartBeat();
Instance?.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
Instance?.Dispose();
}
}
}

View File

@ -0,0 +1,61 @@
using System.Net;
using System.Net.WebSockets;
using Milimoe.FunGame.Core.Interface.HTTP;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Service;
namespace Milimoe.FunGame.Core.Library.Common.Network
{
public class HTTPListener : IHTTPListener
{
public HttpListener Instance { get; }
public SocketRuntimeType Runtime => SocketRuntimeType.Server;
public Guid Token { get; } = Guid.Empty;
public string ServerAddress { get; } = "";
public int ServerPort { get; } = 0;
public string ServerName { get; } = "";
public string ServerNotice { get; } = "";
public Dictionary<Guid, WebSocket> ClientSockets { get; } = [];
private HTTPListener(HttpListener Instance, int ServerPort)
{
this.Instance = Instance;
this.ServerPort = ServerPort;
}
public static HTTPListener StartListening(int Port, bool SSL = false)
{
HttpListener? socket = HTTPManager.StartListening(Port, SSL);
if (socket != null)
{
HTTPListener instance = new(socket, Port);
Task t = Task.Run(async () => await HTTPManager.Receiving(instance));
return instance;
}
else throw new SocketCreateListenException();
}
public async Task SendMessage(Guid token, SocketObject obj)
{
if (ClientSockets.TryGetValue(token, out WebSocket? socket) && socket != null)
{
await HTTPManager.Send(socket, obj);
}
}
public virtual bool CheckClientConnection(SocketObject objs)
{
return true;
}
public virtual SocketObject SocketObject_Handler(SocketObject objs)
{
return new(SocketMessageType.Unknown, Guid.Empty);
}
public void Close()
{
Instance?.Close();
}
}
}

View File

@ -1,4 +1,5 @@
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Interface.Sockets;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Service;
@ -16,7 +17,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
public bool Connected => Instance != null && Instance.Connected;
public List<IServerModel> ClientList => OnlineClients.GetList();
public List<IServerModel> UserList => OnlineUsers.GetList();
public List<string> BannedList { get; } = new();
public List<string> BannedList { get; } = [];
public int ClientCount => OnlineClients.Count;
public int UserCount => OnlineUsers.Count;
public int BannedCount => BannedList.Count;

View File

@ -1,4 +1,4 @@
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Interface.Sockets;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Library.Exception;
using Milimoe.FunGame.Core.Service;

View File

@ -9,7 +9,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
{
public SocketMessageType SocketType { get; } = SocketMessageType.Unknown;
public Guid Token { get; } = Guid.Empty;
public object[] Parameters { get; } = Array.Empty<object>();
public object[] Parameters { get; } = [];
public int Length => Parameters.Length;
// 从参数列表中获取指定索引的参数的Json字符串

View File

@ -19,12 +19,12 @@ namespace Milimoe.FunGame.Core.Library.Constant
/// 默认的未知用户
/// </summary>
public static User UnknownUserInstance => new();
/// <summary>
/// 游客用户
/// </summary>
public static User GuestUserInstance => new(UserType.Guest);
/// <summary>
/// 本地用户
/// </summary>

View File

@ -134,6 +134,12 @@ namespace Milimoe.FunGame.Core.Library.Constant
Punish
}
public enum TransmittalType
{
Socket,
WebSocket
}
public enum SocketRuntimeType
{
Client,

224
Service/HTTPManager.cs Normal file
View File

@ -0,0 +1,224 @@
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
{
internal static HttpListener? ServerSocket => _ServerSocket;
private static HttpListener? _ServerSocket = null;
internal static HttpListener StartListening(int Port = 22227, bool SSL = false)
{
HttpListener listener = new();
listener.Prefixes.Add((SSL ? "https://" : "http://") + "localhost:" + Port + "/");
listener.Start();
return listener;
}
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;
}
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;
}
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;
}
internal static async Task Receiving(HTTPListener listener)
{
if (ServerSocket != null)
{
try
{
while (true)
{
HttpListenerContext context = await ServerSocket.GetContextAsync();
if (context.Request.IsWebSocketRequest)
{
await AddClientWebSocket(listener, context);
}
else
{
context.Response.StatusCode = 400;
context.Response.Close();
}
}
}
catch
{
_ServerSocket = null;
}
}
}
internal static async Task<bool> ReceiveMessage(HTTPClient client)
{
if (client.Instance is null) return false;
byte[] buffer = new byte[General.SocketByteSize];
WebSocketReceiveResult result = await client.Instance.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
string msg = Encoding.UTF8.GetString(buffer).Replace("\0", "").Trim();
SocketObject[] objs = await GetSocketObjects(client.Instance, result, msg);
foreach (SocketObject obj in objs)
{
SocketObject sendobject = client.SocketObject_Handler(obj);
if (obj.SocketType == SocketMessageType.Connect)
{
return true;
}
else if (obj.SocketType == SocketMessageType.Disconnect)
{
await client.Instance.CloseAsync(result.CloseStatus ?? WebSocketCloseStatus.NormalClosure, result.CloseStatusDescription, CancellationToken.None);
return true;
}
await Send(client.Instance, sendobject);
}
return true;
}
private static async Task AddClientWebSocket(HTTPListener listener, HttpListenerContext context)
{
HttpListenerWebSocketContext socketContext = await context.AcceptWebSocketAsync(null);
WebSocket socket = socketContext.WebSocket;
byte[] buffer = new byte[General.SocketByteSize];
WebSocketReceiveResult result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
string msg = Encoding.UTF8.GetString(buffer).Replace("\0", "").Trim();
SocketObject sendobject = new(SocketMessageType.Unknown, Guid.Empty);
SocketObject[] objs = await GetSocketObjects(socket, result, msg);
bool isConnect = false;
foreach (SocketObject obj in objs)
{
if (obj.SocketType == SocketMessageType.Connect)
{
isConnect = listener.CheckClientConnection(obj);
}
else if (listener.ClientSockets.ContainsKey(obj.Token))
{
sendobject = listener.SocketObject_Handler(obj);
isConnect = true;
}
}
if (isConnect)
{
Guid token = Guid.NewGuid();
listener.ClientSockets.TryAdd(token, socket);
await Send(socket, sendobject);
while (!result.CloseStatus.HasValue)
{
try
{
buffer = new byte[General.SocketByteSize];
result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
msg = Encoding.UTF8.GetString(buffer).Replace("\0", "").Trim();
objs = await GetSocketObjects(socket, result, msg);
foreach (SocketObject obj in objs)
{
sendobject = listener.SocketObject_Handler(obj);
if (obj.SocketType == SocketMessageType.Disconnect)
{
await socket.CloseAsync(result.CloseStatus ?? WebSocketCloseStatus.NormalClosure, result.CloseStatusDescription, CancellationToken.None);
return;
}
await Send(socket, sendobject);
}
}
catch (Exception e)
{
TXTHelper.AppendErrorLog(e.GetErrorInfo());
await socket.CloseAsync(WebSocketCloseStatus.InternalServerError, result.CloseStatusDescription, CancellationToken.None);
}
}
}
}
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];
}
}
}