tanghai 8 жил өмнө
parent
commit
137112db76
27 өөрчлөгдсөн 1584 нэмэгдсэн , 36 устгасан
  1. 1 0
      Client-Server.sln.DotSettings.user
  2. 4 0
      Server/Base/Server.Base.csproj
  3. 4 3
      Server/Model/Component/BenchmarkComponent.cs
  4. 1 1
      Server/Server.sln
  5. 18 0
      Unity/Assets/Scripts/Base/Helper/ByteHelper.cs
  6. 3 8
      Unity/Assets/Scripts/Base/Helper/TimeHelper.cs
  7. 4 4
      Unity/Assets/Scripts/Base/Network/AChannel.cs
  8. 4 1
      Unity/Assets/Scripts/Base/Network/AService.cs
  9. 9 0
      Unity/Assets/Scripts/Base/Network/KNet.meta
  10. 208 0
      Unity/Assets/Scripts/Base/Network/KNet/KChannel.cs
  11. 12 0
      Unity/Assets/Scripts/Base/Network/KNet/KChannel.cs.meta
  12. 229 0
      Unity/Assets/Scripts/Base/Network/KNet/KService.cs
  13. 12 0
      Unity/Assets/Scripts/Base/Network/KNet/KService.cs.meta
  14. 997 0
      Unity/Assets/Scripts/Base/Network/KNet/Kcp.cs
  15. 12 0
      Unity/Assets/Scripts/Base/Network/KNet/Kcp.cs.meta
  16. 34 1
      Unity/Assets/Scripts/Base/Network/TNet/TBuffer.cs
  17. 6 6
      Unity/Assets/Scripts/Base/Network/TNet/TChannel.cs
  18. 1 1
      Unity/Assets/Scripts/Base/Network/TNet/TService.cs
  19. 4 4
      Unity/Assets/Scripts/Base/Network/UNet/UChannel.cs
  20. 6 1
      Unity/Assets/Scripts/Base/Network/UNet/UService.cs
  21. 6 0
      Unity/Assets/Scripts/Component/NetworkComponent.cs
  22. 2 2
      Unity/Assets/Scripts/Entity/Session.cs
  23. 0 0
      Unity/Unity.Editor.Plugins.csproj
  24. 0 0
      Unity/Unity.Editor.csproj
  25. 0 0
      Unity/Unity.Plugins.csproj
  26. 6 3
      Unity/Unity.csproj
  27. 1 1
      Unity/Unity.sln.DotSettings.user

+ 1 - 0
Client-Server.sln.DotSettings.user

@@ -8,6 +8,7 @@
 	<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUnitComponent_002Ecs_002Fl_003AUnity_003FAssets_003FScripts_003FComponent_003FUnitComponent_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
 	<s:String x:Key="/Default/CodeInspection/Highlighting/AnalysisEnabled/@EntryValue">VISIBLE_FILES</s:String>
 	<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=Unity/@EntryIndexedValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;Profile name="Unity"&gt;&lt;CSArrangeThisQualifier&gt;True&lt;/CSArrangeThisQualifier&gt;&lt;CSRemoveCodeRedundancies&gt;True&lt;/CSRemoveCodeRedundancies&gt;&lt;CSMakeFieldReadonly&gt;True&lt;/CSMakeFieldReadonly&gt;&lt;CSUseVar&gt;&lt;BehavourStyle&gt;DISABLED&lt;/BehavourStyle&gt;&lt;LocalVariableStyle&gt;IMPLICIT_WHEN_INITIALIZER_HAS_TYPE&lt;/LocalVariableStyle&gt;&lt;ForeachVariableStyle&gt;IMPLICIT_EXCEPT_SIMPLE_TYPES&lt;/ForeachVariableStyle&gt;&lt;/CSUseVar&gt;&lt;CSOptimizeUsings&gt;&lt;OptimizeUsings&gt;True&lt;/OptimizeUsings&gt;&lt;EmbraceInRegion&gt;False&lt;/EmbraceInRegion&gt;&lt;RegionName&gt;&lt;/RegionName&gt;&lt;/CSOptimizeUsings&gt;&lt;CSShortenReferences&gt;True&lt;/CSShortenReferences&gt;&lt;CSReformatCode&gt;True&lt;/CSReformatCode&gt;&lt;CSMakeAutoPropertyGetOnly&gt;True&lt;/CSMakeAutoPropertyGetOnly&gt;&lt;CSUseAutoProperty&gt;True&lt;/CSUseAutoProperty&gt;&lt;RemoveCodeRedundancies&gt;True&lt;/RemoveCodeRedundancies&gt;&lt;CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="True" ArrangeArgumentsStyle="True" ArrangeCodeBodyStyle="True" /&gt;&lt;/Profile&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/CodeCleanup/RecentlyUsedProfile/@EntryValue">Unity</s:String>
 	<s:String x:Key="/Default/Housekeeping/Layout/DialogWindows/RefactoringWizardWindow/Location/@EntryValue">247,-4</s:String>
 	<s:Int64 x:Key="/Default/Environment/SearchAndNavigation/DefaultOccurrencesGroupingIndex/@EntryValue">0</s:Int64>
 	<s:String x:Key="/Default/Profiling/Configurations/=1/@EntryIndexedValue">&lt;data&gt;&lt;HostParameters type="LocalHostParameters" /&gt;&lt;Argument type="StandaloneArgument"&gt;&lt;Arguments IsNull="False"&gt;&lt;/Arguments&gt;&lt;FileName IsNull="False"&gt;&lt;/FileName&gt;&lt;WorkingDirectory IsNull="False"&gt;&lt;/WorkingDirectory&gt;&lt;Scope&gt;&lt;ProcessFilters /&gt;&lt;/Scope&gt;&lt;/Argument&gt;&lt;Info type="TimelineInfo" /&gt;&lt;HostOptions type="HostOptions"&gt;&lt;HostTempPath IsNull="False"&gt;&lt;/HostTempPath&gt;&lt;/HostOptions&gt;&lt;/data&gt;</s:String>

+ 4 - 0
Server/Base/Server.Base.csproj

@@ -35,6 +35,9 @@
     <Compile Include="..\..\Unity\Assets\Scripts\Base\MultiMap.cs" Link="MultiMap.cs" />
     <Compile Include="..\..\Unity\Assets\Scripts\Base\Network\AChannel.cs" Link="Network\AChannel.cs" />
     <Compile Include="..\..\Unity\Assets\Scripts\Base\Network\AService.cs" Link="Network\AService.cs" />
+    <Compile Include="..\..\Unity\Assets\Scripts\Base\Network\KNet\KChannel.cs" Link="Network\KNet\KChannel.cs" />
+    <Compile Include="..\..\Unity\Assets\Scripts\Base\Network\KNet\Kcp.cs" Link="Network\KNet\Kcp.cs" />
+    <Compile Include="..\..\Unity\Assets\Scripts\Base\Network\KNet\KService.cs" Link="Network\KNet\KService.cs" />
     <Compile Include="..\..\Unity\Assets\Scripts\Base\Network\TNet\PacketParser.cs" Link="Network\TNet\PacketParser.cs" />
     <Compile Include="..\..\Unity\Assets\Scripts\Base\Network\TNet\TBuffer.cs" Link="Network\TNet\TBuffer.cs" />
     <Compile Include="..\..\Unity\Assets\Scripts\Base\Network\TNet\TChannel.cs" Link="Network\TNet\TChannel.cs" />
@@ -57,6 +60,7 @@
     <Folder Include="Helper\" />
     <Folder Include="Network\TNet\" />
     <Folder Include="Network\UNet\" />
+    <Folder Include="Network\KNet\" />
   </ItemGroup>
 
   <ItemGroup>

+ 4 - 3
Server/Model/Component/BenchmarkComponent.cs

@@ -24,9 +24,9 @@ namespace Model
 			{
 				NetOuterComponent networkComponent = Game.Scene.GetComponent<NetOuterComponent>();
 
-				for (int i = 0; i < 200; i++)
+				for (int i = 0; i < 100; i++)
 				{
-					await Game.Scene.GetComponent<TimerComponent>().WaitAsync(10);
+					await Game.Scene.GetComponent<TimerComponent>().WaitAsync(1000);
 					this.TestAsync(networkComponent, address, i);
 				}
 			}
