using System;
using System.Buffers;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Sockets;
using System.Net.Sockets.Kcp;
using System.Threading;
using Fort23.Core;
using Fort23.UTool;
namespace Core.KCPTool
{
public class KCPClient : IKcpCallback, IDisposable, IClientConnection
{
private Socket socket;
private Thread _udpClientThread;
public bool IsConnection { get; set; }
public bool disconnect { get; set; }
public long playerId;
private byte[] buffer = new byte[2048];
private byte[] sendBuffer = new byte[2048];
private Thread _updateThread;
private Thread _receiveThread;
private IClient client;
///
/// 上次心跳时间
///
private long lasetGetHandshakeTime = 0;
public SimpleSegManager.Kcp kcp
{
get { return _kcp; }
}
public IPEndPoint EndPoint { get; set; }
private SimpleSegManager.Kcp _kcp;
private bool isUpdate;
public bool isAwaitReConnect { get; set; }
private CTask _cTask;
// private long _connectTime;
public KCPClient()
{
}
public async CTask Connect(IPEndPoint endPoint, long playerId, IClient client)
{
try
{
_cTask = CTask.Create(false);
this.client = client;
this.playerId = playerId;
isUpdate = true;
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Connect(endPoint);
_kcp = new SimpleSegManager.Kcp(0, this);
kcp.NoDelay(1, 40, 2, 1);
this.EndPoint = endPoint;
_udpClientThread = new Thread(BeginRecv);
_udpClientThread.Start();
IsConnection = false;
disconnect = false;
// _connectTime = System.DateTime.Now.Ticks;
byte[] data = SocketTool.LongToByte(playerId);
socket.Send(data);
// SendToServer(SendDataType.ShakeHands, data);
_updateThread = new Thread(Update);
_receiveThread = new Thread(ReceiveAsyncThread);
_updateThread.Start();
_receiveThread.Start();
lasetGetHandshakeTime = 0;
await _cTask;
}
catch (Exception e)
{
disconnect = true;
LogTool.Exception(e);
throw;
}
}
public void ReConnect(IPEndPoint endPoint, int gameFrame)
{
// LogTool.Log("重新链接");
// if (socket != null)
// {
// socket.Dispose();
// socket = null;
// }
//
// lasetGetHandshakeTime = 0;
// // _udpClientThread = null;
// Thread.Sleep(10);
// socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
// socket.Connect(endPoint);
// this.EndPoint = endPoint;
// byte[] data = SocketTool.LongToByte(playerId);
// socket.Send(data);
// // SendToServer(SendDataType.ShakeHands, data);
// IsConnection = false;
// disconnect = false;
// isAwaitReConnect = true;
byte[] data = SocketTool.LongToByte(playerId);
byte[] gameFrameByte = SocketTool.IntToByte(gameFrame);
byte[] buffer = new byte[data.Length + gameFrameByte.Length];
Array.Copy(data, 0, buffer, 0, data.Length);
Array.Copy(gameFrameByte, 0, buffer, data.Length, gameFrameByte.Length);
SendToServer(SendDataType.GetGameFrameByte, buffer);
}
public void Output(IMemoryOwner buffer, int avalidLength)
{
if (EndPoint == null || disconnect)
{
return;
}
var s = buffer.Memory.Span.Slice(0, avalidLength).ToArray();
if (socket == null)
{
return;
}
try
{
socket.Send(s);
}
catch (Exception e)
{
// disconnect = true;
// disconnect = true;
// IsConnection = false;
if (socket != null)
{
socket.Dispose();
socket = null;
}
}
buffer.Dispose();
}
public void SendToServer(SendDataType sendDataType, byte[] buffer)
{
byte[] sendBuff = new byte[buffer.Length + 1];
sendBuff[0] = (byte)sendDataType;
Array.Copy(buffer, 0, sendBuff, 1, buffer.Length);
int c = kcp.Send(sendBuff.AsSpan().Slice(0, sendBuff.Length));
}
public void Finish()
{
}
public byte[] ReceiveAsync()
{
var (buffer, avalidLength) = kcp.TryRecv();
while (buffer == null)
{
Thread.Sleep(5);
if (_udpClientThread == null)
{
return null;
}
(buffer, avalidLength) = kcp.TryRecv();
}
var s = buffer.Memory.Span.Slice(0, avalidLength).ToArray();
return s;
}
private async void BeginRecv()
{
while (isUpdate)
{
try
{
if (socket == null)
{
Thread.Sleep(3);
continue;
}
if (socket.Receive(buffer, SocketFlags.Peek) <= 0)
{
Thread.Sleep(3);
continue;
}
int count = socket.Receive(buffer, SocketFlags.None);
if (count <= 0)
{
Thread.Sleep(3);
continue;
}
try
{
byte[] data = new byte[count];
Array.Copy(buffer, 0, data, 0, data.Length);
int ok = kcp.Input(data);
}
catch (Exception e)
{
LogTool.Exception(e);
}
}
catch (Exception e)
{
LogTool.Log("链接断开");
// socket.Dispose();
socket = null;
disconnect = true;
IsConnection = false;
}
Thread.Sleep(3);
}
LogTool.Log("执行完成kcp_BeginRecv");
}
private void Update()
{
while (isUpdate)
{
try
{
kcp.Update(DateTimeOffset.UtcNow);
if (IsConnection)
{
if (DateTime.Now.Ticks - lasetGetHandshakeTime > 70000000) //断开了链接
{
disconnect = true;
IsConnection = false;
return;
}
}
Thread.Sleep(5);
}
catch (Exception e)
{
LogTool.Error(e);
}
}
LogTool.Log("执行完成kcp_Update");
}
private void ReceiveAsyncThread()
{
while (isUpdate)
{
try
{
var res = ReceiveAsync();
if (res == null)
{
Thread.Sleep(10);
continue;
}
SendDataType sendDataType = (SendDataType)res[0];
byte[] decompressedByte = null;
if (sendDataType == SendDataType.Data)
{
byte[] jmData = new byte[res.Length - 1];
Array.Copy(res, 1, jmData, 0, jmData.Length);
using (MemoryStream compressedStream = new MemoryStream(jmData))
{
using (MemoryStream decompressedStream = new MemoryStream())
{
using (GZipStream gzipStream =
new GZipStream(compressedStream, CompressionMode.Decompress))
{
gzipStream.CopyTo(decompressedStream);
}
decompressedByte = decompressedStream.ToArray();
}
}
}
else
{
decompressedByte = res;
}
switch (sendDataType)
{
case SendDataType.ShakeHands:
byte[] playerData = new byte[8];
playerData[0] = decompressedByte[1];
playerData[1] = decompressedByte[2];
playerData[2] = decompressedByte[3];
playerData[3] = decompressedByte[4];
playerData[4] = decompressedByte[5];
playerData[5] = decompressedByte[6];
playerData[6] = decompressedByte[7];
playerData[7] = decompressedByte[8];
long playerId = SocketTool.ByteToInt(playerData);
if (playerId != this.playerId)
{
LogTool.Error("握手消息不对");
}
IsConnection = true;
isAwaitReConnect = false;
if (lasetGetHandshakeTime == 0)
{
lasetGetHandshakeTime = DateTime.Now.Ticks;
}
_cTask.SetResult();
break;
case SendDataType.Heartbeat:
if (!IsConnection)
{
LogTool.Log("链接成功");
}
isAwaitReConnect = false;
IsConnection = true;
lasetGetHandshakeTime = DateTime.Now.Ticks;
// LogTool.Log("收到心跳包");
break;
case SendDataType.Data:
// CombatSynchronizeResponsePack combatSynchronizeResponse =
// CombatSynchronizeResponsePack.Parser.ParseFrom(decompressedByte);
// if (combatSynchronizeResponse == null)
// {
// LogTool.Error("错误的实例化战斗" + decompressedByte.Length);
// }
// else
// {
// //
// long t = System.DateTime.Now.Ticks;
// // = (int) ((t - lasetTime) / 10000);
// // lasetTime = t;
// if (!IsConnection)
// {
// LogTool.Log("链接成功");
// }
//
// if (lasetGetHandshakeTime == 0)
// {
// lasetGetHandshakeTime = DateTime.Now.Ticks;
// }
//
// isAwaitReConnect = false;
// IsConnection = true;
// // LogTool.Log("收包延迟" + (t - combatSynchronizeResponse.Time) / 10000);
// client.AddCombatSynchronizeResponse(combatSynchronizeResponse);
// }
break;
}
// await Task.Delay(Random.Next(0,200));
}
catch (Exception e)
{
LogTool.Error(e);
}
}
}
public void Dispose()
{
isUpdate = false;
// _udpClientThread?.Abort();
_udpClientThread = null;
socket?.Dispose();
kcp?.Dispose();
socket = null;
_kcp = null;
}
}
}