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; } } }