@@ -46,6 +46,7 @@ namespace Model
 					while (i < 100000000)
 					{
 						++i;
+						await Game.Scene.GetComponent<TimerComponent>().WaitAsync(10);
 						await this.Send(session, j);
 					}
 				}
@@ -67,7 +68,7 @@ namespace Model
 				await session.Call<R2C_Ping>(new C2R_Ping());
 				++this.k;
 
-				if (this.k % 100000 != 0)
+				if (this.k % 10000 != 0)
 				{
 					return;
 				}

+ 1 - 1
Server/Server.sln

@@ -1,7 +1,7 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 15
-VisualStudioVersion = 15.0.27004.2005
+VisualStudioVersion = 15.0.27004.2009
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server.App", "App\Server.App.csproj", "{11464C2C-E0B6-476E-ABB1-87445E736B76}"
 EndProject

+ 18 - 0
Unity/Assets/Scripts/Base/Helper/ByteHelper.cs

@@ -59,5 +59,23 @@ namespace Model
 		{
 			return Encoding.UTF8.GetString(bytes, index, count);
 		}
+
+		public static void WriteTo(this byte[] bytes, int offset, int num)
+		{
+			byte[] numBytes = BitConverter.GetBytes(num);
+			for (int i = 0; i < numBytes.Length; ++i)
+			{
+				bytes[offset + i] = numBytes[i];
+			}
+		}
+		
+		public static void WriteTo(this byte[] bytes, int offset, uint num)
+		{
+			byte[] numBytes = BitConverter.GetBytes(num);
+			for (int i = 0; i < numBytes.Length; ++i)
+			{
+				bytes[offset + i] = numBytes[i];
+			}
+		}
 	}
 }

+ 3 - 8
Unity/Assets/Scripts/Base/Helper/TimeHelper.cs

