/* * 该接口为基础的网络接口,包括了网络服务,服务端接口,客户端接口的基本定义 e.g. * ⭐INetProvider 网络通信接口的基本定义 * ⭐INetServer 服务端接口的基本定义 * ⭐INetClient 客户端接口的基本定义 */ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using Cysharp.Threading.Tasks; using kcp2k; using Microsoft.CodeAnalysis.Scripting; using Microsoft.Extensions.DependencyInjection; namespace BITKit { /// /// 帮助类 /// public static class NetUtils { /// /// 计算文件大小函数(保留两位小数),Size为字节大小 /// /// 初始文件大小 /// 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 } } /// /// 网络指令类型 /// public enum NetCommandType:byte { Undefined=0, Message=1, Heartbeat=2, Rpc, SetPropertyValue, SetFieldValue, WaitTask, ReturnValue, } /// /// 网络提供服务,包括了基础网络服务,e.g /// ⭐向服务器发送指令 /// ⭐向所有客户端发送指令 /// ⭐向单个客户端发送指令 /// ⭐监听与取消监听网络命令 /// ⭐从服务器获取数据 /// ⭐从客户端获取数据 /// ⭐添加Rpc处理服务 /// ⭐向服务器发送Rpc /// ⭐向所有客户端发送Rpc /// public interface INetProvider { public uint TickRate { get; set; } /// /// 更新Tick /// void Tick(); /// /// 连接协议握手 /// void HandShake(); /// /// 获取远程接口 /// /// /// T GetRemoteInterface(); /// /// 远程调用 /// void Invoke(Memory bytes); /// /// 异步远程调用,有返回结果 /// /// /// /// UniTask> InvokeAsync(Memory bytes); } /// /// 服务端接口,支持服务端的基本功能 /// ⭐开启服务器(支持指定端口) /// ⭐停止服务器 /// ⭐服务器运行状态 /// public interface INetServer { /// /// 通信接口 /// public INetProvider NetProvider=>this as INetProvider; /// /// 源物体,用于通过代理直接访问 /// public object Source => this; /// /// 手动Tick /// public bool ManualTick { get; set; } /// /// 回调:当客户端连接时 /// public event Action OnClientConnected; /// /// 回调:当客户端断开连接时 /// public event Action OnClientDisconnected; /// /// 开启服务端的回调 /// public event Action OnStartServer; /// /// 关闭服务端的回调 /// public event Action OnStopServer; /// /// 运行服务端 /// /// 端口,默认为27014 void StartServer(ushort port = 27014); /// /// 停止服务端 /// /// 可选参数:强行释放,默认为false void StopServer(bool dispose=false); /// /// (只读)服务器是否正在运行 /// bool IsRunningServer { get; } /// /// 向单个链接发送消息 /// /// /// void SendMessageToClient(int id, string message); /// /// 向全部链接发送消息 /// /// void SendMessageToAll(string message); /// /// 所有已连接的客户端 /// public IDictionary Connections { get; } /// /// 踢出客户端 /// /// void KickClient(int id); } /// /// 基本网络客户端的接口定义,包括了基本客户端的功能 /// ⭐基本客户端回调 /// ⭐是否已连接到服务器 /// ⭐连接服务端的延迟 /// ⭐客户端Id /// ⭐开启客户端 /// ⭐关闭客户端 /// public interface INetClient { /// /// 通讯接口 /// public INetProvider NetProvider=>this as INetProvider; //基本客户端回调 public event Action OnStartConnect; public event Action OnConnected; public event Action OnDisconnected; public event Action OnConnectedFailed; /// /// 是否已连接到服务端 /// bool IsConnected { get; } /// /// 手动Tick /// bool ManualTick { get; set; } /// /// 连接服务端的延迟 /// int Ping { get; } /// /// 客户端Id /// int Id { get; } /// /// 断开链接 /// void Disconnect(); /// /// 异步开始链接 /// /// 服务端地址 /// 端口 /// UniTask Connect(string address = "127.0.0.1", ushort port = 27014); /// /// 向服务端发送消息 /// /// 消息 void SendServerMessage(string message); } 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 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>> P2P = new(); public readonly ConcurrentDictionary LastHeartbeat = new(); public readonly ConcurrentDictionary RemoteInterfaces = new(); public readonly ConcurrentDictionary LocalServices = new(); public readonly ConcurrentQueue<(int id,byte[] bytes)> SendQueue = new(); public readonly ConcurrentDictionary 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 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(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() { 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 bytes) { throw new NotImplementedException(); } public async UniTask> InvokeAsync(Memory bytes) { var index = Index++; var source = new UniTaskCompletionSource>(); P2P.TryAdd(index, source); var cancelCts = new CancellationTokenSource(); cancelCts.CancelAfter(Timeout); return await source.Task.AttachExternalCancellation(cancelCts.Token); } } }