2024-11-03 16:42:23 +08:00
|
|
|
using System;
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Diagnostics.Tracing;
|
|
|
|
using System.Timers;
|
|
|
|
using Cysharp.Threading.Tasks;
|
|
|
|
using kcp2k;
|
|
|
|
using Timer = System.Timers.Timer;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
using System.IO;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Net;
|
|
|
|
using System.Numerics;
|
|
|
|
using System.Reflection;
|
|
|
|
using System.Text;
|
2025-02-24 23:03:39 +08:00
|
|
|
using System.Threading;
|
2024-11-03 16:42:23 +08:00
|
|
|
using BITKit.Net.Examples;
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
using Unity.Mathematics;
|
|
|
|
|
|
|
|
namespace BITKit.Net
|
|
|
|
{
|
|
|
|
public class KcpNetClient:INetClient,INetProvider
|
|
|
|
{
|
2025-02-24 23:03:39 +08:00
|
|
|
private readonly NetProviderCommon _common = new();
|
2024-11-03 16:42:23 +08:00
|
|
|
private readonly ILogger<KcpNetClient> _logger;
|
|
|
|
public KcpNetClient(ILogger<KcpNetClient> logger)
|
|
|
|
{
|
|
|
|
_logger = logger;
|
|
|
|
_client = new KcpClient(
|
|
|
|
OnConnectedInternal,
|
|
|
|
OnData,
|
|
|
|
OnDisconnectInternal,
|
|
|
|
OnError,
|
|
|
|
KCPNet.Config
|
|
|
|
);
|
|
|
|
_timer.Elapsed += Tick;
|
|
|
|
_logger.LogInformation("已创建KCP客户端");
|
|
|
|
_isConnected.AddListener(ConnectionCallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
public event Action OnStartConnect;
|
|
|
|
public event Action OnConnected;
|
|
|
|
public event Action OnDisconnected;
|
|
|
|
public event Action OnConnectedFailed;
|
2025-02-24 23:03:39 +08:00
|
|
|
public uint TickRate { get; set; } = 8;
|
2024-11-03 16:42:23 +08:00
|
|
|
public bool IsConnected => _isConnected;
|
|
|
|
public bool IsConnecting { get; private set; }
|
|
|
|
public bool AutoReconnect { get; set; } = true;
|
|
|
|
public float2 Traffic { get; private set; }
|
|
|
|
public bool ManualTick { get; set; }
|
|
|
|
|
|
|
|
private readonly IntervalUpdate _reconnectInterval = new(1);
|
|
|
|
|
|
|
|
public int Ping { get; private set; }
|
|
|
|
public int Id { get; private set; } = -1;
|
|
|
|
private readonly KcpClient _client;
|
|
|
|
|
|
|
|
private readonly ConcurrentQueue<byte[]> _commandQueue = new();
|
|
|
|
|
|
|
|
private DateTime _lastPingTime = DateTime.Now;
|
|
|
|
|
|
|
|
private readonly Timer _timer = new(100)
|
|
|
|
{
|
|
|
|
AutoReset = true
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
private readonly ValidHandle _isConnected = new();
|
|
|
|
private bool _userConnected;
|
|
|
|
private int _index = int.MinValue;
|
2025-02-24 23:03:39 +08:00
|
|
|
|
2024-11-03 16:42:23 +08:00
|
|
|
private DateTime _now = DateTime.Now;
|
|
|
|
private TimeSpan _interval = TimeSpan.FromMilliseconds(100);
|
|
|
|
private string _connectedAddress = "127.0.0.1";
|
|
|
|
private ushort _connectedPort = 27014;
|
|
|
|
|
|
|
|
private async void ConnectionCallback(bool x)
|
|
|
|
{
|
|
|
|
await BITApp.SwitchToMainThread();
|
|
|
|
if (x)
|
|
|
|
{
|
|
|
|
OnConnected?.Invoke();
|
|
|
|
_logger.LogInformation("连接成功");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
OnDisconnected?.Invoke();
|
|
|
|
_logger.LogInformation("连接已断开");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void Tick(object sender, ElapsedEventArgs e)
|
|
|
|
{
|
|
|
|
if (!ManualTick)
|
|
|
|
Tick();
|
|
|
|
}
|
|
|
|
|
|
|
|
public async void Disconnect()
|
|
|
|
{
|
|
|
|
_userConnected = false;
|
|
|
|
DisconnectInternal();
|
|
|
|
}
|
|
|
|
private async void DisconnectInternal()
|
|
|
|
{
|
|
|
|
IsConnecting = false;
|
|
|
|
_client.Disconnect();
|
|
|
|
_isConnected.RemoveElement(this);
|
|
|
|
_timer.Stop();
|
|
|
|
try
|
|
|
|
{
|
|
|
|
await BITApp.SwitchToMainThread();
|
|
|
|
OnDisconnected?.Invoke();
|
|
|
|
}
|
|
|
|
catch (OperationCanceledException){}
|
|
|
|
}
|
|
|
|
|
|
|
|
private string _lastHostName;
|
|
|
|
public async UniTask<bool> Connect(string address = "127.0.0.1", ushort port = 27014)
|
|
|
|
{
|
|
|
|
if (IsConnecting)
|
|
|
|
{
|
2025-02-24 23:03:39 +08:00
|
|
|
_logger.LogWarning("正在连接中");
|
2024-11-03 16:42:23 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
_userConnected = true;
|
2025-02-24 23:03:39 +08:00
|
|
|
|
2024-11-03 16:42:23 +08:00
|
|
|
//如果address是域名,解析为Ip
|
|
|
|
if (address.Contains("."))
|
|
|
|
{
|
|
|
|
var ip = await Dns.GetHostAddressesAsync(address);
|
|
|
|
if (ip.Length > 0)
|
|
|
|
{
|
|
|
|
address = ip[0].ToString();
|
|
|
|
if (_lastHostName != address)
|
|
|
|
{
|
|
|
|
_logger.LogInformation($"解析域名:{address},IP:{ip}");
|
|
|
|
_lastHostName = address;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (address is not "127.0.0.1")
|
|
|
|
_connectedAddress = address;
|
|
|
|
if (port is not 27014)
|
|
|
|
_connectedPort = port;
|
|
|
|
|
|
|
|
|
|
|
|
IsConnecting = true;
|
|
|
|
if (_client.connected) return false;
|
|
|
|
await BITApp.SwitchToMainThread();
|
|
|
|
OnStartConnect?.Invoke();
|
|
|
|
await UniTask.SwitchToThreadPool();
|
|
|
|
try
|
|
|
|
{
|
2025-02-24 23:03:39 +08:00
|
|
|
_common.LastHeartbeat.GetOrCreate(0);
|
|
|
|
_common.LastHeartbeat[0] = DateTime.Now;
|
|
|
|
|
2024-11-03 16:42:23 +08:00
|
|
|
_client.Connect(address, port);
|
|
|
|
|
|
|
|
_timer.Start();
|
|
|
|
_interval = TimeSpan.FromMilliseconds(_timer.Interval);
|
|
|
|
|
2025-02-24 23:03:39 +08:00
|
|
|
if (_client.connected)
|
|
|
|
{
|
|
|
|
HandShake();
|
|
|
|
_client.Send(new[] { (byte)NetCommandType.Heartbeat }, KcpChannel.Reliable);
|
|
|
|
Traffic += new float2(1, 0);
|
|
|
|
}
|
2024-11-03 16:42:23 +08:00
|
|
|
_client.Tick();
|
|
|
|
await Task.Delay(100);
|
|
|
|
|
|
|
|
if (_client.connected)
|
|
|
|
{
|
|
|
|
SendServerMessage(Environment.MachineName);
|
|
|
|
|
|
|
|
IsConnecting = false;
|
|
|
|
|
|
|
|
_connectedAddress = address;
|
|
|
|
_connectedPort = port;
|
2025-02-24 23:03:39 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
2024-11-03 16:42:23 +08:00
|
|
|
return _client.connected;
|
|
|
|
}
|
|
|
|
|
|
|
|
OnConnectedFailed?.Invoke();
|
|
|
|
DisconnectInternal();
|
|
|
|
|
|
|
|
IsConnecting = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
2025-02-24 23:03:39 +08:00
|
|
|
_logger.LogCritical(e,e.Message);
|
2024-11-03 16:42:23 +08:00
|
|
|
if (BITApp.SynchronizationContext is not null)
|
|
|
|
await UniTask.SwitchToSynchronizationContext(BITApp.SynchronizationContext);
|
|
|
|
OnConnectedFailed?.Invoke();
|
|
|
|
_timer.Stop();
|
|
|
|
|
|
|
|
IsConnecting = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void OnData(ArraySegment<byte> bytes, KcpChannel channel)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Traffic+=new float2(0,bytes.Count);
|
2025-02-24 23:03:39 +08:00
|
|
|
OnDataInternal(bytes, channel);
|
2024-11-03 16:42:23 +08:00
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
2025-02-24 23:03:39 +08:00
|
|
|
_logger.LogCritical(e,e.Message);
|
2024-11-03 16:42:23 +08:00
|
|
|
}
|
|
|
|
}
|
2025-02-24 23:03:39 +08:00
|
|
|
private void OnDataInternal(ArraySegment<byte> bytes, KcpChannel channel)
|
2024-11-03 16:42:23 +08:00
|
|
|
{
|
2025-02-24 23:03:39 +08:00
|
|
|
try
|
2024-11-03 16:42:23 +08:00
|
|
|
{
|
2025-02-24 23:03:39 +08:00
|
|
|
_common.OnDataInternal(0,bytes,channel);
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
_logger.LogCritical(e,e.Message);
|
2024-11-03 16:42:23 +08:00
|
|
|
}
|
2025-02-24 23:03:39 +08:00
|
|
|
|
2024-11-03 16:42:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private async void OnConnectedInternal()
|
|
|
|
{
|
2025-02-24 23:03:39 +08:00
|
|
|
|
2024-11-03 16:42:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private async void OnDisconnectInternal()
|
|
|
|
{
|
|
|
|
DisconnectInternal();
|
|
|
|
}
|
|
|
|
private void OnError(ErrorCode errorCode, string message)
|
|
|
|
{
|
|
|
|
_logger.LogInformation($"{_client.remoteEndPoint}异常:{errorCode},{message}");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void SendServerMessage(string message)
|
|
|
|
{
|
|
|
|
var bytes = BinaryBuilder
|
|
|
|
.Create()
|
|
|
|
.Write(((byte)NetCommandType.Message))
|
|
|
|
.Write(message)
|
|
|
|
.Build();
|
|
|
|
Traffic+=new float2(bytes.Length,0);
|
|
|
|
_client.Send(bytes, KcpChannel.Reliable);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Tick()
|
|
|
|
{
|
|
|
|
_now = DateTime.Now;
|
|
|
|
if(_userConnected is false)return;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (_client.connected)
|
|
|
|
{
|
2025-02-24 23:03:39 +08:00
|
|
|
if (DateTime.Now - _common.LastHeartbeat.GetOrCreate(0) > TimeSpan.FromSeconds(5))
|
2024-11-03 16:42:23 +08:00
|
|
|
{
|
2025-02-24 23:03:39 +08:00
|
|
|
_logger.LogWarning("心跳超时,自动断开");
|
2024-11-03 16:42:23 +08:00
|
|
|
DisconnectInternal();
|
|
|
|
_commandQueue.Clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
while (_commandQueue.TryDequeue(out var bytes))
|
|
|
|
{
|
|
|
|
Traffic += new float2(bytes.Length, 0);
|
|
|
|
_client.Send(bytes, KcpChannel.Reliable);
|
|
|
|
}
|
|
|
|
Traffic+=new float2(1,0);
|
2025-02-24 23:03:39 +08:00
|
|
|
_client.Send(_common.HeartBeat, KcpChannel.Unreliable);
|
2024-11-03 16:42:23 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (AutoReconnect && _reconnectInterval.AllowUpdate)
|
|
|
|
{
|
|
|
|
if (IsConnecting is false)
|
|
|
|
{
|
|
|
|
Connect(_connectedAddress,_connectedPort).Forget();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_commandQueue.Count > 0)
|
|
|
|
{
|
|
|
|
_commandQueue.Clear();
|
|
|
|
//BIT4Log.Warning<KcpNetClient>("连接已断开,清空指令队列");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_client.Tick();
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
2025-02-24 23:03:39 +08:00
|
|
|
_logger.LogCritical(e,e.Message);
|
2024-11-03 16:42:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public void HandShake()
|
|
|
|
{
|
|
|
|
// send client to server
|
|
|
|
Traffic+=new float2(2,0);
|
|
|
|
_client.Send(new byte[]{0x01, 0x02}, KcpChannel.Reliable);
|
|
|
|
}
|
2025-02-24 23:03:39 +08:00
|
|
|
public T GetRemoteInterface<T>() => _common.GetRemoteInterface<T>();
|
|
|
|
public void Invoke(Memory<byte> bytes)
|
|
|
|
{
|
|
|
|
throw new NotImplementedException();
|
|
|
|
}
|
|
|
|
|
|
|
|
public UniTask<Memory<byte>> InvokeAsync(Memory<byte> bytes)
|
|
|
|
{
|
|
|
|
throw new NotImplementedException();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Invoke(IReadOnlyList<byte> bytes)
|
|
|
|
{
|
|
|
|
throw new NotImplementedException();
|
|
|
|
}
|
|
|
|
|
|
|
|
public UniTask<IReadOnlyList<byte>> InvokeAsync(IReadOnlyList<byte> bytes)
|
|
|
|
{
|
|
|
|
throw new NotImplementedException();
|
|
|
|
}
|
2024-11-03 16:42:23 +08:00
|
|
|
}
|
|
|
|
}
|