@@ -4,24 +4,19 @@ namespace Model
 {
 	public static class TimeHelper
 	{
-		private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+		private static readonly long epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks;
 		/// <summary>
 		/// 客户端时间
 		/// </summary>
 		/// <returns></returns>
 		public static long ClientNow()
 		{
-			return Convert.ToInt64((DateTime.UtcNow - epoch).TotalMilliseconds);
+			return (DateTime.UtcNow.Ticks - epoch) / 10000;
 		}
 
 		public static long ClientNowSeconds()
 		{
-			return Convert.ToInt64((DateTime.UtcNow - epoch).TotalSeconds);
-		}
-
-		public static long ClientNowTicks()
-		{	
-			return Convert.ToInt64((DateTime.UtcNow - epoch).Ticks);
+			return (DateTime.UtcNow.Ticks - epoch) / 10000000;
 		}
 
 		/// <summary>

+ 4 - 4
Unity/Assets/Scripts/Base/Network/AChannel.cs

@@ -22,7 +22,7 @@ namespace Model
 
 	public abstract class AChannel: IDisposable
 	{
-		public long Id { get; private set; }
+		public long Id { get; set; }
 
 		public ChannelType ChannelType { get; }
 
@@ -46,7 +46,7 @@ namespace Model
 
 		protected void OnError(AChannel channel, SocketError e)
 		{
-			this.errorCallback(channel, e);
+			this.errorCallback?.Invoke(channel, e);
 		}
 
 
@@ -60,9 +60,9 @@ namespace Model
 		/// <summary>
 		/// 发送消息
 		/// </summary>
-		public abstract void Send(byte[] buffer, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable);
+		public abstract void Send(byte[] buffer);
 
-		public abstract void Send(List<byte[]> buffers, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable);
+		public abstract void Send(List<byte[]> buffers);
 
 		/// <summary>
 		/// 接收消息

+ 4 - 1
Unity/Assets/Scripts/Base/Network/AService.cs

@@ -6,11 +6,14 @@ namespace Model
 	public enum NetworkProtocol
 	{
 		TCP,
-		UDP
+		UDP,
+		KCP,
 	}
 
 	public abstract class AService: IDisposable
 	{
+		public abstract void Add(Action action);
+
 		public abstract AChannel GetChannel(long id);
 
 		public abstract Task<AChannel> AcceptChannel();

+ 9 - 0
Unity/Assets/Scripts/Base/Network/KNet.meta

@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 80771ba2b140e0f47b8df6dfe75d5c68
+folderAsset: yes
+timeCreated: 1511491627
+licenseType: Free
+DefaultImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 208 - 0
Unity/Assets/Scripts/Base/Network/KNet/KChannel.cs

@@ -0,0 +1,208 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+
+namespace Model
+{
+	public class KChannel: AChannel
+	{
+		private UdpClient socket;
+
+		private Kcp kcp;
+
+		private readonly TBuffer recvBuffer = new TBuffer(8 * 1024);
+		private readonly Queue<byte[]> sendBuffer = new Queue<byte[]>();
+
+		private readonly PacketParser parser;
+		private bool isConnected;
+		private readonly IPEndPoint remoteEndPoint;
+
+		private TaskCompletionSource<byte[]> recvTcs;
+
+		private uint lastRecvTime;
+
+		private readonly byte[] cacheBytes = new byte[1400];
+
+		private bool isNeedUpdate;
+
+		public uint Conn;
+
+		public uint RemoteConn;
+
+		public uint lastUpdateTime;
+
+		// accept
+		public KChannel(uint conn, uint remoteConn, UdpClient socket, IPEndPoint remoteEndPoint, KService kService): base(kService, ChannelType.Accept)
+		{
+			this.Id = conn;
+			this.Conn = conn;
+			this.RemoteConn = remoteConn;
+			this.remoteEndPoint = remoteEndPoint;
+			this.socket = socket;
+			this.parser = new PacketParser(this.recvBuffer);
+			kcp = new Kcp(this.Conn, this.Output);
+			kcp.SetMtu(512);
+			kcp.NoDelay(1, 10, 2, 1);  //fast
+			this.isConnected = true;
+			this.lastRecvTime = kService.TimeNow;
+			this.lastUpdateTime = kService.TimeNow;
+		}
+
+		// connect
+		public KChannel(uint conn, UdpClient socket, IPEndPoint remoteEndPoint, KService kService): base(kService, ChannelType.Connect)
+		{
+			this.Id = conn;
+			this.Conn = conn;
+			this.socket = socket;
+			this.parser = new PacketParser(this.recvBuffer);
+			this.remoteEndPoint = remoteEndPoint;
+			this.lastRecvTime = kService.TimeNow;
+			this.lastUpdateTime = kService.TimeNow;
+		}
+
+		public override void Dispose()
+		{
+			if (this.socket == null)
+			{
+				return;
+			}
+			base.Dispose();
+			this.socket = null;
+		}
+
+		public void HandleConnnect(uint responseConn)
+		{
+			if (this.isConnected)
+			{
+				return;
+			}
+			this.isConnected = true;
+			this.RemoteConn = responseConn;
+			this.kcp = new Kcp(responseConn, this.Output);
+			kcp.SetMtu(512);
+			kcp.NoDelay(1, 10, 2, 1);  //fast
+		}
+
+		public void HandleAccept(uint requestConn)
+		{
+			cacheBytes.WriteTo(0, 2);
+			cacheBytes.WriteTo(4, requestConn);
+			cacheBytes.WriteTo(8, this.Conn);
+			this.socket.Send(cacheBytes, 12, remoteEndPoint);
+		}
+
+		public void Update(uint timeNow)
+		{
+			// 如果还没连接上,发送连接请求
+			if (!this.isConnected)
+			{
+				cacheBytes[0] = 1;
+				cacheBytes.WriteTo(4, this.Conn);
+				this.socket.Send(cacheBytes, 8, remoteEndPoint);
+				return;
+			}
+
+			// 超时断开连接
+			if (timeNow - this.lastRecvTime > 10 * 1000)
+			{
+				this.OnError(this, SocketError.Disconnecting);
+				return;
+			}
+
+			HandleSend();
+
+			this.kcp.Update(timeNow);
+		}
+
+		private void HandleSend()
+		{
+			while (true)
+			{
+				if (this.sendBuffer.Count <= 0)
+				{
+					break;
+				}
+				byte[] buffer = this.sendBuffer.Dequeue();
+				this.kcp.Send(buffer);
+			}
+		}
+
+		public void HandleRecv(byte[] date, uint timeNow)
+		{
+			this.kcp.Input(date);
+
+			lastRecvTime = timeNow;
+
+			while (true)
+			{
+				int n = kcp.PeekSize();
+				if (n == 0)
+				{
+					this.OnError(this, SocketError.NetworkReset);
+					return;
+				}
+				int count = this.kcp.Recv(cacheBytes);
+				if (count <= 0)
+				{
+					return;
+				}
+				// 收到的数据放入缓冲区
+				this.recvBuffer.SendTo(this.cacheBytes, 0, count);
+
+				if (this.recvTcs != null)
+				{
+					byte[] packet = this.parser.GetPacket();
+					if (packet != null)
+					{
+						var tcs = this.recvTcs;
+						this.recvTcs = null;
+						tcs.SetResult(packet);
+					}
+				}
+			}
+		}
+		
+		public void Output(byte[] bytes, int count)
+		{
+			this.socket.Send(bytes, count, this.remoteEndPoint);
+		}
+		
+		public override void Send(byte[] buffer)
+		{
+			this.sendBuffer.Enqueue(buffer);
+			this.isNeedUpdate = true;
+		}
+
+		public override void Send(List<byte[]> buffers)
+		{
+			ushort size = (ushort)buffers.Select(b => b.Length).Sum();
+			byte[] sizeBuffer = BitConverter.GetBytes(size);
+			this.sendBuffer.Enqueue(sizeBuffer);
+			foreach (byte[] buffer in buffers)
+			{
+				this.sendBuffer.Enqueue(buffer);
+			}
+			this.isNeedUpdate = true;
+		}
+
+		public override Task<byte[]> Recv()
+		{
+			if (this.Id == 0)
+			{
+				throw new Exception("TChannel已经被Dispose, 不能接收消息");
+			}
+
+			byte[] packet = this.parser.GetPacket();
+			if (packet != null)
+			{
+				return Task.FromResult(packet);
+			}
+
+			recvTcs = new TaskCompletionSource<byte[]>();
+			return recvTcs.Task;
+		}
+	}
+}

+ 12 - 0
Unity/Assets/Scripts/Base/Network/KNet/KChannel.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 5479891425d5a284fb4c8246e1a52d34
+timeCreated: 1511491627
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 229 - 0
Unity/Assets/Scripts/Base/Network/KNet/KService.cs

@@ -0,0 +1,229 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+
+namespace Model
+{
+	public sealed class KService: AService
+	{
+		private UdpClient socket;
+		
+		private readonly Dictionary<long, KChannel> idChannels = new Dictionary<long, KChannel>();
+
+		private readonly Queue<UdpReceiveResult> udpResults = new Queue<UdpReceiveResult>();
+
+		private TaskCompletionSource<AChannel> acceptTcs;
+
+		private uint IdGenerater = 1000;
+
+		public uint TimeNow;
+
+		private uint lastUpdateTime;
+
+		private readonly Queue<long> removedChannels = new Queue<long>();
+
+		/// <summary>
+		/// 即可做client也可做server
+		/// </summary>
+		public KService(string host, int port)
+		{
+			this.TimeNow = (uint)(TimeHelper.Now() & 0x00000000ffffffff);
+			this.socket = new UdpClient(new IPEndPoint(IPAddress.Parse(host), port));
+			this.StartRecv();
+		}
+
+		public KService()
+		{
+			this.TimeNow = (uint)(TimeHelper.Now() & 0x00000000ffffffff);
+			this.socket = new UdpClient(new IPEndPoint(IPAddress.Any, 0));
+			this.StartRecv();
+		}
+
+		public override void Dispose()
+		{
+			if (this.socket == null)
+			{
+				return;
+			}
+
+			this.socket.Dispose();
+			this.socket = null;
+		}
+
+		public async void StartRecv()
+		{
+			while (true)
+			{
+				if (this.socket == null)
+				{
+					return;
+				}
+				UdpReceiveResult udpReceiveResult = await this.socket.ReceiveAsync();
+
+				// accept
+				uint conn = BitConverter.ToUInt32(udpReceiveResult.Buffer, 0);
+				// conn从1000开始,如果为1,2则是特殊包
+				if (conn == 1)
+				{
+					this.HandleAccept(udpReceiveResult);
+					continue;
+				}
+				
+				// connect response
+				if (conn == 2)
+				{
+					this.HandleConnect(udpReceiveResult);
+					continue;
+				}
+
+				this.HandleRecv(udpReceiveResult);
+			}
+		}
+
+		private void HandleConnect(UdpReceiveResult udpReceiveResult)
+		{
+			uint requestConn = BitConverter.ToUInt32(udpReceiveResult.Buffer, 4);
+			uint responseConn = BitConverter.ToUInt32(udpReceiveResult.Buffer, 8);
+			KChannel kChannel;
+			if (!this.idChannels.TryGetValue(requestConn, out kChannel))
+			{
+				return;
+			}
+			// 处理chanel
+			kChannel.HandleConnnect(responseConn);
+		}
+
+		private void HandleRecv(UdpReceiveResult udpReceiveResult)
+		{
+			uint conn = 0;
+			Kcp.ikcp_decode32u(udpReceiveResult.Buffer, 0, ref conn);
+
+			KChannel kChannel;
+			if (!this.idChannels.TryGetValue(conn, out kChannel))
+			{
+				return;
+			}
+			// 处理chanel
+			kChannel.HandleRecv(udpReceiveResult.Buffer, this.TimeNow);
+		}
+
+		private void HandleAccept(UdpReceiveResult udpReceiveResult)
+		{
+			if (this.acceptTcs == null)
+			{
+				this.udpResults.Enqueue(udpReceiveResult);
+				return;
+			}
+
+			uint requestConn = BitConverter.ToUInt32(udpReceiveResult.Buffer, 4);
+			// 如果已经连接上,则重新响应请求
+			KChannel kChannel;
+			if (this.idChannels.TryGetValue(requestConn, out kChannel))
+			{
+				kChannel.HandleAccept(requestConn);
+				return;
+			}
+
+			TaskCompletionSource<AChannel> t = this.acceptTcs;
+			this.acceptTcs = null;
+			kChannel = this.CreateAcceptChannel(udpReceiveResult.RemoteEndPoint, requestConn);
+			kChannel.HandleAccept(requestConn);
+			t.SetResult(kChannel);
+		}
+
+		private KChannel CreateAcceptChannel(IPEndPoint remoteEndPoint, uint remoteConn)
+		{
+			KChannel channel = new KChannel(++this.IdGenerater, remoteConn, this.socket, remoteEndPoint, this);
+			this.idChannels[channel.Id] = channel;
+			return channel;
+		}
+		
+		private KChannel CreateConnectChannel(IPEndPoint remoteEndPoint)
+		{
+			KChannel channel = new KChannel(++this.IdGenerater, this.socket, remoteEndPoint, this);
+			this.idChannels[channel.Id] = channel;
+			return channel;
+		}
+
+		public override void Add(Action action)
+		{
+		}
+
+		public override AChannel GetChannel(long id)
+		{
+			KChannel channel;
+			this.idChannels.TryGetValue(id, out channel);
+			return channel;
+		}
+
+		public override Task<AChannel> AcceptChannel()
+		{
+			if (this.udpResults.Count > 0)
+			{
+				UdpReceiveResult udpReceiveResult = this.udpResults.Dequeue();
+				uint requestConn = BitConverter.ToUInt32(udpReceiveResult.Buffer, 4);
+				KChannel kChannel = this.CreateAcceptChannel(udpReceiveResult.RemoteEndPoint, requestConn);
+				return Task.FromResult<AChannel>(kChannel);
+			}
+
+			acceptTcs = new TaskCompletionSource<AChannel>();
+
+			return this.acceptTcs.Task;
+		}
+
+		public override AChannel ConnectChannel(string host, int port)
+		{
+			IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse(host), port);
+			KChannel channel = this.CreateConnectChannel(ipEndPoint);
+			return channel;
+		}
+
+
+		public override void Remove(long id)
+		{
+			KChannel channel;
+			if (!this.idChannels.TryGetValue(id, out channel))
+			{
+				return;
+			}
+			if (channel == null)
+			{
+				return;
+			}
+			this.removedChannels.Enqueue(id);
+			channel.Dispose();
+		}
+		
+		public override void Update()
+		{
+			this.TimeNow = (uint)(TimeHelper.Now() & 0x00000000ffffffff);
+			if (this.TimeNow - lastUpdateTime < 5)
+			{
+				return;
+			}
+
+			this.lastUpdateTime = this.TimeNow;
+
+			foreach (KChannel channel in this.idChannels.Values)
+			{
+				if (channel.Id == 0)
+				{
+					continue;
+				}
+				channel.Update(this.TimeNow);
+			}
+
+			while (true)
+			{
+				if (this.removedChannels.Count <= 0)
+				{
+					break;
+				}
+				long id = this.removedChannels.Dequeue();
+				this.idChannels.Remove(id);
+			}
+		}
+	}
+}

+ 12 - 0
Unity/Assets/Scripts/Base/Network/KNet/KService.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 38037f5390cd34e41849290ca1737863
+timeCreated: 1511491627
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 997 - 0
Unity/Assets/Scripts/Base/Network/KNet/Kcp.cs

@@ -0,0 +1,997 @@
+using System;
+
+public class Kcp
+{
+	public const int IKCP_RTO_NDL = 30; // no delay min rto
+	public const int IKCP_RTO_MIN = 100; // normal min rto
+	public const int IKCP_RTO_DEF = 200;
+	public const int IKCP_RTO_MAX = 60000;
+	public const int IKCP_CMD_PUSH = 81; // cmd: push data
+	public const int IKCP_CMD_ACK = 82; // cmd: ack
+	public const int IKCP_CMD_WASK = 83; // cmd: window probe (ask)
+	public const int IKCP_CMD_WINS = 84; // cmd: window size (tell)
+	public const int IKCP_ASK_SEND = 1; // need to send IKCP_CMD_WASK
+	public const int IKCP_ASK_TELL = 2; // need to send IKCP_CMD_WINS
+	public const int IKCP_WND_SND = 32;
+	public const int IKCP_WND_RCV = 32;
+	public const int IKCP_MTU_DEF = 1024;
+	public const int IKCP_ACK_FAST = 3;
+	public const int IKCP_INTERVAL = 100;
+	public const int IKCP_OVERHEAD = 24;
+	public const int IKCP_DEADLINK = 10;
+	public const int IKCP_THRESH_INIT = 2;
+	public const int IKCP_THRESH_MIN = 2;
+	public const int IKCP_PROBE_INIT = 7000; // 7 secs to probe window size
+	public const int IKCP_PROBE_LIMIT = 120000; // up to 120 secs to probe window
+
+	// encode 8 bits unsigned int
+	public static int ikcp_encode8u(byte[] p, int offset, byte c)
+	{
+		p[0 + offset] = c;
+		return 1;
+	}
+
+	// decode 8 bits unsigned int
+	public static int ikcp_decode8u(byte[] p, int offset, ref byte c)
+	{
+		c = p[0 + offset];
+		return 1;
+	}
+
+	/* encode 16 bits unsigned int (lsb) */
+	public static int ikcp_encode16u(byte[] p, int offset, UInt16 w)
+	{
+		p[0 + offset] = (byte) (w >> 0);
+		p[1 + offset] = (byte) (w >> 8);
+		return 2;
+	}
+
+	/* decode 16 bits unsigned int (lsb) */
+	public static int ikcp_decode16u(byte[] p, int offset, ref UInt16 c)
+	{
+		UInt16 result = 0;
+		result |= p[0 + offset];
+		result |= (UInt16) (p[1 + offset] << 8);
+		c = result;
+		return 2;
+	}
+
+	/* encode 32 bits unsigned int (lsb) */
+	public static int ikcp_encode32u(byte[] p, int offset, UInt32 l)
+	{
+		p[0 + offset] = (byte) (l >> 0);
+		p[1 + offset] = (byte) (l >> 8);
+		p[2 + offset] = (byte) (l >> 16);
+		p[3 + offset] = (byte) (l >> 24);
+		return 4;
+	}
+
+	/* decode 32 bits unsigned int (lsb) */
+	public static int ikcp_decode32u(byte[] p, int offset, ref UInt32 c)
+	{
+		UInt32 result = 0;
+		result |= p[0 + offset];
+		result |= (UInt32) (p[1 + offset] << 8);
+		result |= (UInt32) (p[2 + offset] << 16);
+		result |= (UInt32) (p[3 + offset] << 24);
+		c = result;
+		return 4;
+	}
+
+	public static byte[] slice(byte[] p, int start, int stop)
+	{
+		var bytes = new byte[stop - start];
+		Array.Copy(p, start, bytes, 0, bytes.Length);
+		return bytes;
+	}
+
+	public static T[] slice<T>(T[] p, int start, int stop)
+	{
+		var arr = new T[stop - start];
+		var index = 0;
+		for (var i = start; i < stop; i++)
+		{
+			arr[index] = p[i];
+			index++;
+		}
+
+		return arr;
+	}
+
+	public static byte[] append(byte[] p, byte c)
+	{
+		var bytes = new byte[p.Length + 1];
+		Array.Copy(p, bytes, p.Length);
+		bytes[p.Length] = c;
+		return bytes;
+	}
+
+	public static T[] append<T>(T[] p, T c)
+	{
+		var arr = new T[p.Length + 1];
+		for (var i = 0; i < p.Length; i++)
+			arr[i] = p[i];
+		arr[p.Length] = c;
+		return arr;
+	}
+
+	public static T[] append<T>(T[] p, T[] cs)
+	{
+		var arr = new T[p.Length + cs.Length];
+		for (var i = 0; i < p.Length; i++)
+			arr[i] = p[i];
+		for (var i = 0; i < cs.Length; i++)
+			arr[p.Length + i] = cs[i];
+		return arr;
+	}
+
+	private static UInt32 _imin_(UInt32 a, UInt32 b)
+	{
+		return a <= b? a : b;
+	}
+
+	private static UInt32 _imax_(UInt32 a, UInt32 b)
+	{
+		return a >= b? a : b;
+	}
+
+	private static UInt32 _ibound_(UInt32 lower, UInt32 middle, UInt32 upper)
+	{
+		return _imin_(_imax_(lower, middle), upper);
+	}
+
+	private static Int32 _itimediff(UInt32 later, UInt32 earlier)
+	{
+		return (Int32) (later - earlier);
+	}
+
+	// KCP Segment Definition
+	internal class Segment
+	{
+		internal UInt32 conv;
+		internal UInt32 cmd;
+		internal UInt32 frg;
+		internal UInt32 wnd;
+		internal UInt32 ts;
+		internal UInt32 sn;
+		internal UInt32 una;
+		internal UInt32 resendts;
+		internal UInt32 rto;
+		internal UInt32 fastack;
+		internal UInt32 xmit;
+		internal byte[] data;
+
+		internal Segment(int size)
+		{
+			this.data = new byte[size];
+		}
+
+		// encode a segment into buffer
+		internal int encode(byte[] ptr, int offset)
+		{
+			var offset_ = offset;
+
+			offset += ikcp_encode32u(ptr, offset, conv);
+			offset += ikcp_encode8u(ptr, offset, (byte) cmd);
+			offset += ikcp_encode8u(ptr, offset, (byte) frg);
+			offset += ikcp_encode16u(ptr, offset, (UInt16) wnd);
+			offset += ikcp_encode32u(ptr, offset, ts);
+			offset += ikcp_encode32u(ptr, offset, sn);
+			offset += ikcp_encode32u(ptr, offset, una);
+			offset += ikcp_encode32u(ptr, offset, (UInt32) data.Length);
+
+			return offset - offset_;
+		}
+	}
+
+	// kcp members.
+	private readonly UInt32 conv;
+
+	private UInt32 mtu;
+	private UInt32 mss;
+	private UInt32 state;
+	private UInt32 snd_una;
+	private UInt32 snd_nxt;
+	private UInt32 rcv_nxt;
+	private UInt32 ts_recent;
+	private UInt32 ts_lastack;
+	private UInt32 ssthresh;
+	private UInt32 rx_rttval;
+	private UInt32 rx_srtt;
+	private UInt32 rx_rto;
+	private UInt32 rx_minrto;
+	private UInt32 snd_wnd;
+	private UInt32 rcv_wnd;
+	private UInt32 rmt_wnd;
+	private UInt32 cwnd;
+	private UInt32 probe;
+	private UInt32 current;
+	private UInt32 interval;
+	private UInt32 ts_flush;
+	private UInt32 xmit;
+	private UInt32 nodelay;
+	private UInt32 updated;
+	private UInt32 ts_probe;
+	private UInt32 probe_wait;
+	private readonly UInt32 dead_link;
+	private UInt32 incr;
+
+	private Segment[] snd_queue = new Segment[0];
+	private Segment[] rcv_queue = new Segment[0];
+	private Segment[] snd_buf = new Segment[0];
+	private Segment[] rcv_buf = new Segment[0];
+
+	private UInt32[] acklist = new UInt32[0];
+
+	private byte[] buffer;
+	private Int32 fastresend;
+	private Int32 nocwnd;
+
+	private Int32 logmask;
+
+	// buffer, size
+	private readonly Action<byte[], int> output;
+
+	// create a new kcp control object, 'conv' must equal in two endpoint
+	// from the same connection.
+	public Kcp(UInt32 conv_, Action<byte[], int> output_)
+	{
+		conv = conv_;
+		snd_wnd = IKCP_WND_SND;
+		rcv_wnd = IKCP_WND_RCV;
+		rmt_wnd = IKCP_WND_RCV;
+		mtu = IKCP_MTU_DEF;
+		mss = mtu - IKCP_OVERHEAD;
+
+		rx_rto = IKCP_RTO_DEF;
+		rx_minrto = IKCP_RTO_MIN;
+		interval = IKCP_INTERVAL;
+		ts_flush = IKCP_INTERVAL;
+		ssthresh = IKCP_THRESH_INIT;
+		dead_link = IKCP_DEADLINK;
+		buffer = new byte[(mtu + IKCP_OVERHEAD) * 3];
+		output = output_;
+	}
+
+	// check the size of next message in the recv queue
+	public int PeekSize()
+	{
+		if (0 == rcv_queue.Length)
+			return -1;
+
+		var seq = rcv_queue[0];
+
+		if (0 == seq.frg)
+			return seq.data.Length;
+
+		if (rcv_queue.Length < seq.frg + 1)
+			return -1;
+
+		int length = 0;
+
+		foreach (var item in rcv_queue)
+		{
+			length += item.data.Length;
+			if (0 == item.frg)
+				break;
+		}
+
+		return length;
+	}
+
+	// user/upper level recv: returns size, returns below zero for EAGAIN
+	public int Recv(byte[] buffer)
+	{
+		if (0 == rcv_queue.Length)
+			return -1;
+
+		var peekSize = PeekSize();
+		if (0 > peekSize)
+			return -2;
+
+		if (peekSize > buffer.Length)
+			return -3;
+
+		var fast_recover = false;
+		if (rcv_queue.Length >= rcv_wnd)
+			fast_recover = true;
+
+		// merge fragment.
+		var count = 0;
+		var n = 0;
+		foreach (var seg in rcv_queue)
+		{
+			Array.Copy(seg.data, 0, buffer, n, seg.data.Length);
+			n += seg.data.Length;
+			count++;
+			if (0 == seg.frg)
+				break;
+		}
+
+		if (0 < count)
+			this.rcv_queue = slice(this.rcv_queue, count, this.rcv_queue.Length);
+
+		// move available data from rcv_buf -> rcv_queue
+		count = 0;
+		foreach (var seg in rcv_buf)
+			if (seg.sn == this.rcv_nxt && this.rcv_queue.Length < this.rcv_wnd)
+			{
+				this.rcv_queue = append(this.rcv_queue, seg);
+				this.rcv_nxt++;
+				count++;
+			}
+			else
+			{
+				break;
+			}
+
+		if (0 < count)
+			rcv_buf = slice(rcv_buf, count, rcv_buf.Length);
+
+		// fast recover
+		if (rcv_queue.Length < rcv_wnd && fast_recover)
+			this.probe |= IKCP_ASK_TELL;
+
+		return n;
+	}
+
+	// user/upper level send, returns below zero for error
+	public int Send(byte[] buffer)
+	{
+		if (0 == buffer.Length)
+			return -1;
+
+		var count = 0;
+
+		if (buffer.Length < mss)
+			count = 1;
+		else
+			count = (int) (buffer.Length + mss - 1) / (int) mss;
+
+		if (255 < count)
+			return -2;
+
+		if (0 == count)
+			count = 1;
+
+		var offset = 0;
+
+		for (var i = 0; i < count; i++)
+		{
+			var size = 0;
+			if (buffer.Length - offset > mss)
+				size = (int) mss;
+			else
+				size = buffer.Length - offset;
+
+			var seg = new Segment(size);
+			Array.Copy(buffer, offset, seg.data, 0, size);
+			offset += size;
+			seg.frg = (UInt32) (count - i - 1);
+			snd_queue = append(snd_queue, seg);
+		}
+
+		return 0;
+	}
+
+	// update ack.
+	private void update_ack(Int32 rtt)
+	{
+		if (0 == rx_srtt)
+		{
+			rx_srtt = (UInt32) rtt;
+			rx_rttval = (UInt32) rtt / 2;
+		}
+		else
+		{
+			Int32 delta = (Int32) ((UInt32) rtt - rx_srtt);
+			if (0 > delta)
+				delta = -delta;
+
+			rx_rttval = (3 * rx_rttval + (uint) delta) / 4;
+			rx_srtt = (UInt32) ((7 * rx_srtt + rtt) / 8);
+			if (rx_srtt < 1)
+				rx_srtt = 1;
+		}
+
+		var rto = (int) (rx_srtt + _imax_(1, 4 * rx_rttval));
+		rx_rto = _ibound_(rx_minrto, (UInt32) rto, IKCP_RTO_MAX);
+	}
+
+	private void shrink_buf()
+	{
+		if (snd_buf.Length > 0)
+			snd_una = snd_buf[0].sn;
+		else
+			snd_una = snd_nxt;
+	}
+
+	private void parse_ack(UInt32 sn)
+	{
+		if (_itimediff(sn, snd_una) < 0 || _itimediff(sn, snd_nxt) >= 0)
+			return;
+
+		var index = 0;
+		foreach (var seg in snd_buf)
+		{
+			if (sn == seg.sn)
+			{
+				snd_buf = append(slice(snd_buf, 0, index), slice(snd_buf, index + 1, snd_buf.Length));
+				break;
+			}
+			seg.fastack++;
+
+			index++;
+		}
+	}
+
+	private void parse_una(UInt32 una)
+	{
+		var count = 0;
+		foreach (var seg in snd_buf)
+			if (_itimediff(una, seg.sn) > 0)
+				count++;
+			else
+				break;
+
+		if (0 < count)
+			snd_buf = slice(snd_buf, count, snd_buf.Length);
+	}
+
+	private void ack_push(UInt32 sn, UInt32 ts)
+	{
+		acklist = append(acklist, new UInt32[2] { sn, ts });
+	}
+
+	private void ack_get(int p, ref UInt32 sn, ref UInt32 ts)
+	{
+		sn = acklist[p * 2 + 0];
+		ts = acklist[p * 2 + 1];
+	}
+
+	private void parse_data(Segment newseg)
+	{
+		var sn = newseg.sn;
+		if (_itimediff(sn, rcv_nxt + rcv_wnd) >= 0 || _itimediff(sn, rcv_nxt) < 0)
+			return;
+
+		var n = rcv_buf.Length - 1;
+		var after_idx = -1;
+		var repeat = false;
+		for (var i = n; i >= 0; i--)
+		{
+			var seg = rcv_buf[i];
+			if (seg.sn == sn)
+			{
+				repeat = true;
+				break;
+			}
+
+			if (_itimediff(sn, seg.sn) > 0)
+			{
+				after_idx = i;
+				break;
+			}
+		}
+
+		if (!repeat)
+			if (after_idx == -1)
+				this.rcv_buf = append(new Segment[1] { newseg }, this.rcv_buf);
+			else
+				this.rcv_buf = append(slice(this.rcv_buf, 0, after_idx + 1),
+				                      append(new Segment[1] { newseg }, slice(this.rcv_buf, after_idx + 1, this.rcv_buf.Length)));
+
+		// move available data from rcv_buf -> rcv_queue
+		var count = 0;
+		foreach (var seg in rcv_buf)
+			if (seg.sn == this.rcv_nxt && this.rcv_queue.Length < this.rcv_wnd)
+			{
+				this.rcv_queue = append(this.rcv_queue, seg);
+				this.rcv_nxt++;
+				count++;
+			}
+			else
+			{
+				break;
+			}
+
+		if (0 < count)
+			this.rcv_buf = slice(this.rcv_buf, count, this.rcv_buf.Length);
+	}
+
+	// when you received a low level packet (eg. UDP packet), call it
+	public int Input(byte[] data)
+	{
+		var s_una = snd_una;
+		if (data.Length < IKCP_OVERHEAD)
+			return 0;
+
+		var offset = 0;
+
+		while (true)
+		{
+			UInt32 ts = 0;
+			UInt32 sn = 0;
+			UInt32 length = 0;
+			UInt32 una = 0;
+			UInt32 conv_ = 0;
+
+			UInt16 wnd = 0;
+
+			byte cmd = 0;
+			byte frg = 0;
+
+			if (data.Length - offset < IKCP_OVERHEAD)
+				break;
+
+			offset += ikcp_decode32u(data, offset, ref conv_);
+
+			if (conv != conv_)
+				return -1;
+
+			offset += ikcp_decode8u(data, offset, ref cmd);
+			offset += ikcp_decode8u(data, offset, ref frg);
+			offset += ikcp_decode16u(data, offset, ref wnd);
+			offset += ikcp_decode32u(data, offset, ref ts);
+			offset += ikcp_decode32u(data, offset, ref sn);
+			offset += ikcp_decode32u(data, offset, ref una);
+			offset += ikcp_decode32u(data, offset, ref length);
+
+			if (data.Length - offset < length)
+				return -2;
+
+			switch (cmd)
+			{
+				case IKCP_CMD_PUSH:
+				case IKCP_CMD_ACK:
+				case IKCP_CMD_WASK:
+				case IKCP_CMD_WINS:
+					break;
+				default:
+					return -3;
+			}
+
+			rmt_wnd = wnd;
+			parse_una(una);
+			shrink_buf();
+
+			if (IKCP_CMD_ACK == cmd)
+			{
+				if (_itimediff(current, ts) >= 0)
+					this.update_ack(_itimediff(this.current, ts));
+				parse_ack(sn);
+				shrink_buf();
+			}
+			else if (IKCP_CMD_PUSH == cmd)
+			{
+				if (_itimediff(sn, rcv_nxt + rcv_wnd) < 0)
+				{
+					ack_push(sn, ts);
+					if (_itimediff(sn, rcv_nxt) >= 0)
+					{
+						var seg = new Segment((int) length);
+						seg.conv = conv_;
+						seg.cmd = cmd;
+						seg.frg = frg;
+						seg.wnd = wnd;
+						seg.ts = ts;
+						seg.sn = sn;
+						seg.una = una;
+
+						if (length > 0)
+							Array.Copy(data, offset, seg.data, 0, length);
+
+						parse_data(seg);
+					}
+				}
+			}
+			else if (IKCP_CMD_WASK == cmd)
+			{
+				// ready to send back IKCP_CMD_WINS in Ikcp_flush
+				// tell remote my window size
+				probe |= IKCP_ASK_TELL;
+			}
+			else if (IKCP_CMD_WINS == cmd)
+			{
+				// do nothing
+			}
+			else
+			{
+				return -3;
+			}
+
+			offset += (int) length;
+		}
+
+		if (_itimediff(snd_una, s_una) > 0)
+			if (this.cwnd < this.rmt_wnd)
+			{
+				var mss_ = this.mss;
+				if (this.cwnd < this.ssthresh)
+				{
+					this.cwnd++;
+					this.incr += mss_;
+				}
+				else
+				{
+					if (this.incr < mss_)
+						this.incr = mss_;
+					this.incr += mss_ * mss_ / this.incr + mss_ / 16;
+					if ((this.cwnd + 1) * mss_ <= this.incr)
+						this.cwnd++;
+				}
+				if (this.cwnd > this.rmt_wnd)
+				{
+					this.cwnd = this.rmt_wnd;
+					this.incr = this.rmt_wnd * mss_;
+				}
+			}
+
+		return 0;
+	}
+
+	private Int32 wnd_unused()
+	{
+		if (rcv_queue.Length < rcv_wnd)
+			return (int) this.rcv_wnd - rcv_queue.Length;
+		return 0;
+	}
+
+	// flush pending data
+	private void flush()
+	{
+		var current_ = current;
+		var buffer_ = buffer;
+		var change = 0;
+		var lost = 0;
+
+		if (0 == updated)
+			return;
+
+		var seg = new Segment(0);
+		seg.conv = conv;
+		seg.cmd = IKCP_CMD_ACK;
+		seg.wnd = (UInt32) wnd_unused();
+		seg.una = rcv_nxt;
+
+		// flush acknowledges
+		var count = acklist.Length / 2;
+		var offset = 0;
+		for (var i = 0; i < count; i++)
+		{
+			if (offset + IKCP_OVERHEAD > mtu)
+			{
+				output(buffer, offset);
+				//Array.Clear(buffer, 0, offset);
+				offset = 0;
+			}
+			ack_get(i, ref seg.sn, ref seg.ts);
+			offset += seg.encode(buffer, offset);
+		}
+		acklist = new UInt32[0];
+
+		// probe window size (if remote window size equals zero)
+		if (0 == rmt_wnd)
+		{
+			if (0 == probe_wait)
+			{
+				probe_wait = IKCP_PROBE_INIT;
+				ts_probe = current + probe_wait;
+			}
+			else
+			{
+				if (_itimediff(current, ts_probe) >= 0)
+				{
+					if (probe_wait < IKCP_PROBE_INIT)
+						probe_wait = IKCP_PROBE_INIT;
+					probe_wait += probe_wait / 2;
+					if (probe_wait > IKCP_PROBE_LIMIT)
+						probe_wait = IKCP_PROBE_LIMIT;
+					ts_probe = current + probe_wait;
+					probe |= IKCP_ASK_SEND;
+				}
+			}
+		}
+		else
+		{
+			ts_probe = 0;
+			probe_wait = 0;
+		}
+
+		// flush window probing commands
+		if ((probe & IKCP_ASK_SEND) != 0)
+		{
+			seg.cmd = IKCP_CMD_WASK;
+			if (offset + IKCP_OVERHEAD > (int) mtu)
+			{
+				output(buffer, offset);
+				//Array.Clear(buffer, 0, offset);
+				offset = 0;
+			}
+			offset += seg.encode(buffer, offset);
+		}
+
+		probe = 0;
+
+		// calculate window size
+		var cwnd_ = _imin_(snd_wnd, rmt_wnd);
+		if (0 == nocwnd)
+			cwnd_ = _imin_(cwnd, cwnd_);
+
+		count = 0;
+		for (var k = 0; k < snd_queue.Length; k++)
+		{
+			if (_itimediff(snd_nxt, snd_una + cwnd_) >= 0)
+				break;
+
+			var newseg = snd_queue[k];
+			newseg.conv = conv;
+			newseg.cmd = IKCP_CMD_PUSH;
+			newseg.wnd = seg.wnd;
+			newseg.ts = current_;
+			newseg.sn = snd_nxt;
+			newseg.una = rcv_nxt;
+			newseg.resendts = current_;
+			newseg.rto = rx_rto;
+			newseg.fastack = 0;
+			newseg.xmit = 0;
+			snd_buf = append(snd_buf, newseg);
+			snd_nxt++;
+			count++;
+		}
+
+		if (0 < count)
+			this.snd_queue = slice(this.snd_queue, count, this.snd_queue.Length);
+
+		// calculate resent
+		var resent = (UInt32) fastresend;
+		if (fastresend <= 0)
+			resent = 0xffffffff;
+		var rtomin = rx_rto >> 3;
+		if (nodelay != 0)
+			rtomin = 0;
+
+		// flush data segments
+		foreach (var segment in snd_buf)
+		{
+			var needsend = false;
+			var debug = _itimediff(current_, segment.resendts);
+			if (0 == segment.xmit)
+			{
+				needsend = true;
+				segment.xmit++;
+				segment.rto = rx_rto;
+				segment.resendts = current_ + segment.rto + rtomin;
+			}
+			else if (_itimediff(current_, segment.resendts) >= 0)
+			{
+				needsend = true;
+				segment.xmit++;
+				xmit++;
+				if (0 == nodelay)
+					segment.rto += rx_rto;
+				else
+					segment.rto += rx_rto / 2;
+				segment.resendts = current_ + segment.rto;
+				lost = 1;
+			}
+			else if (segment.fastack >= resent)
+			{
+				needsend = true;
+				segment.xmit++;
+				segment.fastack = 0;
+				segment.resendts = current_ + segment.rto;
+				change++;
+			}
+
+			if (needsend)
+			{
+				segment.ts = current_;
+				segment.wnd = seg.wnd;
+				segment.una = rcv_nxt;
+
+				var need = IKCP_OVERHEAD + segment.data.Length;
+				if (offset + need > mtu)
+				{
+					output(buffer, offset);
+					//Array.Clear(buffer, 0, offset);
+					offset = 0;
+				}
+
+				offset += segment.encode(buffer, offset);
+				if (segment.data.Length > 0)
+				{
+					Array.Copy(segment.data, 0, buffer, offset, segment.data.Length);
+					offset += segment.data.Length;
+				}
+
+				if (segment.xmit >= dead_link)
+					this.state = 0;
+			}
+		}
+
+		// flash remain segments
+		if (offset > 0)
+		{
+			output(buffer, offset);
+			//Array.Clear(buffer, 0, offset);
+			offset = 0;
+		}
+
+		// update ssthresh
+		if (change != 0)
+		{
+			var inflight = snd_nxt - snd_una;
+			ssthresh = inflight / 2;
+			if (ssthresh < IKCP_THRESH_MIN)
+				ssthresh = IKCP_THRESH_MIN;
+			cwnd = ssthresh + resent;
+			incr = cwnd * mss;
+		}
+
+		if (lost != 0)
+		{
+			ssthresh = cwnd / 2;
+			if (ssthresh < IKCP_THRESH_MIN)
+				ssthresh = IKCP_THRESH_MIN;
+			cwnd = 1;
+			incr = mss;
+		}
+
+		if (cwnd < 1)
+		{
+			cwnd = 1;
+			incr = mss;
+		}
+	}
+
+	// update state (call it repeatedly, every 10ms-100ms), or you can ask
+	// ikcp_check when to call it again (without ikcp_input/_send calling).
+	// 'current' - current timestamp in millisec.
+	public void Update(UInt32 current_)
+	{
+		current = current_;
+
+		if (0 == updated)
+		{
+			updated = 1;
+			ts_flush = current;
+		}
+
+		var slap = _itimediff(current, ts_flush);
+
+		if (slap >= 10000 || slap < -10000)
+		{
+			ts_flush = current;
+			slap = 0;
+		}
+
+		if (slap >= 0)
+		{
+			ts_flush += interval;
+			if (_itimediff(current, ts_flush) >= 0)
+				ts_flush = current + interval;
+			flush();
+		}
+	}
+
+	// Determine when should you invoke ikcp_update:
+	// returns when you should invoke ikcp_update in millisec, if there
+	// is no ikcp_input/_send calling. you can call ikcp_update in that
+	// time, instead of call update repeatly.
+	// Important to reduce unnacessary ikcp_update invoking. use it to
+	// schedule ikcp_update (eg. implementing an epoll-like mechanism,
+	// or optimize ikcp_update when handling massive kcp connections)
+	public UInt32 Check(UInt32 current_)
+	{
+		if (0 == updated)
+			return current_;
+
+		var ts_flush_ = ts_flush;
+		var tm_flush_ = 0x7fffffff;
+		var tm_packet = 0x7fffffff;
+		var minimal = 0;
+
+		if (_itimediff(current_, ts_flush_) >= 10000 || _itimediff(current_, ts_flush_) < -10000)
+			ts_flush_ = current_;
+
+		if (_itimediff(current_, ts_flush_) >= 0)
+			return current_;
+
+		tm_flush_ = _itimediff(ts_flush_, current_);
+
+		foreach (var seg in snd_buf)
+		{
+			var diff = _itimediff(seg.resendts, current_);
+			if (diff <= 0)
+				return current_;
+			if (diff < tm_packet)
+				tm_packet = diff;
+		}
+
+		minimal = tm_packet;
+		if (tm_packet >= tm_flush_)
+			minimal = tm_flush_;
+		if (minimal >= interval)
+			minimal = (int) interval;
+
+		return current_ + (UInt32) minimal;
+	}
+
+	// change MTU size, default is 1400
+	public int SetMtu(Int32 mtu_)
+	{
+		if (mtu_ < 50 || mtu_ < IKCP_OVERHEAD)
+			return -1;
+
+		var buffer_ = new byte[(mtu_ + IKCP_OVERHEAD) * 3];
+		if (null == buffer_)
+			return -2;
+
+		mtu = (UInt32) mtu_;
+		mss = mtu - IKCP_OVERHEAD;
+		buffer = buffer_;
+		return 0;
+	}
+
+	public int Interval(Int32 interval_)
+	{
+		if (interval_ > 5000)
+			interval_ = 5000;
+		else if (interval_ < 10)
+			interval_ = 10;
+		interval = (UInt32) interval_;
+		return 0;
+	}
+
+	// fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
+	// nodelay: 0:disable(default), 1:enable
+	// interval: internal update timer interval in millisec, default is 100ms
+	// resend: 0:disable fast resend(default), 1:enable fast resend
+	// nc: 0:normal congestion control(default), 1:disable congestion control
+	public int NoDelay(int nodelay_, int interval_, int resend_, int nc_)
+	{
+		if (nodelay_ > 0)
+		{
+			nodelay = (UInt32) nodelay_;
+			if (nodelay_ != 0)
+				rx_minrto = IKCP_RTO_NDL;
+			else
+				rx_minrto = IKCP_RTO_MIN;
+		}
+
+		if (interval_ >= 0)
+		{
+			if (interval_ > 5000)
+				interval_ = 5000;
+			else if (interval_ < 10)
+				interval_ = 10;
+			interval = (UInt32) interval_;
+		}
+
+		if (resend_ >= 0)
+			fastresend = resend_;
+
+		if (nc_ >= 0)
+			nocwnd = nc_;
+
+		return 0;
+	}
+
+	// set maximum window size: sndwnd=32, rcvwnd=32 by default
+	public int WndSize(int sndwnd, int rcvwnd)
+	{
+		if (sndwnd > 0)
+			snd_wnd = (UInt32) sndwnd;
+
+		if (rcvwnd > 0)
+			rcv_wnd = (UInt32) rcvwnd;
+		return 0;
+	}
+
+	// get how many packet is waiting to be sent
+	public int WaitSnd()
+	{
+		return snd_buf.Length + snd_queue.Length;
+	}
+}

+ 12 - 0
Unity/Assets/Scripts/Base/Network/KNet/Kcp.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 0832a74dcd3e44341bcffd3b1f182419
+timeCreated: 1511491627
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 34 - 1
Unity/Assets/Scripts/Base/Network/TNet/TBuffer.cs

@@ -5,7 +5,7 @@ namespace Model
 {
 	public class TBuffer
 	{
-		public const int ChunkSize = 8192;
+		public int ChunkSize = 8192;
 
 		private readonly LinkedList<byte[]> bufferList = new LinkedList<byte[]>();
 
@@ -18,6 +18,12 @@ namespace Model
 			this.bufferList.AddLast(new byte[ChunkSize]);
 		}
 
+		public TBuffer(int chunkSize)
+		{
+			this.ChunkSize = chunkSize;
+			this.bufferList.AddLast(new byte[ChunkSize]);
+		}
+
 		public int Count
 		{
 			get
@@ -125,5 +131,32 @@ namespace Model
 				}
 			}
 		}
+		
+		public void SendTo(byte[] buffer, int offset, int count)
+		{
+			int alreadyCopyCount = 0;
+			while (alreadyCopyCount < count)
+			{
+				if (this.LastIndex == ChunkSize)
+				{
+					this.bufferList.AddLast(new byte[ChunkSize]);
+					this.LastIndex = 0;
+				}
+
+				int n = count - alreadyCopyCount;
+				if (ChunkSize - this.LastIndex > n)
+				{
+					Array.Copy(buffer, alreadyCopyCount + offset, this.bufferList.Last.Value, this.LastIndex, n);
+					this.LastIndex += count - alreadyCopyCount;
+					alreadyCopyCount += n;
+				}
+				else
+				{
+					Array.Copy(buffer, alreadyCopyCount + offset, this.bufferList.Last.Value, this.LastIndex, ChunkSize - this.LastIndex);
+					alreadyCopyCount += ChunkSize - this.LastIndex;
+					this.LastIndex = ChunkSize;
+				}
+			}
+		}
 	}
 }

