using System; using System.Collections.Generic; using System.Timers; using Cysharp.Threading.Tasks; using kcp2k; using Timer = System.Timers.Timer; using System.Threading.Tasks; using System.IO; using System.Numerics; using BITKit.Net.Examples; using Newtonsoft.Json; namespace BITKit.Net { public class KcpNetClient:INetClient,INetProvider { public event Action OnStartConnect; public event Action OnConnected; public event Action OnDisconnected; public event Action OnConnectedFailed; public bool IsConnected => client.connected; public int Ping => -1; public int Id { get; private set; } = -1; private readonly KcpClient client; private readonly Queue commandQueue = new(); private readonly Timer _timer = new(100) { AutoReset = true }; private readonly GenericEvent _events = new(); public KcpNetClient() { client = new KcpClient( OnConnectedInternal, OnData, OnDisconnectInternal, OnError, KCPNet.Config ); _timer.Elapsed += Tick; BIT4Log.Log("已创建KCP客户端"); AddCommandListener(x => { Id = x.Id; }); } private void Tick(object sender, ElapsedEventArgs e) { //await UniTask.SwitchToThreadPool(); while (commandQueue.TryDequeue(out var bytes)) { client.Send(bytes, KcpChannel.Reliable); } //for (var i = 0; i < 32; i++) { client.Tick(); } } public async void Disconnect() { client.Disconnect(); try { await UniTask.SwitchToSynchronizationContext(BITApp.SynchronizationContext,BITApp.CancellationToken); OnDisconnected?.Invoke(); } catch (OperationCanceledException){} } public async UniTask Connect(string address = "127.0.0.1", ushort port = 27014) { if (BITApp.SynchronizationContext is not null) await UniTask.SwitchToSynchronizationContext(BITApp.SynchronizationContext, BITApp.CancellationToken); OnStartConnect?.Invoke(); await UniTask.SwitchToThreadPool(); try { client.Connect(address, port); for (var i = 0; i < 5; i++) { client.Tick(); await Task.Delay(100); } _timer.Start(); client.Send(new[] { (byte)NetCommandType.Heartbeat }, KcpChannel.Reliable); if (BITApp.SynchronizationContext is not null) await UniTask.SwitchToSynchronizationContext(BITApp.SynchronizationContext); OnConnected?.Invoke(); if (client.connected) { SendServerMessage(Environment.MachineName); } return client.connected; } catch (Exception e) { BIT4Log.LogException(e); if (BITApp.SynchronizationContext is not null) await UniTask.SwitchToSynchronizationContext(BITApp.SynchronizationContext); OnConnectedFailed?.Invoke(); return false; } } private async void OnData(ArraySegment bytes, KcpChannel channel) { using var ms = new MemoryStream(bytes.ToArray()); using var reader = new BinaryReader(ms); var type = (NetCommandType)ms.ReadByte(); switch (type) { case NetCommandType.Message: reader.ReadBoolean(); reader.ReadString(); BIT4Log.Log($"已收到消息:{reader.ReadString()}"); break; case NetCommandType.AllClientCommand: case NetCommandType.Command: var command = BITBinary.Read(reader); if (command is object[] { Length: 1 } objs) command = objs[0]; //BIT4Log.Log($"已收到指令:{command},值:\n{JsonConvert.SerializeObject(command, Formatting.Indented)}"); try { if (BITApp.SynchronizationContext is not null) await UniTask.SwitchToSynchronizationContext(BITApp.SynchronizationContext, BITApp.CancellationToken); _events.Invoke(command.GetType().FullName, command); } catch (OperationCanceledException) { } catch (Exception e) { BIT4Log.LogException(e); } break; case NetCommandType.Heartbeat: client.Send(new[] { (byte)NetCommandType.Heartbeat }, KcpChannel.Reliable); break; default: BIT4Log.Log($"未知消息类型:{type},字节:{(byte)type}"); if (bytes.Array != null) BIT4Log.Log( $"已收到:({Id}, {BitConverter.ToString(bytes.Array, bytes.Offset, bytes.Count)} @ {channel})"); break; } } private async void OnConnectedInternal() { if (BITApp.SynchronizationContext is not null) await UniTask.SwitchToSynchronizationContext(BITApp.SynchronizationContext); OnConnected?.Invoke(); BIT4Log.Log("已连接"); } private async void OnDisconnectInternal() { BIT4Log.Log("断开连接"); _timer.Stop(); try { if (BITApp.SynchronizationContext is not null) await UniTask.SwitchToSynchronizationContext(BITApp.SynchronizationContext, BITApp.CancellationToken); OnDisconnected?.Invoke(); } catch (OperationCanceledException) { } } private void OnError(ErrorCode errorCode, string message) { BIT4Log.Log($"{client.remoteEndPoint}异常:{errorCode},{message}"); } public void ServerCommand(T command = default) { Send(NetCommandType.Command,command); } public void AllClientCommand(T command = default) { Send(NetCommandType.AllClientCommand,command); } public void ClientCommand(int id, T command) { Send(NetCommandType.TargetCommand,id,command); } public UniTask GetFromServer(string addressablePath = Constant.System.Internal) { throw new NotImplementedException(); } public UniTask GetFromClient(int id, string addressablePath = Constant.System.Internal) { throw new NotImplementedException(); } public void AddRpcHandle(object rpcHandle) { throw new NotImplementedException(); } public void AddCommandListener(Action handle) { _events.AddListener(handle); } public void RemoveCommandListener(Action handle) { throw new NotImplementedException(); } public void SendRT(string rpcName, params object[] pars) { throw new NotImplementedException(); } public void SendTargetRT(int id, string rpcName, params object[] pars) { throw new NotImplementedException(); } public void SendAllRT(string rpcName, params object[] pars) { throw new NotImplementedException(); } public void SendServerMessage(string message) { var bytes = BinaryBuilder .Create() .Write(((byte)NetCommandType.Message)) .Write(message) .Build(); client.Send(bytes, KcpChannel.Reliable); } public void Tick() => client.Tick(); public void HandShake() { // send client to server client.Send(new byte[]{0x01, 0x02}, KcpChannel.Reliable); } private void Send(NetCommandType commandType,params object[] values) { var bytes = BinaryBuilder .Create() .Write((byte)commandType) .WriteObject(values) .Build(); commandQueue.Enqueue(bytes); //client.Send(bytes, KcpChannel.Reliable); } } }