2024-11-03 16:42:23 +08:00
/ *
* 该 接 口 为 基 础 的 网 络 接 口 , 包 括 了 网 络 服 务 , 服 务 端 接 口 , 客 户 端 接 口 的 基 本 定 义 e . g .
* ⭐ INetProvider 网 络 通 信 接 口 的 基 本 定 义
* ⭐ INetServer 服 务 端 接 口 的 基 本 定 义
* ⭐ INetClient 客 户 端 接 口 的 基 本 定 义
* /
using System ;
2025-02-24 23:03:39 +08:00
using System.Collections.Concurrent ;
2024-11-03 16:42:23 +08:00
using System.Collections.Generic ;
2025-02-24 23:03:39 +08:00
using System.IO ;
using System.Linq ;
2024-11-03 16:42:23 +08:00
using System.Net ;
2025-02-24 23:03:39 +08:00
using System.Reflection ;
using System.Text ;
using System.Threading ;
using System.Threading.Tasks ;
2024-11-03 16:42:23 +08:00
using Cysharp.Threading.Tasks ;
2025-02-24 23:03:39 +08:00
using kcp2k ;
using Microsoft.CodeAnalysis.Scripting ;
using Microsoft.Extensions.DependencyInjection ;
2024-11-03 16:42:23 +08:00
namespace BITKit
{
/// <summary>
/// 帮助类
/// </summary>
public static class NetUtils
{
/// <summary>
/// 计算文件大小函数(保留两位小数),Size为字节大小
/// </summary>
/// <param name="size">初始文件大小</param>
/// <returns></returns>
public static string GetFileSize ( long size )
{
var num = 1024.00 ; //byte
if ( size < num )
return size + "B" ;
if ( size < Math . Pow ( num , 2 ) )
return ( size / num ) . ToString ( "f2" ) + "K" ; //kb
if ( size < Math . Pow ( num , 3 ) )
return ( size / Math . Pow ( num , 2 ) ) . ToString ( "f2" ) + "M" ; //M
if ( size < Math . Pow ( num , 4 ) )
return ( size / Math . Pow ( num , 3 ) ) . ToString ( "f2" ) + "G" ; //G
return ( size / Math . Pow ( num , 4 ) ) . ToString ( "f2" ) + "T" ; //T
}
}
/// <summary>
/// 网络指令类型
/// </summary>
public enum NetCommandType : byte
{
Undefined = 0 ,
2025-02-24 23:03:39 +08:00
Message = 1 ,
Heartbeat = 2 ,
Rpc ,
SetPropertyValue ,
SetFieldValue ,
WaitTask ,
ReturnValue ,
2024-11-03 16:42:23 +08:00
}
/// <summary>
/// 网络提供服务, 包括了基础网络服务, e.g
/// ⭐向服务器发送指令
/// ⭐向所有客户端发送指令
/// ⭐向单个客户端发送指令
/// ⭐监听与取消监听网络命令
/// ⭐从服务器获取数据
/// ⭐从客户端获取数据
/// ⭐添加Rpc处理服务
/// ⭐向服务器发送Rpc
/// ⭐向所有客户端发送Rpc
/// </summary>
public interface INetProvider
{
2025-02-24 23:03:39 +08:00
public uint TickRate { get ; set ; }
2024-11-03 16:42:23 +08:00
/// <summary>
2025-02-24 23:03:39 +08:00
/// 更新Tick
2024-11-03 16:42:23 +08:00
/// </summary>
2025-02-24 23:03:39 +08:00
void Tick ( ) ;
2024-11-03 16:42:23 +08:00
/// <summary>
2025-02-24 23:03:39 +08:00
/// 连接协议握手
2024-11-03 16:42:23 +08:00
/// </summary>
2025-02-24 23:03:39 +08:00
void HandShake ( ) ;
2024-11-03 16:42:23 +08:00
/// <summary>
2025-02-24 23:03:39 +08:00
/// 获取远程接口
2024-11-03 16:42:23 +08:00
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
2025-02-24 23:03:39 +08:00
T GetRemoteInterface < T > ( ) ;
2024-11-03 16:42:23 +08:00
/// <summary>
2025-02-24 23:03:39 +08:00
/// 远程调用
2024-11-03 16:42:23 +08:00
/// </summary>
2025-02-24 23:03:39 +08:00
void Invoke ( Memory < byte > bytes ) ;
2024-11-03 16:42:23 +08:00
/// <summary>
2025-02-24 23:03:39 +08:00
/// 异步远程调用,有返回结果
2024-11-03 16:42:23 +08:00
/// </summary>
2025-02-24 23:03:39 +08:00
/// <param name="bytes"></param>
2024-11-03 16:42:23 +08:00
/// <typeparam name="T"></typeparam>
2025-02-24 23:03:39 +08:00
/// <returns></returns>
UniTask < Memory < byte > > InvokeAsync ( Memory < byte > bytes ) ;
2024-11-03 16:42:23 +08:00
}
/// <summary>
/// 服务端接口,支持服务端的基本功能
/// ⭐开启服务器(支持指定端口)
/// ⭐停止服务器
/// ⭐服务器运行状态
/// </summary>
public interface INetServer
{
/// <summary>
/// 通信接口
/// </summary>
public INetProvider NetProvider = > this as INetProvider ;
/// <summary>
/// 源物体,用于通过代理直接访问
/// </summary>
public object Source = > this ;
/// <summary>
/// 手动Tick
/// </summary>
public bool ManualTick { get ; set ; }
/// <summary>
/// 回调:当客户端连接时
/// </summary>
public event Action < int > OnClientConnected ;
/// <summary>
/// 回调:当客户端断开连接时
/// </summary>
public event Action < int > OnClientDisconnected ;
/// <summary>
/// 开启服务端的回调
/// </summary>
public event Action OnStartServer ;
/// <summary>
/// 关闭服务端的回调
/// </summary>
public event Action OnStopServer ;
/// <summary>
/// 运行服务端
/// </summary>
/// <param name="port">端口, 默认为27014</param>
void StartServer ( ushort port = 27014 ) ;
/// <summary>
/// 停止服务端
/// </summary>
/// <param name="dispose">可选参数: 强行释放, 默认为false</param>
void StopServer ( bool dispose = false ) ;
/// <summary>
/// (只读)服务器是否正在运行
/// </summary>
bool IsRunningServer { get ; }
/// <summary>
/// 向单个链接发送消息
/// </summary>
/// <param name="id"></param>
/// <param name="message"></param>
void SendMessageToClient ( int id , string message ) ;
/// <summary>
/// 向全部链接发送消息
/// </summary>
/// <param name="message"></param>
void SendMessageToAll ( string message ) ;
/// <summary>
/// 所有已连接的客户端
/// </summary>
public IDictionary < int , EndPoint > Connections { get ; }
/// <summary>
/// 踢出客户端
/// </summary>
/// <param name="id"></param>
void KickClient ( int id ) ;
}
/// <summary>
/// 基本网络客户端的接口定义,包括了基本客户端的功能
/// ⭐基本客户端回调
/// ⭐是否已连接到服务器
/// ⭐连接服务端的延迟
/// ⭐客户端Id
/// ⭐开启客户端
/// ⭐关闭客户端
/// </summary>
public interface INetClient
{
/// <summary>
/// 通讯接口
/// </summary>
public INetProvider NetProvider = > this as INetProvider ;
//基本客户端回调
public event Action OnStartConnect ;
public event Action OnConnected ;
public event Action OnDisconnected ;
public event Action OnConnectedFailed ;
/// <summary>
/// 是否已连接到服务端
/// </summary>
bool IsConnected { get ; }
/// <summary>
/// 手动Tick
/// </summary>
bool ManualTick { get ; set ; }
/// <summary>
/// 连接服务端的延迟
/// </summary>
int Ping { get ; }
/// <summary>
/// 客户端Id
/// </summary>
int Id { get ; }
/// <summary>
/// 断开链接
/// </summary>
void Disconnect ( ) ;
/// <summary>
/// 异步开始链接
/// </summary>
/// <param name="address">服务端地址</param>
/// <param name="port">端口</param>
/// <returns></returns>
2025-02-24 23:03:39 +08:00
UniTask < bool > Connect ( string address = "127.0.0.1" , ushort port = 27014 ) ;
2024-11-03 16:42:23 +08:00
/// <summary>
/// 向服务端发送消息
/// </summary>
/// <param name="message">消息</param>
void SendServerMessage ( string message ) ;
}
2025-02-24 23:03:39 +08:00
public class NetProviderCommon : INetProvider
{
public static readonly BITSharp . ICodeGenerator CodeGenerator = new RemoteInterfaceGenerator ( ) ;
private class RemoteInterfaceGenerator : BITSharp . CodeGenerator
{
public INetProvider NetProvider = new NetProviderCommon ( ) ;
public override string BeforeGenerate ( Type type )
{
return $"public {nameof(INetProvider)} {nameof(NetProvider)};" ;
}
public override IReadOnlyList < string > GenerateNamespaces ( Type type )
{
return new string [ ]
{
"System.IO" ,
typeof ( INetProvider ) . Namespace ,
} ;
}
public override string GenerateMethodContext ( MethodInfo methodInfo )
{
var codeBuilder = new StringBuilder ( ) ;
var parameterInfos = methodInfo . GetParameters ( ) ;
using var ms = new MemoryStream ( ) ;
using var writer = new BinaryWriter ( ms ) ;
foreach ( var parameterInfo in methodInfo . GetParameters ( ) )
{
if ( parameterInfo . IsOut )
{
codeBuilder . AppendLine ( $"{parameterInfo.Name} = default;" ) ;
}
}
codeBuilder . AppendLine ( " using var ms = new MemoryStream();" ) ;
codeBuilder . AppendLine ( " using var writer = new BinaryWriter(ms);" ) ;
codeBuilder . AppendLine ( " writer.Write((byte)NetCommandType.Rpc);" ) ;
codeBuilder . AppendLine ( " writer.Write((byte)NetCommandType.Rpc);" ) ;
codeBuilder . AppendLine ( $" writer.Write(\" { methodInfo . DeclaringType ! . FullName } \ ");" ) ;
codeBuilder . AppendLine ( $" writer.Write(\" { methodInfo . Name } \ ");" ) ;
codeBuilder . AppendLine ( $" writer.Write({parameterInfos.Length});" ) ;
foreach ( var parameterInfo in parameterInfos )
{
codeBuilder . AppendLine ( $"BITBinary.Write(writer,{parameterInfo.Name});" ) ;
}
var isAwaitable = methodInfo . ReturnType . GetMethod ( nameof ( Task . GetAwaiter ) ) ! = null ;
if ( isAwaitable )
{
var generics = methodInfo . ReturnType . CSharpName ( ) ;
generics = generics . Replace ( nameof ( UniTask ) , string . Empty ) ;
generics = generics . Replace ( nameof ( Task ) , string . Empty ) ;
codeBuilder . AppendLine ( $" return BITBinary.Read{generics}((await NetProvider.InvokeAsync(ms.ToArray())).ToArray());" ) ;
}
else
{
codeBuilder . AppendLine ( base . GenerateMethodContext ( methodInfo ) ) ;
}
return codeBuilder . ToString ( ) ;
}
public override string GenerateProperty ( PropertyInfo propertyInfo )
{
var codeBuilder = new StringBuilder ( ) ;
var source = base . GenerateProperty ( propertyInfo ) ;
source = source . Replace ( "get;" , $"\nget=>_{propertyInfo.Name};" ) ;
source = source . Replace ( "set;" , $"\nset{{\n_{propertyInfo.Name}=value; \n using var ms = new MemoryStream();\n using var writer = new BinaryWriter(ms);\n writer.Write((byte)NetCommandType.Rpc);\n writer.Write((byte)NetCommandType.SetPropertyValue);\n writer.Write(\" { propertyInfo . DeclaringType ! . FullName } \ ");\n writer.Write(\"{propertyInfo.Name}\");\n BITBinary.Write(writer,value);\n NetProvider.Invoke(ms.ToArray());\n }}" ) ;
codeBuilder . AppendLine ( source ) ;
codeBuilder . AppendLine ( $"public {propertyInfo.PropertyType.CSharpName()} _{propertyInfo.Name};" ) ;
return codeBuilder . ToString ( ) ;
}
}
private void SyntaxTest ( )
{
/ *
using var ms = new MemoryStream ( ) ;
using var writer = new BinaryWriter ( ms ) ;
writer . Write ( ( byte ) NetCommandType . Rpc ) ;
writer . Write ( ( byte ) NetCommandType . SetPropertyValue ) ;
writer . Write ( nameof ( SyntaxTest ) ) ;
BITBinary . Write ( writer , 1 ) ;
NetProvider . Invoke ( ms . ToArray ( ) ) ;
* /
}
public IServiceProvider ServiceProvider { get ; set ; } = new ServiceCollection ( ) . BuildServiceProvider ( ) ;
public readonly GenericEvent Events = new ( ) ;
public int Timeout = 8000 ;
public int Index ;
public readonly ConcurrentDictionary < int , UniTaskCompletionSource < Memory < byte > > > P2P = new ( ) ;
public readonly ConcurrentDictionary < int , DateTime > LastHeartbeat = new ( ) ;
public readonly ConcurrentDictionary < string , object > RemoteInterfaces = new ( ) ;
public readonly ConcurrentDictionary < Type , object > LocalServices = new ( ) ;
public readonly ConcurrentQueue < ( int id , byte [ ] bytes ) > SendQueue = new ( ) ;
public readonly ConcurrentDictionary < int , int > DropCount = new ( ) ;
public readonly byte [ ] HeartBeat = new byte [ ] { ( byte ) NetCommandType . Heartbeat } ;
public uint TickRate { get ; set ; }
public void Tick ( )
{
throw new NotImplementedException ( ) ;
}
public void HandShake ( )
{
throw new NotImplementedException ( ) ;
}
public async void OnDataInternal ( int id , ArraySegment < byte > bytes , KcpChannel channel )
{
using var ms = new MemoryStream ( bytes . ToArray ( ) ) ;
using var reader = new BinaryReader ( ms ) ;
var type = ( NetCommandType ) ms . ReadByte ( ) ;
var now = DateTime . Now ;
switch ( type )
{
case NetCommandType . Heartbeat :
{
LastHeartbeat . GetOrCreate ( id ) ;
LastHeartbeat [ id ] = now ;
}
break ;
case NetCommandType . Message :
{
var message = reader . ReadString ( ) ;
BIT4Log . Log < INetProvider > ( message ) ;
}
break ;
case NetCommandType . Rpc :
{
var command = ( NetCommandType ) ms . ReadByte ( ) ;
var serviceName = reader . ReadString ( ) ;
var serviceType = BITSharp . GetTypeFromFullName ( serviceName ) ;
var service = ServiceProvider . GetRequiredService ( serviceType ) ;
switch ( command )
{
case NetCommandType . SetPropertyValue :
{
serviceType . GetProperty ( reader . ReadString ( ) ) ! . SetValue ( service , BITBinary . Read ( reader ) ) ;
}
break ;
case NetCommandType . SetFieldValue :
{
serviceType . GetField ( reader . ReadString ( ) ) ! . SetValue ( service , BITBinary . Read ( reader ) ) ;
}
break ;
case NetCommandType . Rpc :
{
var methodInfo = serviceType . GetMethod ( reader . ReadString ( ) ) ! ;
var parameterLength = reader . ReadInt32 ( ) ;
var parameters = new object [ parameterLength ] ;
for ( var i = 0 ; i < parameterLength ; i + + )
{
parameters [ i ] = BITBinary . Read ( reader ) ;
}
var isAwaitable = methodInfo . ReturnType . GetMethod ( nameof ( Task . GetAwaiter ) ) ! = null ;
if ( isAwaitable )
{
dynamic result = methodInfo . Invoke ( service , parameters ) ! ;
object resultValue = null ;
if ( methodInfo . ReturnType = = typeof ( void )
| |
methodInfo . ReturnType = = typeof ( UniTask )
| |
methodInfo . ReturnType = = typeof ( UniTask < > )
)
{
await result ;
resultValue = - 1 ;
}
else
{
resultValue = await result ;
}
}
else
{
methodInfo . Invoke ( service , parameters ) ;
}
}
break ;
}
}
break ;
}
}
public T GetRemoteInterface < T > ( )
{
if ( RemoteInterfaces . TryGetValue ( typeof ( T ) . FullName ! , out var obj ) )
{
return ( T ) obj ;
}
var code = CodeGenerator . Generate ( typeof ( T ) ) ;
var options = ScriptOptions . Default ;
var assemblies = BITSharp . GetReferencedAssemblies ( typeof ( T ) ) ;
foreach ( var referencedAssembly in assemblies )
{
options = options . AddReferences ( referencedAssembly ) ;
}
options = options . AddReferences ( typeof ( BITApp ) . Assembly ) ;
var assembly = BITSharp . Compile ( code , options ) ;
var type = assembly . GetExportedTypes ( ) . First ( x = > typeof ( T ) . IsAssignableFrom ( x ) ) ;
var instance = Activator . CreateInstance ( type ) ! ;
instance . GetType ( ) . GetField ( "NetProvider" ) ! . SetValue ( instance , this ) ;
return ( T ) instance ;
}
public void Invoke ( Memory < byte > bytes )
{
throw new NotImplementedException ( ) ;
}
public async UniTask < Memory < byte > > InvokeAsync ( Memory < byte > bytes )
{
var index = Index + + ;
var source = new UniTaskCompletionSource < Memory < byte > > ( ) ;
P2P . TryAdd ( index , source ) ;
var cancelCts = new CancellationTokenSource ( ) ;
cancelCts . CancelAfter ( Timeout ) ;
return await source . Task . AttachExternalCancellation ( cancelCts . Token ) ;
}
}
2024-11-03 16:42:23 +08:00
}