+ 6 - 6
Unity/Assets/Scripts/Base/Network/TNet/TChannel.cs

@@ -83,7 +83,7 @@ namespace Model
 			this.StartRecv();
 		}
 
-		public override void Send(byte[] buffer, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable)
+		public override void Send(byte[] buffer)
 		{
 			if (this.Id == 0)
 			{
@@ -98,7 +98,7 @@ namespace Model
 			}
 		}
 
-		public override void Send(List<byte[]> buffers, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable)
+		public override void Send(List<byte[]> buffers)
 		{
 			if (this.Id == 0)
 			{
@@ -148,14 +148,14 @@ namespace Model
 
 					this.isSending = true;
 
-					int sendSize = TBuffer.ChunkSize - this.sendBuffer.FirstIndex;
+					int sendSize = sendBuffer.ChunkSize - this.sendBuffer.FirstIndex;
 					if (sendSize > this.sendBuffer.Count)
 					{
 						sendSize = this.sendBuffer.Count;
 					}
 					await this.tcpClient.GetStream().WriteAsync(this.sendBuffer.First, this.sendBuffer.FirstIndex, sendSize);
 					this.sendBuffer.FirstIndex += sendSize;
-					if (this.sendBuffer.FirstIndex == TBuffer.ChunkSize)
+					if (this.sendBuffer.FirstIndex == sendBuffer.ChunkSize)
 					{
 						this.sendBuffer.FirstIndex = 0;
 						this.sendBuffer.RemoveFirst();
@@ -179,7 +179,7 @@ namespace Model
 					{
 						return;
 					}
-					int size = TBuffer.ChunkSize - this.recvBuffer.LastIndex;
+					int size = this.recvBuffer.ChunkSize - this.recvBuffer.LastIndex;
 
 					int n = await this.tcpClient.GetStream().ReadAsync(this.recvBuffer.Last, this.recvBuffer.LastIndex, size);
 
@@ -191,7 +191,7 @@ namespace Model
 
 					this.recvBuffer.LastIndex += n;
 
-					if (this.recvBuffer.LastIndex == TBuffer.ChunkSize)
+					if (this.recvBuffer.LastIndex == this.recvBuffer.ChunkSize)
 					{
 						this.recvBuffer.AddLast();
 						this.recvBuffer.LastIndex = 0;

+ 1 - 1
Unity/Assets/Scripts/Base/Network/TNet/TService.cs

@@ -46,7 +46,7 @@ namespace Model
 			this.acceptor = null;
 		}
 
-		public void Add(Action action)
+		public override void Add(Action action)
 		{
 			this.actions.Enqueue(action);
 		}

+ 4 - 4
Unity/Assets/Scripts/Base/Network/UNet/UChannel.cs

@@ -47,16 +47,16 @@ namespace Model
 			this.socket.Dispose();
 		}
 
-		public override void Send(byte[] buffer, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable)
+		public override void Send(byte[] buffer)
 		{
 			if (this.Id == 0)
 			{
 				throw new Exception("UChannel已经被Dispose, 不能发送消息");
 			}
-			this.socket.SendAsync(buffer, channelID, flags);
+			this.socket.SendAsync(buffer);
 		}
 
-		public override void Send(List<byte[]> buffers, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable)
+		public override void Send(List<byte[]> buffers)
 		{
 			if (this.Id == 0)
 			{
@@ -70,7 +70,7 @@ namespace Model
 				Array.Copy(bytes, 0, buffer, index, bytes.Length);
 				index += bytes.Length;
 			}
-			this.socket.SendAsync(buffer, channelID, flags);
+			this.socket.SendAsync(buffer);
 		}
 
 		public override Task<byte[]> Recv()

+ 6 - 1
Unity/Assets/Scripts/Base/Network/UNet/UService.cs

@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
 
@@ -58,6 +59,10 @@ namespace Model
 			return channel;
 		}
 
+		public override void Add(Action action)
+		{
+		}
+
 		public override AChannel GetChannel(long id)
 		{
 			UChannel channel = null;

+ 6 - 0
Unity/Assets/Scripts/Component/NetworkComponent.cs

@@ -25,6 +25,9 @@ namespace Model
 				case NetworkProtocol.UDP:
 					this.Service = new UService();
 					break;
+				case NetworkProtocol.KCP:
+					this.Service = new KService();
+					break;
 				default:
 					throw new ArgumentOutOfRangeException();
 			}
@@ -42,6 +45,9 @@ namespace Model
 					case NetworkProtocol.UDP:
 						this.Service = new UService(host, port);
 						break;
+					case NetworkProtocol.KCP:
+						this.Service = new KService(host, port);
+						break;
 					default:
 						throw new ArgumentOutOfRangeException();
 				}

+ 2 - 2
Unity/Assets/Scripts/Entity/Session.cs

@@ -81,7 +81,7 @@ namespace Model
 
 		private void Run(ushort opcode, byte[] messageBytes)
 		{
-			int offset = 0;
+			int offset;
 			// opcode最高位表示是否压缩
 			bool isCompressed = (opcode & 0x8000) > 0;
 			if (isCompressed) // 最高位为1,表示有压缩,需要解压缩
@@ -339,7 +339,7 @@ namespace Model
 			long id = this.Id;
 
 			base.Dispose();
-
+			
 			this.channel.Dispose();
 			this.network.Remove(id);
 		}

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
Unity/Unity.Editor.Plugins.csproj


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
Unity/Unity.Editor.csproj


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
Unity/Unity.Plugins.csproj


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 6 - 3
Unity/Unity.csproj


+ 1 - 1
Unity/Unity.sln.DotSettings.user

@@ -2,4 +2,4 @@
 	<s:String x:Key="/Default/CodeEditing/Intellisense/CodeCompletion/IntelliSenseCompletingCharacters/CSharpCompletingCharacters/NonCompletingCharacters/@EntryValue"></s:String>
 	<s:String x:Key="/Default/CodeInspection/Highlighting/AnalysisEnabled/@EntryValue">VISIBLE_FILES</s:String>
 	<s:Int64 x:Key="/Default/Environment/SearchAndNavigation/DefaultOccurrencesGroupingIndex/@EntryValue">0</s:Int64>
-	<s:String x:Key="/Default/Housekeeping/Layout/DialogWindows/RefactoringWizardWindow/Location/@EntryValue">247,-4</s:String></wpf:ResourceDictionary>
+	<s:String x:Key="/Default/Housekeeping/Layout/DialogWindows/RefactoringWizardWindow/Location/@EntryValue">-198,-18</s:String></wpf:ResourceDictionary>

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно