├── bin
└── Debug
│ └── UnityEngine.dll
├── KCP_PInvoke
├── kcp_native_dll
│ ├── ikcp.c
│ ├── stdafx.h
│ ├── dllmain.cpp
│ ├── stdafx.cpp
│ ├── targetver.h
│ ├── x86
│ │ ├── ikcp.dll
│ │ ├── ikcp.pdb
│ │ ├── ikcp.pdb.meta
│ │ └── ikcp.dll.meta
│ ├── x86_64
│ │ ├── ikcp.dll
│ │ ├── ikcp.pdb
│ │ ├── ikcp.pdb.meta
│ │ └── ikcp.dll.meta
│ ├── ReadMe.txt
│ ├── kcpdll.vcxproj.filters
│ ├── ikcp.h
│ └── kcpdll.vcxproj
└── kcp_native.cs
├── KcpProject.csproj.user
├── Unity3D
├── KCPReceiveListener.cs
├── SwitchQueue.cs
├── KCP_H.cs
├── KCPProxy_BE.cs
├── KCPProxy_LE.cs
├── NetworkDebuger.cs
├── KCPSocket.cs
├── kcpUnity3DClientTest.cs
└── KCP_LE.cs
├── common
├── switch_queue.cs
└── kcp.cs
├── Properties
└── AssemblyInfo.cs
├── Program.cs
├── KcpProject.csproj
├── v2
└── udp_socket_v2.cs
├── README.md
└── v1
└── udp_socket_v1.cs
/bin/Debug/UnityEngine.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainsSoft/kcp-csharp/HEAD/bin/Debug/UnityEngine.dll
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/ikcp.c:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainsSoft/kcp-csharp/HEAD/KCP_PInvoke/kcp_native_dll/ikcp.c
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/stdafx.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainsSoft/kcp-csharp/HEAD/KCP_PInvoke/kcp_native_dll/stdafx.h
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/dllmain.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainsSoft/kcp-csharp/HEAD/KCP_PInvoke/kcp_native_dll/dllmain.cpp
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/stdafx.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainsSoft/kcp-csharp/HEAD/KCP_PInvoke/kcp_native_dll/stdafx.cpp
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/targetver.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainsSoft/kcp-csharp/HEAD/KCP_PInvoke/kcp_native_dll/targetver.h
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/x86/ikcp.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainsSoft/kcp-csharp/HEAD/KCP_PInvoke/kcp_native_dll/x86/ikcp.dll
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/x86/ikcp.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainsSoft/kcp-csharp/HEAD/KCP_PInvoke/kcp_native_dll/x86/ikcp.pdb
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/x86_64/ikcp.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainsSoft/kcp-csharp/HEAD/KCP_PInvoke/kcp_native_dll/x86_64/ikcp.dll
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/x86_64/ikcp.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainsSoft/kcp-csharp/HEAD/KCP_PInvoke/kcp_native_dll/x86_64/ikcp.pdb
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/x86/ikcp.pdb.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3c62416f57f0f4d449d21bce03809673
3 | timeCreated: 1525848235
4 | licenseType: Pro
5 | DefaultImporter:
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/x86_64/ikcp.pdb.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ea0a796fb109c154c9b0b68e032a1c52
3 | timeCreated: 1525848138
4 | licenseType: Pro
5 | DefaultImporter:
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/KcpProject.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ShowAllFiles
5 |
6 |
--------------------------------------------------------------------------------
/Unity3D/KCPReceiveListener.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net;
5 | using System.Text;
6 |
7 | namespace Network_Kcp
8 | {
9 | public delegate void KCPReceiveListener(byte[] buff, int size, IPEndPoint remotePoint);
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/ReadMe.txt:
--------------------------------------------------------------------------------
1 | ========================================================================
2 | 动态链接库:kcpdll 项目概述
3 | ========================================================================
4 |
5 | 应用程序向导已为您创建了此 kcp_native_dll DLL。
6 |
7 | 本文件概要介绍组成 kcpdll 应用程序的
8 | 的每个文件的内容。
9 |
10 |
11 | kcpdll.vcproj
12 | 这是使用应用程序向导生成的 VC++ 项目的主项目文件,
13 | 其中包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。
14 |
15 | kcpdll.cpp
16 | 这是主 DLL 源文件。
17 |
18 | 此 DLL 在创建时不导出任何符号。因此,在生成此 DLL 时
19 | 将不会产生 .lib 文件。如果希望此项目
20 | 成为其他某个项目的项目依赖项,则需要
21 | 添加代码以从 DLL 导出某些符号,
22 | 以便产生一个导出库,或者,也可以在项目“属性页”对话框中的
23 | “链接器”文件夹中,将“常规”属性页上的
24 | “忽略输入库”属性设置为“是”。
25 |
26 | /////////////////////////////////////////////////////////////////////////////
27 | 其他标准文件:
28 |
29 | StdAfx.h, StdAfx.cpp
30 | 这些文件用于生成名为 kcpdll.pch 的预编译头 (PCH) 文件和名为 StdAfx.obj 的预编译类型文件。
31 |
32 | /////////////////////////////////////////////////////////////////////////////
33 | 其他注释:
34 |
35 | 应用程序向导使用“TODO:”注释来指示应添加或自定义的源代码部分。
36 |
37 | /////////////////////////////////////////////////////////////////////////////
--------------------------------------------------------------------------------
/common/switch_queue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 |
4 | public class Utility {
5 |
6 | public static void Swap< QT >( ref QT t1, ref QT t2 ) {
7 |
8 | QT temp = t1;
9 | t1 = t2;
10 | t2 = temp;
11 | }
12 | }
13 |
14 | public class SwitchQueue where T : class {
15 |
16 | private Queue mConsumeQueue;
17 | private Queue mProduceQueue;
18 |
19 | public SwitchQueue() {
20 | mConsumeQueue = new Queue(16);
21 | mProduceQueue = new Queue(16);
22 | }
23 |
24 | public SwitchQueue(int capcity) {
25 | mConsumeQueue = new Queue(capcity);
26 | mProduceQueue = new Queue(capcity);
27 | }
28 |
29 | // producer
30 | public void Push( T obj ) {
31 | lock (mProduceQueue)
32 | {
33 | mProduceQueue.Enqueue( obj );
34 | }
35 | }
36 |
37 | // consumer.
38 | public T Pop() {
39 |
40 | return (T)mConsumeQueue.Dequeue();
41 | }
42 |
43 | public bool Empty() {
44 | return 0 == mConsumeQueue.Count;
45 | }
46 |
47 | public void Switch() {
48 | lock (mProduceQueue)
49 | {
50 | Utility.Swap ( ref mConsumeQueue, ref mProduceQueue );
51 | }
52 | }
53 |
54 | public void Clear() {
55 | lock (mProduceQueue)
56 | {
57 | mConsumeQueue.Clear();
58 | mProduceQueue.Clear();
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Unity3D/SwitchQueue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | namespace Network_Kcp
4 | {
5 | public class SwitchQueue where T : class
6 | {
7 |
8 | private Queue mConsumeQueue;
9 | private Queue mProduceQueue;
10 |
11 | public SwitchQueue() {
12 | mConsumeQueue = new Queue(16);
13 | mProduceQueue = new Queue(16);
14 | }
15 |
16 | public SwitchQueue(int capcity) {
17 | mConsumeQueue = new Queue(capcity);
18 | mProduceQueue = new Queue(capcity);
19 | }
20 |
21 | // producer
22 | public void Push(T obj) {
23 | lock (mProduceQueue) {
24 | mProduceQueue.Enqueue(obj);
25 | }
26 | }
27 |
28 | // consumer.
29 | public T Pop() {
30 |
31 | return (T)mConsumeQueue.Dequeue();
32 | }
33 |
34 | public bool Empty() {
35 | return 0 == mConsumeQueue.Count;
36 | }
37 |
38 | public void Switch() {
39 | lock (mProduceQueue) {
40 | Swap(ref mConsumeQueue, ref mProduceQueue);
41 | }
42 | }
43 |
44 | public void Clear() {
45 | lock (mProduceQueue) {
46 | mConsumeQueue.Clear();
47 | mProduceQueue.Clear();
48 | }
49 | }
50 |
51 | public static void Swap(ref QT t1, ref QT t2) {
52 |
53 | QT temp = t1;
54 | t1 = t2;
55 | t2 = temp;
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/kcpdll.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav
15 |
16 |
17 |
18 |
19 | 源文件
20 |
21 |
22 | 源文件
23 |
24 |
25 | 源文件
26 |
27 |
28 |
29 |
30 | 头文件
31 |
32 |
33 | 头文件
34 |
35 |
36 | 头文件
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("KcpProject")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Microsoft")]
12 | [assembly: AssemblyProduct("KcpProject")]
13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("5ae62a35-f172-411a-b95b-502af9dfd222")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/x86_64/ikcp.dll.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e1f796eb30ea0e04697ebd2c2e86f8e5
3 | timeCreated: 1510752959
4 | licenseType: Pro
5 | PluginImporter:
6 | serializedVersion: 1
7 | iconMap: {}
8 | executionOrder: {}
9 | isPreloaded: 0
10 | isOverridable: 0
11 | platformData:
12 | Android:
13 | enabled: 0
14 | settings:
15 | CPU: ARMv7
16 | Any:
17 | enabled: 0
18 | settings:
19 | Exclude Android: 1
20 | Exclude Editor: 0
21 | Exclude Linux: 0
22 | Exclude Linux64: 0
23 | Exclude LinuxUniversal: 0
24 | Exclude OSXIntel: 0
25 | Exclude OSXIntel64: 0
26 | Exclude OSXUniversal: 0
27 | Exclude WebGL: 1
28 | Exclude Win: 0
29 | Exclude Win64: 0
30 | Editor:
31 | enabled: 1
32 | settings:
33 | CPU: x86_64
34 | DefaultValueInitialized: true
35 | OS: Windows
36 | Linux:
37 | enabled: 1
38 | settings:
39 | CPU: None
40 | Linux64:
41 | enabled: 1
42 | settings:
43 | CPU: x86_64
44 | LinuxUniversal:
45 | enabled: 1
46 | settings:
47 | CPU: AnyCPU
48 | OSXIntel:
49 | enabled: 1
50 | settings:
51 | CPU: None
52 | OSXIntel64:
53 | enabled: 1
54 | settings:
55 | CPU: AnyCPU
56 | OSXUniversal:
57 | enabled: 1
58 | settings:
59 | CPU: AnyCPU
60 | Win:
61 | enabled: 1
62 | settings:
63 | CPU: None
64 | Win64:
65 | enabled: 1
66 | settings:
67 | CPU: AnyCPU
68 | userData:
69 | assetBundleName:
70 | assetBundleVariant:
71 |
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/x86/ikcp.dll.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 45a3d2a0bb67fb340ab88e832e378e71
3 | timeCreated: 1510752959
4 | licenseType: Pro
5 | PluginImporter:
6 | serializedVersion: 1
7 | iconMap: {}
8 | executionOrder: {}
9 | isPreloaded: 0
10 | isOverridable: 0
11 | platformData:
12 | Android:
13 | enabled: 0
14 | settings:
15 | CPU: ARMv7
16 | Any:
17 | enabled: 0
18 | settings:
19 | Exclude Android: 1
20 | Exclude Editor: 0
21 | Exclude Linux: 0
22 | Exclude Linux64: 0
23 | Exclude LinuxUniversal: 0
24 | Exclude OSXIntel: 0
25 | Exclude OSXIntel64: 0
26 | Exclude OSXUniversal: 0
27 | Exclude WebGL: 1
28 | Exclude Win: 0
29 | Exclude Win64: 0
30 | Editor:
31 | enabled: 1
32 | settings:
33 | CPU: x86
34 | DefaultValueInitialized: true
35 | OS: Windows
36 | Linux:
37 | enabled: 1
38 | settings:
39 | CPU: x86
40 | Linux64:
41 | enabled: 1
42 | settings:
43 | CPU: None
44 | LinuxUniversal:
45 | enabled: 1
46 | settings:
47 | CPU: AnyCPU
48 | OSXIntel:
49 | enabled: 1
50 | settings:
51 | CPU: AnyCPU
52 | OSXIntel64:
53 | enabled: 1
54 | settings:
55 | CPU: None
56 | OSXUniversal:
57 | enabled: 1
58 | settings:
59 | CPU: AnyCPU
60 | WebGL:
61 | enabled: 0
62 | settings: {}
63 | Win:
64 | enabled: 1
65 | settings:
66 | CPU: AnyCPU
67 | Win64:
68 | enabled: 1
69 | settings:
70 | CPU: None
71 | userData:
72 | assetBundleName:
73 | assetBundleVariant:
74 |
--------------------------------------------------------------------------------
/Unity3D/KCP_H.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | namespace Network_Kcp
7 | {
8 | ///
9 | /// 小端数据模式
10 | ///
11 | public partial class KCP_LE
12 | {
13 | //上下文地址
14 | //public class IQUEUEHEAD
15 | //{
16 | // public IQUEUEHEAD next;
17 | // public IQUEUEHEAD prev;
18 | //}
19 | //public IQUEUEHEAD iqueue_head;
20 | #region 自己加的方法
21 | public void Dispose() {
22 | output = null;
23 | }
24 | public void FastSet() {
25 | rx_minrto = 10;
26 | fastresend = 1;
27 | }
28 | public UInt32 GetConv() {
29 | return conv;
30 | }
31 | public void ResetData() {
32 | Segment[] snd_queue = new Segment[0];
33 | Segment[] rcv_queue = new Segment[0];
34 | Segment[] snd_buf = new Segment[0];
35 | Segment[] rcv_buf = new Segment[0];
36 |
37 | UInt32[] acklist = new UInt32[0];
38 | }
39 | ///
40 | /// reset array buffer size
41 | ///
42 | ///
43 | ///
44 | ///
45 | ///
46 | public static byte[] Recapacity(byte[] self, int length, bool copyData = false) {
47 | byte[] newBytes = new byte[length];
48 | //if (self.Length != length) {
49 | // newBytes = new byte[length];
50 | if (copyData) {
51 | Array.Copy(self, 0, newBytes, 0, length <= self.Length ? length : self.Length);
52 | }
53 | //}
54 | return newBytes;
55 | }
56 | #endregion
57 | }
58 | ///
59 | /// 大端数据模式
60 | ///
61 | public partial class KCP_BE {
62 | #region 自己添加的方法
63 | public void Dispose() {
64 | output = null;
65 | }
66 | #endregion
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 |
7 | class Program
8 | {
9 | static void test_v1(string host, UInt16 port)
10 | {
11 | var wait_response = true;
12 |
13 | KcpProject.v1.UdpSocket client = null;
14 |
15 | client = new KcpProject.v1.UdpSocket((KcpProject.v1.UdpSocket.cliEvent ev, byte[] buf, string err) =>
16 | {
17 | wait_response = false;
18 |
19 | switch (ev)
20 | {
21 | case KcpProject.v1.UdpSocket.cliEvent.Connected:
22 | Console.WriteLine("connected.");
23 | client.Send("Hello KCP.");
24 | break;
25 | case KcpProject.v1.UdpSocket.cliEvent.ConnectFailed:
26 | Console.WriteLine("connect failed. {0}", err);
27 | break;
28 | case KcpProject.v1.UdpSocket.cliEvent.Disconnect:
29 | Console.WriteLine("disconnect. {0}", err);
30 | break;
31 | case KcpProject.v1.UdpSocket.cliEvent.RcvMsg:
32 | Console.WriteLine("recv message: {0}", System.Text.ASCIIEncoding.ASCII.GetString(buf) );
33 | break;
34 | }
35 | });
36 |
37 | client.Connect(host, port);
38 |
39 | while (wait_response)
40 | {
41 | client.Update();
42 | System.Threading.Thread.Sleep(10);
43 | }
44 | }
45 |
46 | static void test_v2(string host, UInt16 port)
47 | {
48 | var wait_response = true;
49 |
50 | KcpProject.v2.UdpSocket client = null;
51 |
52 | // 创建一个实例
53 | client = new KcpProject.v2.UdpSocket((byte[] buf) =>
54 | {
55 | wait_response = false;
56 | Console.WriteLine("recv message: {0}", System.Text.ASCIIEncoding.ASCII.GetString(buf));
57 | });
58 |
59 | // 绑定端口
60 | client.Connect(host, port);
61 |
62 | // 发送消息
63 | client.Send("Hello KCP.");
64 |
65 | // update.
66 | while (wait_response)
67 | {
68 | client.Update();
69 | System.Threading.Thread.Sleep(10);
70 | }
71 | }
72 |
73 | static void Main(string[] args)
74 | {
75 | // 测试v1版本,有握手过程,服务器决定conv的分配
76 | //test_v1("192.168.1.2", 4444);
77 |
78 | //Console.WriteLine("**********************************************************");
79 |
80 | // 测试v2版本,没有握手过程,客户端自行决定conv的分配
81 | // 适合配合 kcp-go
82 | test_v2("192.168.1.2", 4455);
83 | }
84 | }
85 |
86 |
--------------------------------------------------------------------------------
/KcpProject.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {8C02F001-6134-4BC1-AD24-B6F3B458C929}
8 | Exe
9 | Properties
10 | KcpProject
11 | KcpProject
12 | v3.5
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 | true
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | C:\Program Files\Unity5.5.0\Editor\Data\Managed\UnityEngine.dll
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
74 |
--------------------------------------------------------------------------------
/v2/udp_socket_v2.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Net.Sockets;
6 | using System.Net;
7 |
8 | namespace KcpProject.v2
9 | {
10 | // 客户端随机生成conv并作为后续与服务器通信
11 | public class UdpSocket
12 | {
13 | private static readonly DateTime utc_time = new DateTime(1970, 1, 1);
14 |
15 | public static UInt32 iclock()
16 | {
17 | return (UInt32)(Convert.ToInt64(DateTime.UtcNow.Subtract(utc_time).TotalMilliseconds) & 0xffffffff);
18 | }
19 |
20 | private UdpClient mUdpClient;
21 | private IPEndPoint mIPEndPoint;
22 | private IPEndPoint mSvrEndPoint;
23 | private Action evHandler;
24 | private KCP mKcp;
25 | private bool mNeedUpdateFlag;
26 | private UInt32 mNextUpdateTime;
27 |
28 | private SwitchQueue mRecvQueue = new SwitchQueue(128);
29 |
30 | public UdpSocket(Action handler)
31 | {
32 | evHandler = handler;
33 | }
34 |
35 | public void Connect(string host, UInt16 port)
36 | {
37 | mSvrEndPoint = new IPEndPoint(IPAddress.Parse(host), port);
38 | mUdpClient = new UdpClient(host, port);
39 | mUdpClient.Connect(mSvrEndPoint);
40 |
41 | init_kcp((UInt32)new Random((int)DateTime.Now.Ticks).Next(1, Int32.MaxValue));
42 |
43 | mUdpClient.BeginReceive(ReceiveCallback, this);
44 | }
45 |
46 | void init_kcp(UInt32 conv)
47 | {
48 | mKcp = new KCP(conv, (byte[] buf, int size) =>
49 | {
50 | mUdpClient.Send(buf, size);
51 | });
52 |
53 | // fast mode.
54 | mKcp.NoDelay(1, 10, 2, 1);
55 | mKcp.WndSize(128, 128);
56 | }
57 |
58 | void ReceiveCallback(IAsyncResult ar)
59 | {
60 | Byte[] data = (mIPEndPoint == null) ?
61 | mUdpClient.Receive(ref mIPEndPoint) :
62 | mUdpClient.EndReceive(ar, ref mIPEndPoint);
63 |
64 | if (null != data)
65 | {
66 | // push udp packet to switch queue.
67 | mRecvQueue.Push(data);
68 | }
69 |
70 | if (mUdpClient != null)
71 | {
72 | // try to receive again.
73 | mUdpClient.BeginReceive(ReceiveCallback, this);
74 | }
75 | }
76 |
77 | public void Send(byte[] buf)
78 | {
79 | mKcp.Send(buf);
80 | mNeedUpdateFlag = true;
81 | }
82 |
83 | public void Send(string str)
84 | {
85 | Send(System.Text.ASCIIEncoding.ASCII.GetBytes(str));
86 | }
87 |
88 | public void Update()
89 | {
90 | update(iclock());
91 | }
92 |
93 | public void Close()
94 | {
95 | mUdpClient.Close();
96 | }
97 |
98 | void process_recv_queue()
99 | {
100 | mRecvQueue.Switch();
101 |
102 | while (!mRecvQueue.Empty())
103 | {
104 | var buf = mRecvQueue.Pop();
105 |
106 | mKcp.Input(buf);
107 | mNeedUpdateFlag = true;
108 |
109 | for (var size = mKcp.PeekSize(); size > 0; size = mKcp.PeekSize())
110 | {
111 | var buffer = new byte[size];
112 | if (mKcp.Recv(buffer) > 0)
113 | {
114 | evHandler(buffer);
115 | }
116 | }
117 | }
118 | }
119 |
120 | void update(UInt32 current)
121 | {
122 | process_recv_queue();
123 |
124 | if (mNeedUpdateFlag || current >= mNextUpdateTime)
125 | {
126 | mKcp.Update(current);
127 | mNextUpdateTime = mKcp.Check(current);
128 | mNeedUpdateFlag = false;
129 | }
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/Unity3D/KCPProxy_BE.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net;
5 | using System.Net.Sockets;
6 | using System.Text;
7 |
8 | namespace Network_Kcp
9 | {
10 |
11 | public class KCPProxy_BE
12 | {
13 | #region 工具函数
14 | public static IPEndPoint IPEP_Any = new IPEndPoint(IPAddress.Any, 0);
15 | public static IPEndPoint IPEP_IPv6Any = new IPEndPoint(IPAddress.IPv6Any, 0);
16 | public static IPEndPoint GetIPEndPointAny(AddressFamily family, int port) {
17 | if (family == AddressFamily.InterNetwork) {
18 | if (port == 0) {
19 | return IPEP_Any;
20 | }
21 |
22 | return new IPEndPoint(IPAddress.Any, port);
23 | }
24 | else if (family == AddressFamily.InterNetworkV6) {
25 | if (port == 0) {
26 | return IPEP_IPv6Any;
27 | }
28 |
29 | return new IPEndPoint(IPAddress.IPv6Any, port);
30 | }
31 | return null;
32 | }
33 |
34 |
35 | private static readonly DateTime UTCTimeBegin = new DateTime(1970, 1, 1);
36 |
37 | public static long GetClockMS() {
38 | return (Convert.ToInt64(DateTime.UtcNow.Subtract(UTCTimeBegin).TotalMilliseconds) & 0xffffffff);
39 | }
40 |
41 |
42 |
43 | #endregion
44 |
45 |
46 | //private KCP m_Kcp;
47 | private KCP_BE m_Kcp;
48 | private bool m_NeedKcpUpdateFlag = false;
49 | private long m_NextKcpUpdateTime = 0;
50 | private SwitchQueue m_RecvQueue = new SwitchQueue(128);
51 |
52 | private IPEndPoint m_RemotePoint;
53 | private Socket m_Socket;
54 | private KCPReceiveListener m_Listener;
55 |
56 | public IPEndPoint RemotePoint { get { return m_RemotePoint; } }
57 |
58 |
59 |
60 | public KCPProxy_BE(uint key, IPEndPoint remotePoint, Socket socket) {
61 | m_Socket = socket;
62 | m_RemotePoint = remotePoint;
63 |
64 | //m_Kcp = new KCP(key, HandleKcpSend);
65 | m_Kcp = new KCP_BE(key, HandleKcpSend);
66 | m_Kcp.NoDelay(1, 10, 2, 1);
67 | m_Kcp.WndSize(128, 128);
68 |
69 | }
70 |
71 | public void Dispose() {
72 | m_Socket = null;
73 |
74 | if (m_Kcp != null) {
75 | m_Kcp.Dispose();
76 | m_Kcp = null;
77 | }
78 |
79 | m_Listener = null;
80 | }
81 |
82 | //---------------------------------------------
83 | private void HandleKcpSend(byte[] buff, int size) {
84 | //KCP输出回调
85 | if (m_Socket != null) {
86 | m_Socket.SendTo(buff, 0, size, SocketFlags.None, m_RemotePoint);
87 | }
88 | }
89 |
90 | private void HandleKcpSend_Hook(byte[] buff, int size) {
91 | if (m_Socket != null) {
92 | m_Socket.SendTo(buff, 0, size, SocketFlags.None, m_RemotePoint);
93 | }
94 | }
95 |
96 | public bool DoSend(byte[] buff, int size) {
97 | m_NeedKcpUpdateFlag = true;
98 | byte[] dst = new byte[size];
99 | Buffer.BlockCopy(buff, 0, dst, 0, size);
100 | return m_Kcp.Send(dst) >= 0;
101 | }
102 | public bool DoSend(byte[] buff) {
103 | m_NeedKcpUpdateFlag = true;
104 | return m_Kcp.Send(buff) >= 0;
105 | }
106 | //---------------------------------------------
107 |
108 | public void AddReceiveListener(KCPReceiveListener listener) {
109 | m_Listener += listener;
110 | }
111 |
112 | public void RemoveReceiveListener(KCPReceiveListener listener) {
113 | m_Listener -= listener;
114 | }
115 |
116 |
117 |
118 | public void DoReceiveInThread(byte[] buffer, int size) {
119 | byte[] dst = new byte[size];
120 | Buffer.BlockCopy(buffer, 0, dst, 0, size);
121 | m_RecvQueue.Push(dst);
122 | }
123 |
124 | private void HandleRecvQueue() {
125 | m_RecvQueue.Switch();
126 | while (!m_RecvQueue.Empty()) {
127 | var recvBufferRaw = m_RecvQueue.Pop();
128 | int ret = m_Kcp.Input(recvBufferRaw);
129 |
130 | //收到的不是一个正确的KCP包
131 | if (ret < 0) {
132 | if (m_Listener != null) {
133 | m_Listener(recvBufferRaw, recvBufferRaw.Length, m_RemotePoint);
134 | }
135 | return;
136 | }
137 |
138 | m_NeedKcpUpdateFlag = true;
139 |
140 | for (int size = m_Kcp.PeekSize(); size > 0; size = m_Kcp.PeekSize()) {
141 | var recvBuffer = new byte[size];
142 | if (m_Kcp.Recv(recvBuffer) > 0) {
143 | if (m_Listener != null) {
144 | m_Listener(recvBuffer, size, m_RemotePoint);
145 | }
146 | }
147 | }
148 | }
149 | }
150 |
151 | //---------------------------------------------
152 | public void Update(long currentTimeMS) {
153 | HandleRecvQueue();
154 |
155 | if (m_NeedKcpUpdateFlag || currentTimeMS >= m_NextKcpUpdateTime) {
156 | m_Kcp.Update(currentTimeMS);
157 | m_NextKcpUpdateTime = m_Kcp.Check(currentTimeMS);
158 | m_NeedKcpUpdateFlag = false;
159 | }
160 | }
161 |
162 | //---------------------------------------------
163 |
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/Unity3D/KCPProxy_LE.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net;
5 | using System.Net.Sockets;
6 | using System.Text;
7 |
8 | namespace Network_Kcp
9 | {
10 |
11 | public class KCPProxy_LE
12 | {
13 | #region 工具函数
14 | public static IPEndPoint IPEP_Any = new IPEndPoint(IPAddress.Any, 0);
15 | public static IPEndPoint IPEP_IPv6Any = new IPEndPoint(IPAddress.IPv6Any, 0);
16 | public static IPEndPoint GetIPEndPointAny(AddressFamily family, int port) {
17 | if (family == AddressFamily.InterNetwork) {
18 | if (port == 0) {
19 | return IPEP_Any;
20 | }
21 |
22 | return new IPEndPoint(IPAddress.Any, port);
23 | }
24 | else if (family == AddressFamily.InterNetworkV6) {
25 | if (port == 0) {
26 | return IPEP_IPv6Any;
27 | }
28 |
29 | return new IPEndPoint(IPAddress.IPv6Any, port);
30 | }
31 | return null;
32 | }
33 |
34 |
35 | private static readonly DateTime UTCTimeBegin = new DateTime(1970, 1, 1);
36 |
37 | public static long GetClockMS() {
38 | return (Convert.ToInt64(DateTime.UtcNow.Subtract(UTCTimeBegin).TotalMilliseconds) & 0xffffffff);
39 | }
40 |
41 |
42 |
43 | #endregion
44 |
45 |
46 | private KCP_LE m_Kcp;
47 | //private KCP_BE m_Kcp;
48 | private bool m_NeedKcpUpdateFlag = false;
49 | private long m_NextKcpUpdateTime = 0;
50 | private SwitchQueue m_RecvQueue = new SwitchQueue(128);
51 |
52 | private IPEndPoint m_RemotePoint;
53 | private Socket m_Socket;
54 | private KCPReceiveListener m_Listener;
55 |
56 | public IPEndPoint RemotePoint { get { return m_RemotePoint; } }
57 |
58 |
59 |
60 | public KCPProxy_LE(uint key, IPEndPoint remotePoint, Socket socket) {
61 | m_Socket = socket;
62 | m_RemotePoint = remotePoint;
63 |
64 | m_Kcp = new KCP_LE(key, HandleKcpSend);
65 | //m_Kcp = new KCP_BE(key, HandleKcpSend);
66 | m_Kcp.NoDelay(1, 10, 2, 1);
67 | m_Kcp.WndSize(128, 128);
68 |
69 | }
70 |
71 | public void Dispose() {
72 | m_Socket = null;
73 |
74 | if (m_Kcp != null) {
75 | m_Kcp.Dispose();
76 | m_Kcp = null;
77 | }
78 |
79 | m_Listener = null;
80 | }
81 |
82 | //---------------------------------------------
83 | private void HandleKcpSend(byte[] buff, int size) {
84 | //KCP输出回调
85 | if (m_Socket != null) {
86 | m_Socket.SendTo(buff, 0, size, SocketFlags.None, m_RemotePoint);
87 | }
88 | }
89 |
90 | private void HandleKcpSend_Hook(byte[] buff, int size) {
91 | if (m_Socket != null) {
92 | m_Socket.SendTo(buff, 0, size, SocketFlags.None, m_RemotePoint);
93 | }
94 | }
95 |
96 | public bool DoSend(byte[] buff, int size) {
97 | m_NeedKcpUpdateFlag = true;
98 | byte[] dst = new byte[size];
99 | Buffer.BlockCopy(buff, 0, dst, 0, size);
100 | return m_Kcp.Send(dst) >= 0;
101 | }
102 | public bool DoSend(byte[] buff) {
103 | m_NeedKcpUpdateFlag = true;
104 | return m_Kcp.Send(buff) >= 0;
105 | }
106 | //---------------------------------------------
107 |
108 | public void AddReceiveListener(KCPReceiveListener listener) {
109 | m_Listener += listener;
110 | }
111 |
112 | public void RemoveReceiveListener(KCPReceiveListener listener) {
113 | m_Listener -= listener;
114 | }
115 |
116 |
117 |
118 | public void DoReceiveInThread(byte[] buffer, int size) {
119 | byte[] dst = new byte[size];
120 | Buffer.BlockCopy(buffer, 0, dst, 0, size);
121 | m_RecvQueue.Push(dst);
122 | }
123 |
124 | private void HandleRecvQueue() {
125 | m_RecvQueue.Switch();
126 | while (!m_RecvQueue.Empty()) {
127 | var recvBufferRaw = m_RecvQueue.Pop();
128 | int ret = m_Kcp.Input(recvBufferRaw);
129 |
130 | //收到的不是一个正确的KCP包
131 | if (ret < 0) {
132 | if (m_Listener != null) {
133 | m_Listener(recvBufferRaw, recvBufferRaw.Length, m_RemotePoint);
134 | }
135 | return;
136 | }
137 |
138 | m_NeedKcpUpdateFlag = true;
139 |
140 | for (int size = m_Kcp.PeekSize(); size > 0; size = m_Kcp.PeekSize()) {
141 | var recvBuffer = new byte[size];
142 | if (m_Kcp.Recv(recvBuffer) > 0) {
143 | if (m_Listener != null) {
144 | m_Listener(recvBuffer, size, m_RemotePoint);
145 | }
146 | }
147 | }
148 | }
149 | }
150 |
151 | //---------------------------------------------
152 | public void Update(long currentTimeMS) {
153 | HandleRecvQueue();
154 |
155 | if (m_NeedKcpUpdateFlag || currentTimeMS >= m_NextKcpUpdateTime) {
156 | m_Kcp.Update(currentTimeMS);
157 | m_NextKcpUpdateTime = m_Kcp.Check(currentTimeMS);
158 | m_NeedKcpUpdateFlag = false;
159 | }
160 | }
161 |
162 | //---------------------------------------------
163 |
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # kcp-csharp
2 | KCP - A Fast and Reliable ARQ Protocol
3 |
4 | ref:
5 | c: skywind3000 [KCP](https://github.com/skywind3000/kcp)
6 | go: xtaci [kcp-go](https://github.com/xtaci/kcp-go)
7 |
8 |
9 |
10 | KCP - A Fast and Reliable ARQ Protocol
11 | ======================================
12 |
13 | # 简介
14 |
15 | KCP是一个快速可靠协议,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。纯算法实现,并不负责底层协议(如UDP)的收发,需要使用者自己定义下层数据包的发送方式,以 callback的方式提供给 KCP。 连时钟都需要外部传递进来,内部不会有任何一次系统调用。
16 |
17 | 整个协议只有 ikcp.h, ikcp.c两个源文件,可以方便的集成到用户自己的协议栈中。也许你实现了一个P2P,或者某个基于 UDP的协议,而缺乏一套完善的ARQ可靠协议实现,那么简单的拷贝这两个文件到现有项目中,稍微编写两行代码,即可使用。
18 |
19 |
20 | # 技术特性
21 |
22 | TCP是为流量设计的(每秒内可以传输多少KB的数据),讲究的是充分利用带宽。而 KCP是为流速设计的(单个数据包从一端发送到一端需要多少时间),以10%-20%带宽浪费的代价换取了比 TCP快30%-40%的传输速度。TCP信道是一条流速很慢,但每秒流量很大的大运河,而KCP是水流湍急的小激流。KCP有正常模式和快速模式两种,通过以下策略达到提高流速的结果:
23 |
24 | #### RTO翻倍vs不翻倍:
25 |
26 | TCP超时计算是RTOx2,这样连续丢三次包就变成RTOx8了,十分恐怖,而KCP启动快速模式后不x2,只是x1.5(实验证明1.5这个值相对比较好),提高了传输速度。
27 |
28 | #### 选择性重传 vs 全部重传:
29 |
30 | TCP丢包时会全部重传从丢的那个包开始以后的数据,KCP是选择性重传,只重传真正丢失的数据包。
31 |
32 | #### 快速重传:
33 |
34 | 发送端发送了1,2,3,4,5几个包,然后收到远端的ACK: 1, 3, 4, 5,当收到ACK3时,KCP知道2被跳过1次,收到ACK4时,知道2被跳过了2次,此时可以认为2号丢失,不用等超时,直接重传2号包,大大改善了丢包时的传输速度。
35 |
36 | #### 延迟ACK vs 非延迟ACK:
37 |
38 | TCP为了充分利用带宽,延迟发送ACK(NODELAY都没用),这样超时计算会算出较大 RTT时间,延长了丢包时的判断过程。KCP的ACK是否延迟发送可以调节。
39 |
40 | #### UNA vs ACK+UNA:
41 |
42 | ARQ模型响应有两种,UNA(此编号前所有包已收到,如TCP)和ACK(该编号包已收到),光用UNA将导致全部重传,光用ACK则丢失成本太高,以往协议都是二选其一,而 KCP协议中,除去单独的 ACK包外,所有包都有UNA信息。
43 |
44 | #### 非退让流控:
45 |
46 | KCP正常模式同TCP一样使用公平退让法则,即发送窗口大小由:发送缓存大小、接收端剩余接收缓存大小、丢包退让及慢启动这四要素决定。但传送及时性要求很高的小数据时,可选择通过配置跳过后两步,仅用前两项来控制发送频率。以牺牲部分公平性及带宽利用率之代价,换取了开着BT都能流畅传输的效果。
47 |
48 |
49 | # 基本使用
50 |
51 | 1. 创建 KCP对象:
52 |
53 | ```cpp
54 | // 初始化 kcp对象,conv为一个表示会话编号的整数,和tcp的 conv一样,通信双
55 | // 方需保证 conv相同,相互的数据包才能够被认可,user是一个给回调函数的指针
56 | ikcpcb *kcp = ikcp_create(conv, user);
57 | ```
58 |
59 | 2. 设置回调函数:
60 |
61 | ```cpp
62 | // KCP的下层协议输出函数,KCP需要发送数据时会调用它
63 | // buf/len 表示缓存和长度
64 | // user指针为 kcp对象创建时传入的值,用于区别多个 KCP对象
65 | int udp_output(const char *buf, int len, ikcpcb *kcp, void *user)
66 | {
67 | ....
68 | }
69 | // 设置回调函数
70 | kcp->output = udp_output;
71 | ```
72 |
73 | 3. 循环调用 update:
74 |
75 | ```cpp
76 | // 以一定频率调用 ikcp_update来更新 kcp状态,并且传入当前时钟(毫秒单位)
77 | // 如 10ms调用一次,或用 ikcp_check确定下次调用 update的时间不必每次调用
78 | ikcp_update(kcp, millisec);
79 | ```
80 |
81 | 4. 输入一个下层数据包:
82 |
83 | ```cpp
84 | // 收到一个下层数据包(比如UDP包)时需要调用:
85 | ikcp_input(kcp, received_udp_packet, received_udp_size);
86 | ```
87 | 处理了下层协议的输出/输入后 KCP协议就可以正常工作了,使用 ikcp_send 来向
88 | 远端发送数据。而另一端使用 ikcp_recv(kcp, ptr, size)来接收数据。
89 |
90 |
91 | # 协议配置
92 |
93 | 协议默认模式是一个标准的 ARQ,需要通过配置打开各项加速开关:
94 |
95 | 1. 工作模式:
96 | ```cpp
97 | int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc)
98 | ```
99 |
100 | > nodelay :是否启用 nodelay模式,0不启用;1启用。
101 | > interval :协议内部工作的 interval,单位毫秒,比如 10ms或者 20ms
102 | > resend :快速重传模式,默认0关闭,可以设置2(2次ACK跨越将会直接重传)
103 | > nc :是否关闭流控,默认是0代表不关闭,1代表关闭。
104 | > 普通模式:`ikcp_nodelay(kcp, 0, 40, 0, 0);
105 | > 极速模式: ikcp_nodelay(kcp, 1, 10, 2, 1);
106 |
107 | 2. 最大窗口:
108 | ```cpp
109 | int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd);
110 | ```
111 | 该调用将会设置协议的最大发送窗口和最大接收窗口大小,默认为32. 这个可以理解为 TCP的 SND_BUF 和 RCV_BUF,只不过单位不一样 SND/RCV_BUF 单位是字节,这个单位是包。
112 |
113 | 3. 最大传输单元:
114 |
115 | 纯算法协议并不负责探测 MTU,默认 mtu是1400字节,可以使用ikcp_setmtu来设置该值。该值将会影响数据包归并及分片时候的最大传输单元。
116 |
117 | 4. 最小RTO:
118 |
119 | 不管是 TCP还是 KCP计算 RTO时都有最小 RTO的限制,即便计算出来RTO为40ms,由于默认的 RTO是100ms,协议只有在100ms后才能检测到丢包,快速模式下为30ms,可以手动更改该值:
120 | ```cpp
121 | kcp->rx_minrto = 10;
122 | ```
123 |
124 |
125 | # 文档索引
126 |
127 | 协议的使用和配置都是很简单的,大部分情况看完上面的内容基本可以使用了。如果你需要进一步进行精细的控制,比如改变 KCP的内存分配器,或者你需要更有效的大规模调度 KCP链接(比如 3500个以上),或者如何更好的同 TCP结合,那么可以继续延伸阅读:
128 |
129 | - [Wiki Home](https://github.com/skywind3000/kcp/wiki)
130 | - [KCP 最佳实践](https://github.com/skywind3000/kcp/wiki/KCP-Best-Practice)
131 | - [同现有TCP服务器集成](https://github.com/skywind3000/kcp/wiki/Cooperate-With-Tcp-Server)
132 | - [传输数据加密](https://github.com/skywind3000/kcp/wiki/Network-Encryption)
133 | - [应用层流量控制](https://github.com/skywind3000/kcp/wiki/Flow-Control-for-Users)
134 | - [性能评测](https://github.com/skywind3000/kcp/wiki/KCP-Benchmark)
135 |
136 |
137 | # 相关应用
138 |
139 | - [dog-tunnel](https://github.com/vzex/dog-tunnel): GO开发的网络隧道,使用 KCP极大的改进了传输速度,并移植了一份 GO版本 KCP
140 | - [lua-kcp](https://github.com/linxiaolong/lua-kcp): KCP的 Lua扩展,用于 Lua服务器
141 | - [asio-kcp](https://github.com/libinzhangyuan/asio_kcp): 使用 KCP的完整 UDP网络库,完整实现了基于 UDP的链接状态管理,会话控制,KCP协议调度等
142 | - [kcp-go](https://github.com/xtaci/kcp-go): 另一份对 KCP更精简的 GO移植,同时包含简单的 UDP会话管理,可以直接使用
143 | - [kcp-csharp](https://github.com/limpo1989/kcp-csharp): kcp的csharp移植,同时包含一份回话管理,可以连接上面kcp-go的服务端。
144 | - [kcptun](https://github.com/xtaci/kcptun): 基于kcpgo做的远程端口转发,配合ssh -D,可以比 sslocal/ssserver 更流畅的看在线视频。
145 | - [kcpuv](https://github.com/elisaday/kcpuv): 使用 libuv开发的kcpuv库,目前还在 Demo阶段。
146 |
147 | # 协议比较
148 |
149 | 如果网络永远不卡,那 KCP/TCP 表现类似,但是网络本身就是不可靠的,丢包和抖动无法避免(否则还要各种可靠协议干嘛)。在内网这种几乎理想的环境里直接比较,大家都差不多,但是放到公网上,放到3G/4G网络情况下,或者使用内网丢包模拟,差距就很明显了。公网在高峰期有平均接近10%的丢包,wifi/3g/4g下更糟糕,这些都会让传输变卡。
150 |
151 | 感谢 [asio-kcp](https://github.com/libinzhangyuan/asio_kcp) 的作者 [zhangyuan](https://github.com/libinzhangyuan) 对 KCP 与 enet, udt做过的一次横向评测,结论如下:
152 |
153 | - ASIO-KCP **has good performace in wifi and phone network(3G, 4G)**.
154 | - The kcp is the **first choice for realtime pvp game**.
155 | - The lag is less than 1 second when network lag happen. **3 times better than enet** when lag happen.
156 | - The enet is a good choice if your game allow 2 second lag.
157 | - **UDT is a bad idea**. It always sink into badly situation of more than serval seconds lag. And the recovery is not expected.
158 | - enet has the problem of lack of doc. And it has lots of functions that you may intrest.
159 | - kcp's doc is chinese. Good thing is the function detail which is writen in code is english. And you can use asio_kcp which is a good wrap.
160 | - The kcp is a simple thing. You will write more code if you want more feature.
161 | - UDT has a perfect doc. UDT may has more bug than others as I feeling.
162 |
163 | 具体见:[横向比较](https://github.com/libinzhangyuan/reliable_udp_bench_mark) 和 [评测数据](https://github.com/skywind3000/kcp/wiki/KCP-Benchmark),为犹豫选择的人提供了更多指引。
164 |
--------------------------------------------------------------------------------
/Unity3D/NetworkDebuger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using UnityEngine;
7 |
8 | namespace Network_Kcp
9 | {
10 | public class NetworkDebuger
11 | {
12 | public static bool EnableLog;
13 | public static bool EnableTime = false;
14 | public static bool EnableSave = false;
15 | public static bool EnableStack = false;
16 | public static string LogFileDir = Application.persistentDataPath + "/NetworkLog/";
17 | public static string LogFileName = "";
18 | public static string Prefix = "> ";
19 | public static StreamWriter LogFileWriter = null;
20 |
21 |
22 | public static void Log(object message) {
23 | if (!NetworkDebuger.EnableLog) {
24 | return;
25 | }
26 |
27 | string msg = GetLogTime() + message;
28 |
29 | //Debug.Log(Prefix + msg, null);
30 | LogToFile("[I]" + msg);
31 | }
32 |
33 | //public static void Log(object message, object context) {
34 | // if (!NetworkDebuger.EnableLog) {
35 | // return;
36 | // }
37 |
38 | // string msg = GetLogTime() + message;
39 |
40 | // //Debug.Log(Prefix + msg, context);
41 | // LogToFile("[I]" + msg);
42 | //}
43 |
44 | public static void LogError(object message) {
45 | string msg = GetLogTime() + message;
46 |
47 | Debug.LogError(Prefix + msg, null);
48 | LogToFile("[E]" + msg, true);
49 | }
50 |
51 | //public static void LogError(object message, object context) {
52 | // string msg = GetLogTime() + message;
53 |
54 | // Debug.LogError(Prefix + msg, context);
55 | // LogToFile("[E]" + msg, true);
56 | //}
57 |
58 | public static void LogWarning(object message) {
59 | string msg = GetLogTime() + message;
60 |
61 | Debug.LogWarning(Prefix + msg, null);
62 | LogToFile("[W]" + msg);
63 | }
64 |
65 | //public static void LogWarning(object message, Object context) {
66 | // string msg = GetLogTime() + message;
67 |
68 | // Debug.LogWarning(Prefix + msg, context);
69 | // LogToFile("[W]" + msg);
70 | //}
71 |
72 |
73 | //----------------------------------------------------------------------
74 |
75 | public static void Log(string tag, string message) {
76 | if (!NetworkDebuger.EnableLog) {
77 | return;
78 | }
79 |
80 | message = GetLogText(tag, message);
81 | //Debug.Log(Prefix + message);
82 | LogToFile("[I]" + message);
83 | }
84 |
85 | public static void Log(string tag, string format, params object[] args) {
86 | if (!NetworkDebuger.EnableLog) {
87 | return;
88 | }
89 |
90 | string message = GetLogText(tag, string.Format(format, args));
91 | //Debug.Log(Prefix + message);
92 | LogToFile("[I]" + message);
93 | }
94 |
95 | public static void LogError(string tag, string message) {
96 | message = GetLogText(tag, message);
97 | Debug.LogError(Prefix + message);
98 | LogToFile("[E]" + message, true);
99 | }
100 |
101 | public static void LogError(string tag, string format, params object[] args) {
102 | string message = GetLogText(tag, string.Format(format, args));
103 | Debug.LogError(Prefix + message);
104 | LogToFile("[E]" + message, true);
105 | }
106 |
107 |
108 | public static void LogWarning(string tag, string message) {
109 | message = GetLogText(tag, message);
110 | Debug.LogWarning(Prefix + message);
111 | LogToFile("[W]" + message);
112 | }
113 |
114 | public static void LogWarning(string tag, string format, params object[] args) {
115 | string message = GetLogText(tag, string.Format(format, args));
116 | Debug.LogWarning(Prefix + message);
117 | LogToFile("[W]" + message);
118 | }
119 |
120 |
121 | //----------------------------------------------------------------------
122 | private static string GetLogText(string tag, string message) {
123 | string str = "";
124 | if (NetworkDebuger.EnableTime) {
125 | DateTime now = DateTime.Now;
126 | str = now.ToString("HH:mm:ss.fff") + " ";
127 | }
128 |
129 | str = str + tag + "::" + message;
130 | return str;
131 | }
132 |
133 | private static string GetLogTime() {
134 | string str = "";
135 | if (NetworkDebuger.EnableTime) {
136 | DateTime now = DateTime.Now;
137 | str = now.ToString("HH:mm:ss.fff") + " ";
138 | }
139 | return str;
140 | }
141 |
142 |
143 |
144 | private static void LogToFile(string message, bool EnableStack = false) {
145 | if (!EnableSave) {
146 | if (LogFileWriter != null) {
147 | LogFileWriter.Flush();
148 | LogFileWriter.Close();
149 | LogFileWriter = null;
150 | }
151 | return;
152 | }
153 |
154 | if (LogFileWriter == null) {
155 | DateTime now = DateTime.Now;
156 | LogFileName = now.GetDateTimeFormats('s')[0].ToString();//2005-11-05T14:06:25
157 | LogFileName = LogFileName.Replace("-", "_");
158 | LogFileName = LogFileName.Replace(":", "_");
159 | LogFileName = LogFileName.Replace(" ", "");
160 | LogFileName += ".log";
161 |
162 | string fullpath = LogFileDir + LogFileName;
163 | try {
164 | if (!Directory.Exists(LogFileDir)) {
165 | Directory.CreateDirectory(LogFileDir);
166 | }
167 |
168 | LogFileWriter = File.AppendText(fullpath);
169 | LogFileWriter.AutoFlush = true;
170 | }
171 | catch (Exception e) {
172 | LogFileWriter = null;
173 | Debug.LogError("LogToCache() " + e.Message + e.StackTrace);
174 | return;
175 | }
176 | }
177 |
178 | if (LogFileWriter != null) {
179 | try {
180 | LogFileWriter.WriteLine(message);
181 | if (EnableStack || NetworkDebuger.EnableStack) {
182 | LogFileWriter.WriteLine(StackTraceUtility.ExtractStackTrace());
183 | }
184 | }
185 | catch (Exception) {
186 | return;
187 | }
188 | }
189 | }
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/v1/udp_socket_v1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net;
5 | using System.Net.Sockets;
6 | using System.Text;
7 |
8 | namespace KcpProject.v1
9 | {
10 |
11 | // 模拟TCP客户端主动发送握手数据
12 | // 服务器下发conv
13 | public class UdpSocket
14 | {
15 |
16 | private static readonly DateTime utc_time = new DateTime(1970, 1, 1);
17 |
18 | public static UInt32 iclock()
19 | {
20 | return (UInt32)(Convert.ToInt64(DateTime.UtcNow.Subtract(utc_time).TotalMilliseconds) & 0xffffffff);
21 | }
22 |
23 | public enum cliEvent
24 | {
25 | Connected = 0,
26 | ConnectFailed = 1,
27 | Disconnect = 2,
28 | RcvMsg = 3,
29 | }
30 |
31 | private const UInt32 CONNECT_TIMEOUT = 5000;
32 | private const UInt32 RESEND_CONNECT = 500;
33 |
34 | private UdpClient mUdpClient;
35 | private IPEndPoint mIPEndPoint;
36 | private IPEndPoint mSvrEndPoint;
37 | private Action evHandler;
38 | private KCP mKcp;
39 | private bool mNeedUpdateFlag;
40 | private UInt32 mNextUpdateTime;
41 |
42 | private bool mInConnectStage;
43 | private bool mConnectSucceed;
44 | private UInt32 mConnectStartTime;
45 | private UInt32 mLastSendConnectTime;
46 |
47 | private SwitchQueue mRecvQueue = new SwitchQueue(128);
48 |
49 | public UdpSocket(Action handler)
50 | {
51 | evHandler = handler;
52 | }
53 |
54 | public void Connect(string host, UInt16 port)
55 | {
56 | mSvrEndPoint = new IPEndPoint(IPAddress.Parse(host), port);
57 | mUdpClient = new UdpClient(host, port);
58 | mUdpClient.Connect(mSvrEndPoint);
59 |
60 | reset_state();
61 |
62 | mInConnectStage = true;
63 | mConnectStartTime = iclock();
64 |
65 | mUdpClient.BeginReceive(ReceiveCallback, this);
66 | }
67 |
68 | void ReceiveCallback(IAsyncResult ar)
69 | {
70 | Byte[] data = (mIPEndPoint == null) ?
71 | mUdpClient.Receive(ref mIPEndPoint) :
72 | mUdpClient.EndReceive(ar, ref mIPEndPoint);
73 |
74 | if (null != data)
75 | OnData(data);
76 |
77 | if (mUdpClient != null)
78 | {
79 | // try to receive again.
80 | mUdpClient.BeginReceive(ReceiveCallback, this);
81 | }
82 | }
83 |
84 | void OnData(byte[] buf)
85 | {
86 | mRecvQueue.Push(buf);
87 | }
88 |
89 | void reset_state()
90 | {
91 | mNeedUpdateFlag = false;
92 | mNextUpdateTime = 0;
93 |
94 | mInConnectStage = false;
95 | mConnectSucceed = false;
96 | mConnectStartTime = 0;
97 | mLastSendConnectTime = 0;
98 | mRecvQueue.Clear();
99 | mKcp = null;
100 | }
101 |
102 | string dump_bytes(byte[] buf, int size)
103 | {
104 | var sb = new StringBuilder(size * 2);
105 | for (var i = 0; i < size; i++)
106 | {
107 | sb.Append(buf[i]);
108 | sb.Append(" ");
109 | }
110 | return sb.ToString();
111 | }
112 |
113 | void init_kcp(UInt32 conv)
114 | {
115 | mKcp = new KCP(conv, (byte[] buf, int size) =>
116 | {
117 | mUdpClient.Send(buf, size);
118 | });
119 |
120 | mKcp.NoDelay(1, 10, 2, 1);
121 | }
122 |
123 | public void Send(byte[] buf)
124 | {
125 | mKcp.Send(buf);
126 | mNeedUpdateFlag = true;
127 | }
128 |
129 | public void Send(string str)
130 | {
131 | Send(System.Text.ASCIIEncoding.ASCII.GetBytes(str));
132 | }
133 |
134 | public void Update()
135 | {
136 | update(iclock());
137 | }
138 |
139 | public void Close()
140 | {
141 | mUdpClient.Close();
142 | evHandler(cliEvent.Disconnect, null, "Closed");
143 | }
144 |
145 | void process_connect_packet()
146 | {
147 | mRecvQueue.Switch();
148 |
149 | if (!mRecvQueue.Empty())
150 | {
151 | var buf = mRecvQueue.Pop();
152 |
153 | UInt32 conv = 0;
154 | KCP.ikcp_decode32u(buf, 0, ref conv);
155 |
156 | if (conv <= 0)
157 | throw new Exception("inlvaid connect back packet");
158 |
159 | init_kcp(conv);
160 |
161 | mInConnectStage = false;
162 | mConnectSucceed = true;
163 |
164 | evHandler(cliEvent.Connected, null, null);
165 | }
166 | }
167 |
168 | void process_recv_queue()
169 | {
170 | mRecvQueue.Switch();
171 |
172 | while (!mRecvQueue.Empty())
173 | {
174 | var buf = mRecvQueue.Pop();
175 |
176 | mKcp.Input(buf);
177 | mNeedUpdateFlag = true;
178 |
179 | for (var size = mKcp.PeekSize(); size > 0; size = mKcp.PeekSize())
180 | {
181 | var buffer = new byte[size];
182 | if (mKcp.Recv(buffer) > 0)
183 | {
184 | evHandler(cliEvent.RcvMsg, buffer, null);
185 | }
186 | }
187 | }
188 | }
189 |
190 | bool connect_timeout(UInt32 current)
191 | {
192 | return current - mConnectStartTime > CONNECT_TIMEOUT;
193 | }
194 |
195 | bool need_send_connect_packet(UInt32 current)
196 | {
197 | return current - mLastSendConnectTime > RESEND_CONNECT;
198 | }
199 |
200 | void update(UInt32 current)
201 | {
202 | if (mInConnectStage)
203 | {
204 | if (connect_timeout(current))
205 | {
206 | evHandler(cliEvent.ConnectFailed, null, "Timeout");
207 | mInConnectStage = false;
208 | return;
209 | }
210 |
211 | if (need_send_connect_packet(current))
212 | {
213 | mLastSendConnectTime = current;
214 | mUdpClient.Send(new byte[4] { 0, 0, 0, 0 }, 4);
215 | }
216 |
217 | process_connect_packet();
218 |
219 | return;
220 | }
221 |
222 | if (mConnectSucceed)
223 | {
224 | process_recv_queue();
225 |
226 | if (mNeedUpdateFlag || current >= mNextUpdateTime)
227 | {
228 | mKcp.Update(current);
229 | mNextUpdateTime = mKcp.Check(current);
230 | mNeedUpdateFlag = false;
231 | }
232 | }
233 | }
234 | }
235 |
236 | }
--------------------------------------------------------------------------------
/Unity3D/KCPSocket.cs:
--------------------------------------------------------------------------------
1 | //#define BigEndian
2 | #define LittleEndian
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Net;
6 | using System.Net.Sockets;
7 | using System.Text;
8 | using System.Threading;
9 | using UnityEngine;
10 | #if BigEndian
11 | using KCPProxy = Network_Kcp.KCPProxy_BE;
12 | #else
13 | using KCPProxy = Network_Kcp.KCPProxy_LE;
14 | #endif
15 | namespace Network_Kcp
16 | {
17 | public class KCPSocket
18 | {
19 |
20 |
21 |
22 | public string LOG_TAG = "KCPSocket";
23 |
24 | private bool m_IsRunning = false;
25 | private Socket m_SystemSocket;
26 | private IPEndPoint m_LocalEndPoint;
27 | private AddressFamily m_AddrFamily;
28 | private Thread m_ThreadRecv;
29 | private byte[] m_RecvBufferTemp = new byte[4096];
30 |
31 | //KCP参数
32 | private List m_ListKcp;
33 | private uint m_KcpKey = 0;
34 | private KCPReceiveListener m_AnyEPListener;
35 |
36 | //=================================================================================
37 | #region 构造和析构
38 |
39 | public KCPSocket(int bindPort, uint kcpKey, AddressFamily family = AddressFamily.InterNetwork) {
40 | m_AddrFamily = family;
41 | m_KcpKey = kcpKey;
42 | m_ListKcp = new List();
43 |
44 | m_SystemSocket = new Socket(m_AddrFamily, SocketType.Dgram, ProtocolType.Udp);
45 | IPEndPoint ipep = KCPProxy.GetIPEndPointAny(m_AddrFamily, bindPort);
46 | m_SystemSocket.Bind(ipep);
47 |
48 | bindPort = (m_SystemSocket.LocalEndPoint as IPEndPoint).Port;
49 | LOG_TAG = "KCPSocket[" + bindPort + "-" + kcpKey + "]";
50 |
51 | m_IsRunning = true;
52 | m_ThreadRecv = new Thread(Thread_Recv) { IsBackground = true };
53 | m_ThreadRecv.Start();
54 |
55 |
56 |
57 | #if UNITY_EDITOR_WIN
58 | uint IOC_IN = 0x80000000;
59 | uint IOC_VENDOR = 0x18000000;
60 | uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
61 | m_SystemSocket.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
62 | #endif
63 |
64 |
65 | #if UNITY_EDITOR
66 | UnityEditor.EditorApplication.playmodeStateChanged -= OnEditorPlayModeChanged;
67 | UnityEditor.EditorApplication.playmodeStateChanged += OnEditorPlayModeChanged;
68 | #endif
69 | }
70 |
71 |
72 | #if UNITY_EDITOR
73 | private void OnEditorPlayModeChanged()
74 | {
75 | if (Application.isPlaying == false)
76 | {
77 | this.Log("OnEditorPlayModeChanged()");
78 | UnityEditor.EditorApplication.playmodeStateChanged -= OnEditorPlayModeChanged;
79 | Dispose();
80 | }
81 | }
82 | #endif
83 |
84 | public void Dispose() {
85 | m_IsRunning = false;
86 | m_AnyEPListener = null;
87 |
88 | if (m_ThreadRecv != null) {
89 | m_ThreadRecv.Interrupt();
90 | m_ThreadRecv = null;
91 | }
92 |
93 | int cnt = m_ListKcp.Count;
94 | for (int i = 0; i < cnt; i++) {
95 | m_ListKcp[i].Dispose();
96 | }
97 | m_ListKcp.Clear();
98 |
99 | if (m_SystemSocket != null) {
100 | try {
101 | m_SystemSocket.Shutdown(SocketShutdown.Both);
102 | }
103 | catch (Exception e) {
104 | NetworkDebuger.LogWarning("Close() " + e.Message + e.StackTrace);
105 | }
106 |
107 | m_SystemSocket.Close();
108 | m_SystemSocket = null;
109 | }
110 | }
111 |
112 |
113 | public int LocalPort {
114 | get { return (m_SystemSocket.LocalEndPoint as IPEndPoint).Port; }
115 | }
116 |
117 | public string LocalIP {
118 | get { return UnityEngine.Network.player.ipAddress; }
119 | }
120 |
121 | public IPEndPoint LocalEndPoint {
122 | get {
123 | if (m_LocalEndPoint == null ||
124 | m_LocalEndPoint.Address.ToString() != UnityEngine.Network.player.ipAddress) {
125 | IPAddress ip = IPAddress.Parse(LocalIP);
126 | m_LocalEndPoint = new IPEndPoint(ip, LocalPort);
127 | }
128 |
129 | return m_LocalEndPoint;
130 | }
131 | }
132 |
133 | public Socket SystemSocket { get { return m_SystemSocket; } }
134 |
135 | #endregion
136 |
137 | //=================================================================================
138 |
139 | public bool EnableBroadcast {
140 | get { return m_SystemSocket.EnableBroadcast; }
141 | set { m_SystemSocket.EnableBroadcast = value; }
142 | }
143 |
144 | //=================================================================================
145 | #region 管理KCP
146 |
147 | private KCPProxy GetKcp(IPEndPoint ipep) {
148 | if (ipep == null || ipep.Port == 0 ||
149 | ipep.Address.Equals(IPAddress.Any) ||
150 | ipep.Address.Equals(IPAddress.IPv6Any)) {
151 | return null;
152 | }
153 |
154 | KCPProxy proxy;
155 | int cnt = m_ListKcp.Count;
156 | for (int i = 0; i < cnt; i++) {
157 | proxy = m_ListKcp[i];
158 | if (proxy.RemotePoint.Equals(ipep)) {
159 | return proxy;
160 | }
161 | }
162 |
163 | proxy = new KCPProxy(m_KcpKey, ipep, m_SystemSocket);
164 | proxy.AddReceiveListener(OnReceiveAny);
165 | m_ListKcp.Add(proxy);
166 | return proxy;
167 | }
168 |
169 | #endregion
170 |
171 | //=================================================================================
172 | #region 发送逻辑
173 | public bool SendTo(byte[] buffer, int size, IPEndPoint remotePoint) {
174 | if (remotePoint.Address == IPAddress.Broadcast) {
175 | int cnt = m_SystemSocket.SendTo(buffer, size, SocketFlags.None, remotePoint);
176 | return cnt > 0;
177 | }
178 | else {
179 | KCPProxy proxy = GetKcp(remotePoint);
180 | if (proxy != null) {
181 | return proxy.DoSend(buffer, size);
182 | }
183 | }
184 |
185 | return false;
186 | }
187 |
188 | public bool SendTo(string message, IPEndPoint remotePoint) {
189 | byte[] buffer = Encoding.UTF8.GetBytes(message);
190 | return SendTo(buffer, buffer.Length, remotePoint);
191 | }
192 |
193 | #endregion
194 |
195 |
196 | //=================================================================================
197 | #region 主线程驱动
198 | ushort keepHeartbeat = 0;
199 | const string HeartbeatMsg = "Heartbeat";
200 | byte[] HeartbeatMsgBuffer = Encoding.UTF8.GetBytes(HeartbeatMsg);
201 | public void SendKeepHeartbeat(IPEndPoint remotePoint) {
202 | keepHeartbeat++;
203 | if (keepHeartbeat > 500) {
204 | keepHeartbeat = 0;
205 | SendTo(HeartbeatMsgBuffer, HeartbeatMsgBuffer.Length, remotePoint);
206 | }
207 | }
208 | public void Update() {
209 |
210 | if (m_IsRunning) {
211 | //获取时钟
212 | long current = KCPProxy.GetClockMS();
213 |
214 | int cnt = m_ListKcp.Count;
215 | for (int i = 0; i < cnt; i++) {
216 | KCPProxy proxy = m_ListKcp[i];
217 | proxy.Update(current);
218 | }
219 | }
220 | }
221 |
222 | #endregion
223 |
224 | //=================================================================================
225 | #region 接收逻辑
226 |
227 | public void AddReceiveListener(IPEndPoint remotePoint, KCPReceiveListener listener) {
228 | KCPProxy proxy = GetKcp(remotePoint);
229 | if (proxy != null) {
230 | proxy.AddReceiveListener(listener);
231 | }
232 | else {
233 | m_AnyEPListener += listener;
234 | }
235 | }
236 |
237 | public void RemoveReceiveListener(IPEndPoint remotePoint, KCPReceiveListener listener) {
238 | KCPProxy proxy = GetKcp(remotePoint);
239 | if (proxy != null) {
240 | proxy.RemoveReceiveListener(listener);
241 | }
242 | else {
243 | m_AnyEPListener -= listener;
244 | }
245 | }
246 |
247 | public void AddReceiveListener(KCPReceiveListener listener) {
248 | m_AnyEPListener += listener;
249 | }
250 |
251 | public void RemoveReceiveListener(KCPReceiveListener listener) {
252 | m_AnyEPListener -= listener;
253 | }
254 |
255 |
256 | private void OnReceiveAny(byte[] buffer, int size, IPEndPoint remotePoint) {
257 | if (m_AnyEPListener != null) {
258 | m_AnyEPListener(buffer, size, remotePoint);
259 | }
260 | }
261 |
262 | #endregion
263 |
264 | //=================================================================================
265 | #region 接收线程
266 |
267 | private void Thread_Recv() {
268 | NetworkDebuger.Log("Thread_Recv() Begin ......");
269 |
270 | while (m_IsRunning) {
271 | try {
272 | DoReceive();
273 | }
274 | catch (Exception e) {
275 | NetworkDebuger.LogError("Thread_Recv() " + e.Message + "\n" + e.StackTrace);
276 | Thread.Sleep(10);
277 | }
278 | }
279 |
280 | NetworkDebuger.Log("Thread_Recv() End!");
281 | }
282 |
283 | private void DoReceive() {
284 | if (m_SystemSocket.Available <= 0) {
285 | return;
286 | }
287 |
288 | EndPoint remotePoint = new IPEndPoint(IPAddress.Any, 0);
289 | int cnt = m_SystemSocket.ReceiveFrom(m_RecvBufferTemp, m_RecvBufferTemp.Length,
290 | SocketFlags.None, ref remotePoint);
291 |
292 | if (cnt > 0) {
293 | KCPProxy proxy = GetKcp((IPEndPoint)remotePoint);
294 | if (proxy != null) {
295 | proxy.DoReceiveInThread(m_RecvBufferTemp, cnt);
296 | }
297 | }
298 |
299 | }
300 |
301 | #endregion
302 | }
303 |
304 |
305 |
306 |
307 |
308 | }
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native.cs:
--------------------------------------------------------------------------------
1 |
2 | //KCP_NATIVE 使用P/Invoke调用KCP协议相关处理方法,这种方式尽量规避了KCP源代码C->C#改写过程中造成的问题。
3 | //目前C#版KCP在长时间运行后,IOPS会下降,原因尚不明确。
4 | //简单测试阶段,可以使用C#版
5 |
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Runtime.InteropServices;
10 | using System.Text;
11 | using System.Threading;
12 |
13 | namespace KCP.NetWrapper {
14 | class kcp_native {
15 | #if UNITY_IOS
16 | const string KCPDLL="__Internal";
17 | #elif UNITY_ANDROID
18 | const string KCPDLL="libikcp";
19 | #elif UNITY_EDITOR || UNITY_STANDALONE
20 | const string KCPDLL = "ikcp";
21 | #else
22 | const string KCPDLL = "ikcp";
23 | #endif
24 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
25 | public static extern IntPtr ikcp_create(int conv, IntPtr user);
26 |
27 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
28 | public static extern void ikcp_release(IntPtr kcp);
29 |
30 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
31 | public static extern void ikcp_setoutput(IntPtr kcp, Delegate output);
32 |
33 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
34 | public static extern int ikcp_recv(IntPtr kcp, IntPtr buffer, int len);
35 |
36 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
37 | public static extern int ikcp_send(IntPtr kcp, IntPtr buffer, int len);
38 |
39 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
40 | public static extern void ikcp_update(IntPtr kcp, uint current);
41 |
42 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
43 | public static extern uint ikcp_check(IntPtr kcp, uint current);
44 |
45 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
46 | public static extern int ikcp_input(IntPtr kcp, IntPtr data, long size);
47 |
48 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
49 | public static extern int ikcp_peeksize(IntPtr kcp);
50 |
51 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
52 | public static extern int ikcp_setmtu(IntPtr kcp, int mtu);
53 |
54 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
55 | public static extern uint ikcp_getconv(IntPtr buf);
56 |
57 |
58 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
59 | public static extern int ikcp_get_interval(IntPtr buf);
60 | ///
61 | /// 设置内存分配和释放方法
62 | ///
63 | ///
64 | ///
65 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
66 | public static extern void ikcp_allocator(Delegate bufalloc_callback, Delegate buffree_callbak);
67 |
68 |
69 | //参考:https://github.com/skywind3000/kcp/wiki/Flow-Control-for-Users
70 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
71 | public static extern int ikcp_waitsnd(IntPtr kcp);
72 |
73 |
74 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
75 | public static extern int ikcp_wndsize(IntPtr kcp, int sndwnd, int rcvwnd);
76 |
77 |
78 |
79 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
80 | public static extern int ikcp_nodelay(IntPtr kcp, int nodelay, int interval, int resend, int nc);
81 |
82 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
83 | public static extern int ikcp_flush(IntPtr kcp);
84 |
85 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
86 | public static extern void ikcp_setminrto(IntPtr kcp, int minrto);
87 | [DllImport(KCPDLL, CallingConvention = CallingConvention.Cdecl)]
88 | public static extern int ikcp_getminrto(IntPtr kcp);
89 | }
90 |
91 | public partial class KCPLib {
92 | // encode 8 bits unsigned int
93 | public static int ikcp_encode8u(byte[] p, int offset, byte c) {
94 | p[0 + offset] = c;
95 | return 1;
96 | }
97 |
98 | // decode 8 bits unsigned int
99 | public static int ikcp_decode8u(byte[] p, int offset, ref byte c) {
100 | c = p[0 + offset];
101 | return 1;
102 | }
103 |
104 | /* encode 16 bits unsigned int (lsb) */
105 | public static int ikcp_encode16u(byte[] p, int offset, UInt16 w) {
106 | p[0 + offset] = (byte)(w >> 0);
107 | p[1 + offset] = (byte)(w >> 8);
108 | return 2;
109 | }
110 |
111 | /* decode 16 bits unsigned int (lsb) */
112 | public static int ikcp_decode16u(byte[] p, int offset, ref UInt16 c) {
113 | UInt16 result = 0;
114 | result |= (UInt16)p[0 + offset];
115 | result |= (UInt16)(p[1 + offset] << 8);
116 | c = result;
117 | return 2;
118 | }
119 |
120 | /* encode 32 bits unsigned int (lsb) */
121 | public static int ikcp_encode32u(byte[] p, int offset, UInt32 l) {
122 | p[0 + offset] = (byte)(l >> 0);
123 | p[1 + offset] = (byte)(l >> 8);
124 | p[2 + offset] = (byte)(l >> 16);
125 | p[3 + offset] = (byte)(l >> 24);
126 | return 4;
127 | }
128 |
129 | /* decode 32 bits unsigned int (lsb) */
130 | public static int ikcp_decode32u(byte[] p, int offset, ref UInt32 c) {
131 | UInt32 result = 0;
132 | result |= (UInt32)p[0 + offset];
133 | result |= (UInt32)(p[1 + offset] << 8);
134 | result |= (UInt32)(p[2 + offset] << 16);
135 | result |= (UInt32)(p[3 + offset] << 24);
136 | c = result;
137 | return 4;
138 | }
139 |
140 |
141 | //BufferChunk m_bufChunk;// = new BufferChunk(16 * 1024, 64 * 1024 * 1024);
142 | IntPtr BufAlloc(int size) {
143 | if(size > 16 * 1024) {
144 | throw new Exception("BufAlloc size过大 size:" + size.ToString());
145 | }
146 | return BufferPoolForNative.GetBuf();
147 | }
148 | void BufFree(IntPtr p) {
149 | BufferPoolForNative.Return(p);
150 | }
151 | internal delegate IntPtr BufAllocCallback(int size);
152 | internal delegate void BufFreeCallback(IntPtr p);
153 | public delegate void OutputCallback(IntPtr buf, int len, IntPtr kcp, IntPtr user);
154 |
155 | BufAllocCallback m_BufAlloc;
156 | BufFreeCallback m_BufFree;
157 |
158 | IntPtr m_kcp;
159 | bool m_useNativeBufpool = false;
160 | ///
161 | ///
162 | ///
163 | ///
164 | ///
165 | ///
166 | /// 是否使用内存池,如果true,则
167 | /// 内存池单元尺寸,默认为4K
168 | /// 内存池总尺寸,默认8MB
169 | public KCPLib(uint conv_, object user, OutputCallback output_, bool useNativeBufPool = false) {
170 | if(useNativeBufPool) {
171 | if(BufferPoolForNative.m_inited == false) {
172 | throw new Exception("内存池BufferChunk尚未初始化,请先调用BufferChunk.Init");
173 | }
174 | m_BufAlloc = BufAlloc;
175 | m_BufFree = BufFree;
176 | kcp_native.ikcp_allocator(m_BufAlloc, m_BufFree);
177 | }
178 | m_useNativeBufpool = useNativeBufPool;
179 | //kcp_native.ikcp_allocator(m_BufAlloc, m_BufFree);
180 | m_kcp = kcp_native.ikcp_create((int)conv_, IntPtr.Zero);
181 | if(m_kcp == IntPtr.Zero) {
182 | throw new Exception("初始化KCP失败");
183 | }
184 | kcp_native.ikcp_setoutput(m_kcp, output_);
185 | }
186 |
187 | public unsafe int Input(byte[] buffer, int offset, int size) {
188 | fixed (byte* p = buffer) {
189 | return kcp_native.ikcp_input(m_kcp, new IntPtr(p + offset), size);
190 | }
191 | //IntPtr p = BufAlloc(size);
192 | //Marshal.Copy(buffer, offset, p, size);
193 | //int ret = kcp_native.ikcp_input(m_kcp, p, size);
194 | //BufFree(p);
195 | //return ret;
196 | }
197 |
198 | // check the size of next message in the recv queue
199 | public int PeekSize() {
200 | return kcp_native.ikcp_peeksize(m_kcp);
201 | }
202 |
203 | // user/upper level recv: returns size, returns below zero for EAGAIN
204 | public unsafe int Recv(byte[] buffer, int offset, int size) {
205 |
206 | fixed (byte* p = buffer) {
207 | int recvbytes = kcp_native.ikcp_recv(m_kcp, (IntPtr)(p + offset), size);
208 | return recvbytes;
209 | }
210 |
211 | //IntPtr p = BufAlloc(size);
212 | //int recvbytes = kcp_native.ikcp_recv(m_kcp, p, size);
213 | //Marshal.Copy(p, buffer, offset, size);
214 | //BufFree(p);
215 | //return recvbytes;
216 | }
217 |
218 |
219 |
220 | public unsafe int Send(byte[] buffer, int offset, int bufsize) {
221 |
222 | //IntPtr p = BufAlloc(bufsize);
223 | //Marshal.Copy(buffer, offset, p, bufsize);
224 |
225 | //int ret = kcp_native.ikcp_send(m_kcp, p, bufsize);
226 | //BufFree(p);
227 | //return ret;
228 |
229 | int send = 0;
230 | fixed (byte* p = buffer) {
231 | send = kcp_native.ikcp_send(m_kcp, new IntPtr(p + offset), bufsize);
232 | }
233 | //https://github.com/skywind3000/kcp/issues/10
234 | //https://github.com/skywind3000/kcp/wiki/Flow-Control-for-Users
235 | //https://github.com/skywind3000/kcp/issues/4
236 | kcp_native.ikcp_flush(m_kcp);
237 | return send;
238 | }
239 |
240 |
241 |
242 | // user/upper level send, returns below zero for error
243 | public int Send(byte[] buffer) {
244 | return Send(buffer, 0, buffer.Length);
245 | }
246 |
247 | public void Update(UInt32 current_) {
248 | kcp_native.ikcp_update(m_kcp, current_);
249 | }
250 | public UInt32 Check(UInt32 current_) {
251 | return kcp_native.ikcp_check(m_kcp, current_);
252 | }
253 |
254 | public int NoDelay(int nodelay, int interval, int resend, int nc) {
255 | return kcp_native.ikcp_nodelay(m_kcp, nodelay, interval, resend, nc);
256 | }
257 | public int GetInterval() {
258 | return kcp_native.ikcp_get_interval(m_kcp);
259 | }
260 |
261 | public int WaitSnd() {
262 | //https://github.com/skywind3000/kcp/wiki/Flow-Control-for-Users
263 | return kcp_native.ikcp_waitsnd(m_kcp);
264 | }
265 | public int WndSize(int sndwnd, int rcvwnd) {
266 | return kcp_native.ikcp_wndsize(m_kcp, sndwnd, rcvwnd);
267 | }
268 | public void SetMtu(int mtu) {
269 | kcp_native.ikcp_setmtu(m_kcp, mtu);//ikcp_setmtu
270 | }
271 | public void SetMinRto(int minrto) {
272 | kcp_native.ikcp_setminrto(m_kcp, minrto);
273 | }
274 | public int GetMinRto() {
275 | return kcp_native.ikcp_getminrto(m_kcp);
276 | }
277 | public void Dispose() {
278 | kcp_native.ikcp_release(m_kcp);
279 | }
280 | }
281 |
282 | public static class BufferPoolForNative {
283 | public static string Name;
284 | public static Byte[] Buffer;
285 | public static Stack AddrIndex;
286 | public static GCHandle BufferHandle;
287 | internal static IntPtr BufferAddr;
288 | private static object m_locker = new object();
289 | internal static bool m_inited = false;
290 | public static IntPtr GetBuf() {
291 | lock(m_locker) {
292 | if(AddrIndex.Count == 0) {
293 | throw new Exception("没有充足的空间.ChunkName:" + Name);
294 | }
295 | long addr = AddrIndex.Pop();
296 | Interlocked.Increment(ref InUse);
297 | return new IntPtr(addr);
298 | }
299 | }
300 | public static int InUse = 0;
301 | private static int m_unitBytes;
302 | public static void Init(int unitBytes, int totalSize) {
303 | Buffer = new byte[totalSize];
304 | BufferHandle = GCHandle.Alloc(Buffer, GCHandleType.Pinned);
305 | BufferAddr = BufferHandle.AddrOfPinnedObject();
306 | AddrIndex = new Stack();
307 | long start = BufferAddr.ToInt64();
308 | for(int j = 0; j < totalSize; j += unitBytes) {
309 | long add = start + j;
310 | AddrIndex.Push(add);
311 | }
312 | m_unitBytes = unitBytes;
313 | m_inited = true;
314 | }
315 | public static void Return(IntPtr buf) {
316 | long p = buf.ToInt64();
317 | long l = p % m_unitBytes;
318 |
319 | long addr = p - l;// (int)((buf.ToInt64() - BufferAddr.ToInt64()) / m_unitBytes);
320 | lock(m_locker) {
321 | AddrIndex.Push(addr);
322 | }
323 | Interlocked.Decrement(ref InUse);
324 | }
325 |
326 |
327 | public static void Close() {
328 | BufferHandle.Free();
329 | }
330 |
331 |
332 | }
333 |
334 | }
335 |
336 |
--------------------------------------------------------------------------------
/Unity3D/kcpUnity3DClientTest.cs:
--------------------------------------------------------------------------------
1 | //#define BigEndian
2 | #define LittleEndian
3 | using System;
4 | using System.Collections;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Net;
9 | using System.Net.Sockets;
10 | using UnityEngine;
11 | using Network_Kcp;
12 | #if BigEndian
13 | using KCPProxy = Network_Kcp.KCPProxy_BE;
14 | #else
15 | using KCPProxy = Network_Kcp.KCPProxy_LE;
16 | #endif
17 | namespace Assets.Script.Test
18 | {
19 | public class kcpUnity3DClientTest : MonoBehaviour
20 | {
21 | private KCPPlayer p1, p2;
22 |
23 | void Awake() {
24 |
25 |
26 | }
27 | private bool m_initKcp = false;
28 | private bool m_stopKcpSendMsg = true;
29 | private IEnumerator Start() {
30 | yield return new WaitForSecondsRealtime(3f);
31 | NetworkDebuger.EnableLog = true;
32 | NetworkDebuger.EnableSave = true;
33 | NetworkDebuger.Log("Awake()");
34 |
35 | p1 = new KCPPlayer();
36 | p1.Init("Player1", 12345, 12346);
37 |
38 | p2 = new KCPPlayer();
39 | p2.Init("Player2", 12346, 12345);
40 | m_initKcp = true;
41 | //
42 | StartCoroutine(sendMsgLoop());
43 | }
44 | private IEnumerator sendMsgLoop() {
45 | if (!m_initKcp) yield return null;
46 | while (true) {
47 | if(!m_stopKcpSendMsg) {
48 | //for (int i = 0; i < 10; i++) {
49 | p1.SendMessage();
50 | p2.SendMessage();
51 | //}
52 | yield return null;
53 | }
54 | yield return null;
55 | }
56 | }
57 |
58 | void Update() {
59 | if (m_initKcp) {
60 | p1.OnUpdate();
61 | p2.OnUpdate();
62 | }
63 | }
64 | private void FixedUpdate() {
65 | if (m_initKcp) {
66 | p1.OnFixedUpdate();
67 | p2.OnFixedUpdate();
68 | }
69 | }
70 | void OnGUI() {
71 | if (!m_initKcp) return;
72 | if (GUILayout.Button("Player1 SendMessage")) {
73 | p1.SendMessage();
74 | }
75 |
76 | if (GUILayout.Button("Player2 SendMessage")) {
77 | p2.SendMessage();
78 | }
79 | if (GUILayout.Button("stop/continue SendMessage")) {
80 | m_stopKcpSendMsg = !m_stopKcpSendMsg;
81 | //StartCoroutine(sendMsgLoop());
82 | }
83 |
84 | }
85 | private void OnApplicationQuit() {
86 | NetworkDebuger.EnableSave = false;
87 | p1.Dispose();
88 | p2.Dispose();
89 | }
90 |
91 | public class KCPPlayer
92 | {
93 | public string LOG_TAG = "KCPPlayer";
94 |
95 | private KCPSocket m_Socket;
96 | private string m_Name;
97 | private int m_MsgId = 0;
98 | private IPEndPoint m_RemotePoint;
99 | public void Dispose() {
100 | m_Socket.Dispose();
101 | m_Socket = null;
102 | }
103 | public void Init(string name, int localPort, int remotePort) {
104 | m_Name = name;
105 | LOG_TAG = "KCPPlayer[" + m_Name + "]";
106 |
107 | IPAddress ipa = IPAddress.Parse(UnityEngine.Network.player.ipAddress);
108 | m_RemotePoint = new IPEndPoint(ipa, remotePort);
109 |
110 | m_Socket = new KCPSocket(localPort, 1, AddressFamily.InterNetwork);
111 | m_Socket.AddReceiveListener(KCPProxy.IPEP_Any, OnReceiveAny);
112 | m_Socket.AddReceiveListener(m_RemotePoint, OnReceive);
113 |
114 | NetworkDebuger.Log("Init() name:{0}, localPort:{1}, remotePort:{2}", name, localPort, remotePort);
115 | }
116 |
117 | private void OnReceiveAny(byte[] buffer, int size, IPEndPoint remotePoint) {
118 | string str = Encoding.UTF8.GetString(buffer, 0, size);
119 | NetworkDebuger.Log("OnReceiveAny() " + remotePoint + ":" + str);
120 | }
121 |
122 | private void OnReceive(byte[] buffer, int size, IPEndPoint remotePoint) {
123 | string str = Encoding.UTF8.GetString(buffer, 0, size);
124 | NetworkDebuger.Log("OnReceive() " + remotePoint + ":" + str);
125 | }
126 |
127 | public void OnUpdate() {
128 | if (m_Socket != null) {
129 | m_Socket.Update();
130 | }
131 | }
132 |
133 | public void OnFixedUpdate() {
134 | if (m_Socket != null) {
135 | m_Socket.SendKeepHeartbeat(m_RemotePoint);
136 | }
137 | }
138 | string msgContent = @"
139 | LICENSE SYSTEM [2017918 10:58:53] Next license update check is after 2025-06-30T00:00:00
140 |
141 | Built from '5.5/release' branch; Version is '5.5.0f3 (38b4efef76f0) revision 3716335'; Using compiler version '160040219'
142 | OS: 'Windows 7 Service Pack 1 (6.1.7601) 64bit' Language: 'zh' Physical Memory: 16224 MB
143 | BatchMode: 0, IsHumanControllingUs: 1, StartBugReporterOnCrash: 1, Is64bit: 1, IsPro: 1
144 | Initialize mono
145 | Mono path[0] = 'C:/Program Files/Unity5.5.0/Editor/Data/Managed'
146 | Mono path[1] = 'C:/Program Files/Unity5.5.0/Editor/Data/Mono/lib/mono/2.0'
147 | Mono path[2] = 'C:/Program Files/Unity5.5.0/Editor/Data/UnityScript'
148 | Mono config path = 'C:/Program Files/Unity5.5.0/Editor/Data/Mono/etc'
149 | Using monoOptions --debugger-agent=transport=dt_socket,embedding=1,defer=y,address=0.0.0.0:56392
150 | IsTimeToCheckForNewEditor: Update time 1505705705 current 1505703540
151 | C:/work/irobotqv2.0_dev/irobotqv2.0_app
152 | Loading GUID <-> Path mappings...0.000281 seconds
153 | Loading Asset Database...0.015599 seconds
154 | Audio: FMOD Profiler initialized on port 54900
155 | AssetDatabase consistency checks...0.019115 seconds
156 | Initialize engine version: 5.5.0f3 (38b4efef76f0)
157 | GfxDevice: creating device client; threaded=1
158 | Direct3D:
159 | Version: Direct3D 11.0 [level 11.0]
160 | Renderer: AMD Radeon HD 6670 (ID=0x6758)
161 | Vendor: ATI
162 | VRAM: 4418 MB
163 | Driver: 14.100.0.0
164 | Begin MonoManager ReloadAssembly1
165 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\UnityEngine.dll (this message is harmless)
166 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\UnityEditor.dll (this message is harmless)
167 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\Unity.Locator.dll (this message is harmless)
168 | Refreshing native plugins compatible for Editor in 9.17 ms, found 3 plugins.
169 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\I18N.dll (this message is harmless)
170 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\I18N.CJK.dll (this message is harmless)
171 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\Unity.DataContract.dll (this message is harmless)
172 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.Core.dll (this message is harmless)
173 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\Unity.IvyParser.dll (this message is harmless)
174 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.dll (this message is harmless)
175 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.Xml.dll (this message is harmless)
176 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.Configuration.dll (this message is harmless)
177 | Begin MonoManager ReloadAssembly2
178 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\UnityEngine.dll (this message is harmless)
179 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\UnityEditor.dll (this message is harmless)
180 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\Unity.Locator.dll (this message is harmless)
181 | Refreshing native plugins compatible for Editor in 9.17 ms, found 3 plugins.
182 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\I18N.dll (this message is harmless)
183 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\I18N.CJK.dll (this message is harmless)
184 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\Unity.DataContract.dll (this message is harmless)
185 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.Core.dll (this message is harmless)
186 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\Unity.IvyParser.dll (this message is harmless)
187 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.dll (this message is harmless)
188 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.Xml.dll (this message is harmless)
189 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.Configuration.dll (this message is harmless)
190 | Begin MonoManager ReloadAssembly3
191 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\UnityEngine.dll (this message is harmless)
192 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\UnityEditor.dll (this message is harmless)
193 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\Unity.Locator.dll (this message is harmless)
194 | Refreshing native plugins compatible for Editor in 9.17 ms, found 3 plugins.
195 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\I18N.dll (this message is harmless)
196 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\I18N.CJK.dll (this message is harmless)
197 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\Unity.DataContract.dll (this message is harmless)
198 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.Core.dll (this message is harmless)
199 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\Unity.IvyParser.dll (this message is harmless)
200 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.dll (this message is harmless)
201 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.Xml.dll (this message is harmless)
202 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.Configuration.dll (this message is harmless)
203 | Begin MonoManager ReloadAssembly4
204 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\UnityEngine.dll (this message is harmless)
205 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\UnityEditor.dll (this message is harmless)
206 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\Unity.Locator.dll (this message is harmless)
207 | Refreshing native plugins compatible for Editor in 9.17 ms, found 3 plugins.
208 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\I18N.dll (this message is harmless)
209 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\I18N.CJK.dll (this message is harmless)
210 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\Unity.DataContract.dll (this message is harmless)
211 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.Core.dll (this message is harmless)
212 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\Unity.IvyParser.dll (this message is harmless)
213 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.dll (this message is harmless)
214 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.Xml.dll (this message is harmless)
215 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.Configuration.dll (this message is harmless)
216 | Begin MonoManager ReloadAssembly5
217 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\UnityEngine.dll (this message is harmless)
218 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\UnityEditor.dll (this message is harmless)
219 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\Unity.Locator.dll (this message is harmless)
220 | Refreshing native plugins compatible for Editor in 9.17 ms, found 3 plugins.
221 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\I18N.dll (this message is harmless)
222 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\I18N.CJK.dll (this message is harmless)
223 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\Unity.DataContract.dll (this message is harmless)
224 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.Core.dll (this message is harmless)
225 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Managed\Unity.IvyParser.dll (this message is harmless)
226 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.dll (this message is harmless)
227 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.Xml.dll (this message is harmless)
228 | Platform assembly: C:\Program Files\Unity5.5.0\Editor\Data\Mono\lib\mono\2.0\System.Configuration.dll (this message is harmless)";
229 |
230 |
231 | public void SendMessage() {
232 | if (m_Socket != null) {
233 | m_MsgId++;
234 | m_Socket.SendTo(m_Name + "_" + "Message" + m_MsgId+" [size]"+msgContent, m_RemotePoint);
235 | }
236 | }
237 | }
238 | }
239 |
240 |
241 | }
242 |
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/ikcp.h:
--------------------------------------------------------------------------------
1 | //=====================================================================
2 | //
3 | // KCP - A Better ARQ Protocol Implementation
4 | // skywind3000 (at) gmail.com, 2010-2011
5 | //
6 | // Features:
7 | // + Average RTT reduce 30% - 40% vs traditional ARQ like tcp.
8 | // + Maximum RTT reduce three times vs tcp.
9 | // + Lightweight, distributed as a single source file.
10 | //
11 | //=====================================================================
12 | #ifndef __IKCP_H__
13 | #define __IKCP_H__
14 |
15 | #include
16 | #include
17 | #include
18 |
19 |
20 | //=====================================================================
21 | // 32BIT INTEGER DEFINITION
22 | //=====================================================================
23 | #ifndef __INTEGER_32_BITS__
24 | #define __INTEGER_32_BITS__
25 | #if defined(_WIN64) || defined(WIN64) || defined(__amd64__) || \
26 | defined(__x86_64) || defined(__x86_64__) || defined(_M_IA64) || \
27 | defined(_M_AMD64)
28 | typedef unsigned int ISTDUINT32;
29 | typedef int ISTDINT32;
30 | #elif defined(_WIN32) || defined(WIN32) || defined(__i386__) || \
31 | defined(__i386) || defined(_M_X86)
32 | typedef unsigned long ISTDUINT32;
33 | typedef long ISTDINT32;
34 | #elif defined(__MACOS__)
35 | typedef UInt32 ISTDUINT32;
36 | typedef SInt32 ISTDINT32;
37 | #elif defined(__APPLE__) && defined(__MACH__)
38 | #include
39 | typedef u_int32_t ISTDUINT32;
40 | typedef int32_t ISTDINT32;
41 | #elif defined(__BEOS__)
42 | #include
43 | typedef u_int32_t ISTDUINT32;
44 | typedef int32_t ISTDINT32;
45 | #elif (defined(_MSC_VER) || defined(__BORLANDC__)) && (!defined(__MSDOS__))
46 | typedef unsigned __int32 ISTDUINT32;
47 | typedef __int32 ISTDINT32;
48 | #elif defined(__GNUC__)
49 | #include
50 | typedef uint32_t ISTDUINT32;
51 | typedef int32_t ISTDINT32;
52 | #else
53 | typedef unsigned long ISTDUINT32;
54 | typedef long ISTDINT32;
55 | #endif
56 | #endif
57 |
58 |
59 | //=====================================================================
60 | // Integer Definition
61 | //=====================================================================
62 | #ifndef __IINT8_DEFINED
63 | #define __IINT8_DEFINED
64 | typedef char IINT8;
65 | #endif
66 |
67 | #ifndef __IUINT8_DEFINED
68 | #define __IUINT8_DEFINED
69 | typedef unsigned char IUINT8;
70 | #endif
71 |
72 | #ifndef __IUINT16_DEFINED
73 | #define __IUINT16_DEFINED
74 | typedef unsigned short IUINT16;
75 | #endif
76 |
77 | #ifndef __IINT16_DEFINED
78 | #define __IINT16_DEFINED
79 | typedef short IINT16;
80 | #endif
81 |
82 | #ifndef __IINT32_DEFINED
83 | #define __IINT32_DEFINED
84 | typedef ISTDINT32 IINT32;
85 | #endif
86 |
87 | #ifndef __IUINT32_DEFINED
88 | #define __IUINT32_DEFINED
89 | typedef ISTDUINT32 IUINT32;
90 | #endif
91 |
92 | #ifndef __IINT64_DEFINED
93 | #define __IINT64_DEFINED
94 | #if defined(_MSC_VER) || defined(__BORLANDC__)
95 | typedef __int64 IINT64;
96 | #else
97 | typedef long long IINT64;
98 | #endif
99 | #endif
100 |
101 | #ifndef __IUINT64_DEFINED
102 | #define __IUINT64_DEFINED
103 | #if defined(_MSC_VER) || defined(__BORLANDC__)
104 | typedef unsigned __int64 IUINT64;
105 | #else
106 | typedef unsigned long long IUINT64;
107 | #endif
108 | #endif
109 |
110 | #ifndef INLINE
111 | #if defined(__GNUC__)
112 |
113 | #if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))
114 | #define INLINE __inline__ __attribute__((always_inline))
115 | #else
116 | #define INLINE __inline__
117 | #endif
118 |
119 | #elif (defined(_MSC_VER) || defined(__BORLANDC__) || defined(__WATCOMC__))
120 | #define INLINE __inline
121 | #else
122 | #define INLINE
123 | #endif
124 | #endif
125 |
126 | #if (!defined(__cplusplus)) && (!defined(inline))
127 | #define inline INLINE
128 | #endif
129 |
130 |
131 | //=====================================================================
132 | // QUEUE DEFINITION
133 | //=====================================================================
134 | #ifndef __IQUEUE_DEF__
135 | #define __IQUEUE_DEF__
136 |
137 | struct IQUEUEHEAD {
138 | struct IQUEUEHEAD *next, *prev;
139 | };
140 |
141 | typedef struct IQUEUEHEAD iqueue_head;
142 |
143 |
144 | //---------------------------------------------------------------------
145 | // queue init
146 | //---------------------------------------------------------------------
147 | #define IQUEUE_HEAD_INIT(name) { &(name), &(name) }
148 | #define IQUEUE_HEAD(name) \
149 | struct IQUEUEHEAD name = IQUEUE_HEAD_INIT(name)
150 |
151 | #define IQUEUE_INIT(ptr) ( \
152 | (ptr)->next = (ptr), (ptr)->prev = (ptr))
153 |
154 | #define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
155 |
156 | #define ICONTAINEROF(ptr, type, member) ( \
157 | (type*)( ((char*)((type*)ptr)) - IOFFSETOF(type, member)) )
158 |
159 | #define IQUEUE_ENTRY(ptr, type, member) ICONTAINEROF(ptr, type, member)
160 |
161 |
162 | //---------------------------------------------------------------------
163 | // queue operation
164 | //---------------------------------------------------------------------
165 | #define IQUEUE_ADD(node, head) ( \
166 | (node)->prev = (head), (node)->next = (head)->next, \
167 | (head)->next->prev = (node), (head)->next = (node))
168 |
169 | #define IQUEUE_ADD_TAIL(node, head) ( \
170 | (node)->prev = (head)->prev, (node)->next = (head), \
171 | (head)->prev->next = (node), (head)->prev = (node))
172 |
173 | #define IQUEUE_DEL_BETWEEN(p, n) ((n)->prev = (p), (p)->next = (n))
174 |
175 | #define IQUEUE_DEL(entry) (\
176 | (entry)->next->prev = (entry)->prev, \
177 | (entry)->prev->next = (entry)->next, \
178 | (entry)->next = 0, (entry)->prev = 0)
179 |
180 | #define IQUEUE_DEL_INIT(entry) do { \
181 | IQUEUE_DEL(entry); IQUEUE_INIT(entry); } while (0)
182 |
183 | #define IQUEUE_IS_EMPTY(entry) ((entry) == (entry)->next)
184 |
185 | #define iqueue_init IQUEUE_INIT
186 | #define iqueue_entry IQUEUE_ENTRY
187 | #define iqueue_add IQUEUE_ADD
188 | #define iqueue_add_tail IQUEUE_ADD_TAIL
189 | #define iqueue_del IQUEUE_DEL
190 | #define iqueue_del_init IQUEUE_DEL_INIT
191 | #define iqueue_is_empty IQUEUE_IS_EMPTY
192 |
193 | #define IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) \
194 | for ((iterator) = iqueue_entry((head)->next, TYPE, MEMBER); \
195 | &((iterator)->MEMBER) != (head); \
196 | (iterator) = iqueue_entry((iterator)->MEMBER.next, TYPE, MEMBER))
197 |
198 | #define iqueue_foreach(iterator, head, TYPE, MEMBER) \
199 | IQUEUE_FOREACH(iterator, head, TYPE, MEMBER)
200 |
201 | #define iqueue_foreach_entry(pos, head) \
202 | for( (pos) = (head)->next; (pos) != (head) ; (pos) = (pos)->next )
203 |
204 |
205 | #define __iqueue_splice(list, head) do { \
206 | iqueue_head *first = (list)->next, *last = (list)->prev; \
207 | iqueue_head *at = (head)->next; \
208 | (first)->prev = (head), (head)->next = (first); \
209 | (last)->next = (at), (at)->prev = (last); } while (0)
210 |
211 | #define iqueue_splice(list, head) do { \
212 | if (!iqueue_is_empty(list)) __iqueue_splice(list, head); } while (0)
213 |
214 | #define iqueue_splice_init(list, head) do { \
215 | iqueue_splice(list, head); iqueue_init(list); } while (0)
216 |
217 |
218 | #ifdef _MSC_VER
219 | #pragma warning(disable:4311)
220 | #pragma warning(disable:4312)
221 | #pragma warning(disable:4996)
222 | #endif
223 |
224 | #endif
225 |
226 |
227 | //---------------------------------------------------------------------
228 | // WORD ORDER
229 | //---------------------------------------------------------------------
230 | #ifndef IWORDS_BIG_ENDIAN
231 | #ifdef _BIG_ENDIAN_
232 | #if _BIG_ENDIAN_
233 | #define IWORDS_BIG_ENDIAN 1
234 | #endif
235 | #endif
236 | #ifndef IWORDS_BIG_ENDIAN
237 | #if defined(__hppa__) || \
238 | defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \
239 | (defined(__MIPS__) && defined(__MIPSEB__)) || \
240 | defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \
241 | defined(__sparc__) || defined(__powerpc__) || \
242 | defined(__mc68000__) || defined(__s390x__) || defined(__s390__)
243 | #define IWORDS_BIG_ENDIAN 1
244 | #endif
245 | #endif
246 | #ifndef IWORDS_BIG_ENDIAN
247 | #define IWORDS_BIG_ENDIAN 0
248 | #endif
249 | #endif
250 |
251 |
252 |
253 | //=====================================================================
254 | // SEGMENT
255 | //=====================================================================
256 | struct IKCPSEG
257 | {
258 | struct IQUEUEHEAD node;
259 | IUINT32 conv;
260 | IUINT32 cmd;
261 | IUINT32 frg;
262 | IUINT32 wnd;
263 | IUINT32 ts;
264 | IUINT32 sn;
265 | IUINT32 una;
266 | IUINT32 len;
267 | IUINT32 resendts;
268 | IUINT32 rto;
269 | IUINT32 fastack;
270 | IUINT32 xmit;
271 | char data[1];
272 | };
273 |
274 |
275 | //---------------------------------------------------------------------
276 | // IKCPCB
277 | //---------------------------------------------------------------------
278 | struct IKCPCB
279 | {
280 | IUINT32 conv, mtu, mss, state;
281 | IUINT32 snd_una, snd_nxt, rcv_nxt;
282 | IUINT32 ts_recent, ts_lastack, ssthresh;
283 | IINT32 rx_rttval, rx_srtt, rx_rto, rx_minrto;
284 | IUINT32 snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe;
285 | IUINT32 current, interval, ts_flush, xmit;
286 | IUINT32 nrcv_buf, nsnd_buf;
287 | IUINT32 nrcv_que, nsnd_que;
288 | IUINT32 nodelay, updated;
289 | IUINT32 ts_probe, probe_wait;
290 | IUINT32 dead_link, incr;
291 | struct IQUEUEHEAD snd_queue;
292 | struct IQUEUEHEAD rcv_queue;
293 | struct IQUEUEHEAD snd_buf;
294 | struct IQUEUEHEAD rcv_buf;
295 | IUINT32 *acklist;
296 | IUINT32 ackcount;
297 | IUINT32 ackblock;
298 | void *user;
299 | char *buffer;
300 | int fastresend;
301 | int nocwnd, stream;
302 | int logmask;
303 | int (*output)(const char *buf, int len, struct IKCPCB *kcp, void *user);
304 | void (*writelog)(const char *log, struct IKCPCB *kcp, void *user);
305 | };
306 |
307 |
308 | typedef struct IKCPCB ikcpcb;
309 |
310 | #define IKCP_LOG_OUTPUT 1
311 | #define IKCP_LOG_INPUT 2
312 | #define IKCP_LOG_SEND 4
313 | #define IKCP_LOG_RECV 8
314 | #define IKCP_LOG_IN_DATA 16
315 | #define IKCP_LOG_IN_ACK 32
316 | #define IKCP_LOG_IN_PROBE 64
317 | #define IKCP_LOG_IN_WINS 128
318 | #define IKCP_LOG_OUT_DATA 256
319 | #define IKCP_LOG_OUT_ACK 512
320 | #define IKCP_LOG_OUT_PROBE 1024
321 | #define IKCP_LOG_OUT_WINS 2048
322 |
323 | #ifdef __cplusplus
324 | extern "C" {
325 | #endif
326 |
327 | //---------------------------------------------------------------------
328 | // interface
329 | //---------------------------------------------------------------------
330 |
331 | // create a new kcp control object, 'conv' must equal in two endpoint
332 | // from the same connection. 'user' will be passed to the output callback
333 | // output callback can be setup like this: 'kcp->output = my_udp_output'
334 | #if defined(_MSC_VER)
335 | // create a new kcp control object, 'conv' must equal in two endpoint
336 | // from the same connection. 'user' will be passed to the output callback
337 | // output callback can be setup like this: 'kcp->output = my_udp_output'
338 | __declspec(dllexport) ikcpcb* ikcp_create(IUINT32 conv, void *user);
339 |
340 | // release kcp control object
341 | __declspec(dllexport) void ikcp_release(ikcpcb *kcp);
342 |
343 | // set output callback, which will be invoked by kcp
344 | __declspec(dllexport) void ikcp_setoutput(ikcpcb *kcp, int (*output)(const char *buf, int len,
345 | ikcpcb *kcp, void *user));
346 |
347 | // user/upper level recv: returns size, returns below zero for EAGAIN
348 | __declspec(dllexport) int ikcp_recv(ikcpcb *kcp, char *buffer, int len);
349 |
350 | // user/upper level send, returns below zero for error
351 | __declspec(dllexport) int ikcp_send(ikcpcb *kcp, const char *buffer, int len);
352 |
353 | // update state (call it repeatedly, every 10ms-100ms), or you can ask
354 | // ikcp_check when to call it again (without ikcp_input/_send calling).
355 | // 'current' - current timestamp in millisec.
356 | __declspec(dllexport) void ikcp_update(ikcpcb *kcp, IUINT32 current);
357 |
358 | // Determine when should you invoke ikcp_update:
359 | // returns when you should invoke ikcp_update in millisec, if there
360 | // is no ikcp_input/_send calling. you can call ikcp_update in that
361 | // time, instead of call update repeatly.
362 | // Important to reduce unnacessary ikcp_update invoking. use it to
363 | // schedule ikcp_update (eg. implementing an epoll-like mechanism,
364 | // or optimize ikcp_update when handling massive kcp connections)
365 | __declspec(dllexport) IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current);
366 |
367 | // when you received a low level packet (eg. UDP packet), call it
368 | __declspec(dllexport) int ikcp_input(ikcpcb *kcp, const char *data, long size);
369 |
370 | // flush pending data
371 | __declspec(dllexport) void ikcp_flush(ikcpcb *kcp);
372 |
373 | // check the size of next message in the recv queue
374 | __declspec(dllexport) int ikcp_peeksize(const ikcpcb *kcp);
375 |
376 | // change MTU size, default is 1400
377 | __declspec(dllexport) int ikcp_setmtu(ikcpcb *kcp, int mtu);
378 |
379 | // set maximum window size: sndwnd=32, rcvwnd=32 by default
380 | __declspec(dllexport) int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd);
381 |
382 | // get how many packet is waiting to be sent
383 | __declspec(dllexport) int ikcp_waitsnd(const ikcpcb *kcp);
384 |
385 | // fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
386 | // nodelay: 0:disable(default), 1:enable
387 | // interval: internal update timer interval in millisec, default is 100ms
388 | // resend: 0:disable fast resend(default), 1:enable fast resend
389 | // nc: 0:normal congestion control(default), 1:disable congestion control
390 | __declspec(dllexport) int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc);
391 |
392 | __declspec(dllexport) int ikcp_getminrto(ikcpcb *kcp);
393 | __declspec(dllexport) void ikcp_setminrto(ikcpcb *kcp, int minrto);
394 |
395 | __declspec(dllexport) int ikcp_get_interval(ikcpcb *kcp);
396 |
397 | __declspec(dllexport) int ikcp_rcvbuf_count(const ikcpcb *kcp);
398 | __declspec(dllexport) int ikcp_sndbuf_count(const ikcpcb *kcp);
399 |
400 | __declspec(dllexport) void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...);
401 |
402 | // setup allocator
403 | __declspec(dllexport) void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*));
404 |
405 | // read conv
406 | __declspec(dllexport) IUINT32 ikcp_getconv(const void *ptr);
407 |
408 | #else
409 | ikcpcb* ikcp_create(IUINT32 conv, void *user);
410 |
411 | // release kcp control object
412 | void ikcp_release(ikcpcb *kcp);
413 |
414 | // set output callback, which will be invoked by kcp
415 | void ikcp_setoutput(ikcpcb *kcp, int (*output)(const char *buf, int len,
416 | ikcpcb *kcp, void *user));
417 |
418 | // user/upper level recv: returns size, returns below zero for EAGAIN
419 | int ikcp_recv(ikcpcb *kcp, char *buffer, int len);
420 |
421 | // user/upper level send, returns below zero for error
422 | int ikcp_send(ikcpcb *kcp, const char *buffer, int len);
423 |
424 | // update state (call it repeatedly, every 10ms-100ms), or you can ask
425 | // ikcp_check when to call it again (without ikcp_input/_send calling).
426 | // 'current' - current timestamp in millisec.
427 | void ikcp_update(ikcpcb *kcp, IUINT32 current);
428 |
429 | // Determine when should you invoke ikcp_update:
430 | // returns when you should invoke ikcp_update in millisec, if there
431 | // is no ikcp_input/_send calling. you can call ikcp_update in that
432 | // time, instead of call update repeatly.
433 | // Important to reduce unnacessary ikcp_update invoking. use it to
434 | // schedule ikcp_update (eg. implementing an epoll-like mechanism,
435 | // or optimize ikcp_update when handling massive kcp connections)
436 | IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current);
437 |
438 | // when you received a low level packet (eg. UDP packet), call it
439 | int ikcp_input(ikcpcb *kcp, const char *data, long size);
440 |
441 | // flush pending data
442 | void ikcp_flush(ikcpcb *kcp);
443 |
444 | // check the size of next message in the recv queue
445 | int ikcp_peeksize(const ikcpcb *kcp);
446 |
447 | // change MTU size, default is 1400
448 | int ikcp_setmtu(ikcpcb *kcp, int mtu);
449 |
450 | // set maximum window size: sndwnd=32, rcvwnd=32 by default
451 | int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd);
452 |
453 | // get how many packet is waiting to be sent
454 | int ikcp_waitsnd(const ikcpcb *kcp);
455 |
456 | // fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
457 | // nodelay: 0:disable(default), 1:enable
458 | // interval: internal update timer interval in millisec, default is 100ms
459 | // resend: 0:disable fast resend(default), 1:enable fast resend
460 | // nc: 0:normal congestion control(default), 1:disable congestion control
461 | int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc);
462 |
463 |
464 | int ikcp_get_interval(ikcpcb *kcp);
465 |
466 | void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...);
467 |
468 | // setup allocator
469 | void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*));
470 |
471 | // read conv
472 | IUINT32 ikcp_getconv(const void *ptr);
473 |
474 | #endif
475 | #ifdef __cplusplus
476 | }
477 | #endif
478 |
479 | #endif
480 |
481 |
482 |
--------------------------------------------------------------------------------
/KCP_PInvoke/kcp_native_dll/kcpdll.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug_x64
6 | Win32
7 |
8 |
9 | Debug_x64
10 | x64
11 |
12 |
13 | Debug
14 | Win32
15 |
16 |
17 | Debug
18 | x64
19 |
20 |
21 | Release_x64
22 | Win32
23 |
24 |
25 | Release_x64
26 | x64
27 |
28 |
29 | Release
30 | Win32
31 |
32 |
33 | Release
34 | x64
35 |
36 |
37 |
38 | ikcp
39 | {2A9A14A1-0AFD-42E7-AEA4-D7A2F58C0391}
40 | kcpdll
41 | Win32Proj
42 |
43 |
44 |
45 | DynamicLibrary
46 | v140
47 | Unicode
48 | true
49 |
50 |
51 | DynamicLibrary
52 | v140
53 | Unicode
54 |
55 |
56 | DynamicLibrary
57 | v140
58 | Unicode
59 | true
60 |
61 |
62 | DynamicLibrary
63 | v140
64 | Unicode
65 |
66 |
67 | DynamicLibrary
68 | v140
69 | Unicode
70 | true
71 |
72 |
73 | DynamicLibrary
74 | v140
75 | Unicode
76 |
77 |
78 | DynamicLibrary
79 | v140
80 | Unicode
81 | true
82 |
83 |
84 | DynamicLibrary
85 | v140
86 | Unicode
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | <_ProjectFileVersion>14.0.25420.1
118 |
119 |
120 | $(SolutionDir)$(Configuration)\
121 | $(Configuration)\
122 | true
123 |
124 |
125 | $(SolutionDir)$(Platform)\$(Configuration)\
126 | $(Platform)\$(Configuration)\
127 | true
128 |
129 |
130 | $(SolutionDir)$(Configuration)\
131 | $(Configuration)\
132 | false
133 |
134 |
135 | $(SolutionDir)$(Platform)\$(Configuration)\
136 | $(Platform)\$(Configuration)\
137 | false
138 |
139 |
140 | $(SolutionDir)$(Configuration)\
141 | $(Configuration)\
142 | true
143 |
144 |
145 | $(SolutionDir)$(Platform)\$(Configuration)\
146 | $(Platform)\$(Configuration)\
147 | true
148 |
149 |
150 | $(SolutionDir)$(Configuration)\
151 | $(Configuration)\
152 | false
153 |
154 |
155 | $(SolutionDir)$(Platform)\$(Configuration)\
156 | $(Platform)\$(Configuration)\
157 | false
158 |
159 |
160 |
161 | Disabled
162 | WIN32;_DEBUG;_WINDOWS;_USRDLL;KCPDLL_EXPORTS;%(PreprocessorDefinitions)
163 | true
164 | EnableFastChecks
165 | MultiThreadedDebugDLL
166 | Use
167 | Level3
168 | EditAndContinue
169 | CompileAsC
170 |
171 |
172 | true
173 | Windows
174 | MachineX86
175 |
176 |
177 |
178 |
179 | X64
180 |
181 |
182 | Disabled
183 | WIN32;_DEBUG;_WINDOWS;_USRDLL;KCPDLL_EXPORTS;%(PreprocessorDefinitions)
184 | true
185 | EnableFastChecks
186 | MultiThreadedDebugDLL
187 | Use
188 | Level3
189 | ProgramDatabase
190 | CompileAsC
191 |
192 |
193 | true
194 | Windows
195 | MachineX64
196 |
197 |
198 |
199 |
200 | MaxSpeed
201 | true
202 | WIN32;NDEBUG;_WINDOWS;_USRDLL;KCPDLL_EXPORTS;%(PreprocessorDefinitions)
203 | MultiThreadedDLL
204 | true
205 |
206 | Level3
207 | ProgramDatabase
208 | CompileAsC
209 |
210 |
211 | true
212 | Windows
213 | true
214 | true
215 | MachineX86
216 |
217 |
218 |
219 |
220 | X64
221 |
222 |
223 | MaxSpeed
224 | true
225 | WIN32;NDEBUG;_WINDOWS;_USRDLL;KCPDLL_EXPORTS;%(PreprocessorDefinitions)
226 | MultiThreadedDLL
227 | true
228 | NotUsing
229 | Level3
230 | ProgramDatabase
231 |
232 |
233 | true
234 | Windows
235 | true
236 | true
237 | MachineX64
238 |
239 |
240 |
241 |
242 | Disabled
243 | WIN32;_DEBUG;_WINDOWS;_USRDLL;KCPDLL_EXPORTS;%(PreprocessorDefinitions)
244 | true
245 | EnableFastChecks
246 | MultiThreadedDebugDLL
247 |
248 | Level3
249 | EditAndContinue
250 | CompileAsC
251 |
252 |
253 | true
254 | Windows
255 | MachineX86
256 |
257 |
258 |
259 |
260 | X64
261 |
262 |
263 | Disabled
264 | WIN32;_DEBUG;_WINDOWS;_USRDLL;KCPDLL_EXPORTS;%(PreprocessorDefinitions)
265 | true
266 | EnableFastChecks
267 | MultiThreadedDebugDLL
268 | Use
269 | Level3
270 | ProgramDatabase
271 | CompileAsC
272 |
273 |
274 | true
275 | Windows
276 | MachineX64
277 |
278 |
279 |
280 |
281 | MaxSpeed
282 | true
283 | WIN32;NDEBUG;_WINDOWS;_USRDLL;KCPDLL_EXPORTS;%(PreprocessorDefinitions)
284 | MultiThreadedDLL
285 | true
286 |
287 | Level3
288 | ProgramDatabase
289 |
290 |
291 | true
292 | Windows
293 | true
294 | true
295 | MachineX86
296 |
297 |
298 |
299 |
300 | X64
301 |
302 |
303 | MaxSpeed
304 | true
305 | WIN32;NDEBUG;_WINDOWS;_USRDLL;KCPDLL_EXPORTS;%(PreprocessorDefinitions)
306 | MultiThreadedDLL
307 | true
308 | NotUsing
309 | Level3
310 | ProgramDatabase
311 | CompileAsC
312 |
313 |
314 | true
315 | Windows
316 | true
317 | true
318 | MachineX64
319 |
320 |
321 |
322 |
323 |
324 |
325 | false
326 |
327 |
328 | false
329 |
330 |
331 | false
332 |
333 |
334 | false
335 |
336 |
337 | false
338 |
339 |
340 | false
341 |
342 |
343 | false
344 |
345 |
346 | false
347 |
348 |
349 |
350 | Create
351 | Create
352 | Create
353 | Create
354 | Create
355 | Create
356 | Create
357 | Create
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
--------------------------------------------------------------------------------
/common/kcp.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 |
7 |
8 | public class KCP
9 | {
10 | public const int IKCP_RTO_NDL = 30; // no delay min rto
11 | public const int IKCP_RTO_MIN = 100; // normal min rto
12 | public const int IKCP_RTO_DEF = 200;
13 | public const int IKCP_RTO_MAX = 60000;
14 | public const int IKCP_CMD_PUSH = 81; // cmd: push data
15 | public const int IKCP_CMD_ACK = 82; // cmd: ack
16 | public const int IKCP_CMD_WASK = 83; // cmd: window probe (ask)
17 | public const int IKCP_CMD_WINS = 84; // cmd: window size (tell)
18 | public const int IKCP_ASK_SEND = 1; // need to send IKCP_CMD_WASK
19 | public const int IKCP_ASK_TELL = 2; // need to send IKCP_CMD_WINS
20 | public const int IKCP_WND_SND = 32;
21 | public const int IKCP_WND_RCV = 32;
22 | public const int IKCP_MTU_DEF = 1400;
23 | public const int IKCP_ACK_FAST = 3;
24 | public const int IKCP_INTERVAL = 100;
25 | public const int IKCP_OVERHEAD = 24;
26 | public const int IKCP_DEADLINK = 10;
27 | public const int IKCP_THRESH_INIT = 2;
28 | public const int IKCP_THRESH_MIN = 2;
29 | public const int IKCP_PROBE_INIT = 7000; // 7 secs to probe window size
30 | public const int IKCP_PROBE_LIMIT = 120000; // up to 120 secs to probe window
31 |
32 |
33 | // encode 8 bits unsigned int
34 | public static int ikcp_encode8u(byte[] p, int offset, byte c)
35 | {
36 | p[0 + offset] = c;
37 | return 1;
38 | }
39 |
40 | // decode 8 bits unsigned int
41 | public static int ikcp_decode8u(byte[] p, int offset, ref byte c)
42 | {
43 | c = p[0 + offset];
44 | return 1;
45 | }
46 |
47 | /* encode 16 bits unsigned int (lsb) */
48 | public static int ikcp_encode16u(byte[] p, int offset, UInt16 w)
49 | {
50 | p[0 + offset] = (byte)(w >> 0);
51 | p[1 + offset] = (byte)(w >> 8);
52 | return 2;
53 | }
54 |
55 | /* decode 16 bits unsigned int (lsb) */
56 | public static int ikcp_decode16u(byte[] p, int offset, ref UInt16 c)
57 | {
58 | UInt16 result = 0;
59 | result |= (UInt16)p[0 + offset];
60 | result |= (UInt16)(p[1 + offset] << 8);
61 | c = result;
62 | return 2;
63 | }
64 |
65 | /* encode 32 bits unsigned int (lsb) */
66 | public static int ikcp_encode32u(byte[] p, int offset, UInt32 l)
67 | {
68 | p[0 + offset] = (byte)(l >> 0);
69 | p[1 + offset] = (byte)(l >> 8);
70 | p[2 + offset] = (byte)(l >> 16);
71 | p[3 + offset] = (byte)(l >> 24);
72 | return 4;
73 | }
74 |
75 | /* decode 32 bits unsigned int (lsb) */
76 | public static int ikcp_decode32u(byte[] p, int offset, ref UInt32 c)
77 | {
78 | UInt32 result = 0;
79 | result |= (UInt32)p[0 + offset];
80 | result |= (UInt32)(p[1 + offset] << 8);
81 | result |= (UInt32)(p[2 + offset] << 16);
82 | result |= (UInt32)(p[3 + offset] << 24);
83 | c = result;
84 | return 4;
85 | }
86 |
87 | public static byte[] slice(byte[] p, int start, int stop) {
88 | var bytes = new byte[stop - start];
89 | Array.Copy(p, start, bytes, 0, bytes.Length);
90 | return bytes;
91 | }
92 |
93 | public static T[] slice(T[] p, int start, int stop) {
94 | var arr = new T[stop - start];
95 | var index = 0;
96 | for (var i = start; i < stop; i++)
97 | {
98 | arr[index] = p[i];
99 | index++;
100 | }
101 |
102 | return arr;
103 | }
104 |
105 | public static byte[] append(byte[] p, byte c) {
106 | var bytes = new byte[p.Length + 1];
107 | Array.Copy(p, bytes, p.Length);
108 | bytes[p.Length] = c;
109 | return bytes;
110 | }
111 |
112 | public static T[] append(T[] p, T c) {
113 | var arr = new T[p.Length + 1];
114 | for (var i = 0; i < p.Length; i++)
115 | arr[i] = p[i];
116 | arr[p.Length] = c;
117 | return arr;
118 | }
119 |
120 | public static T[] append(T[] p, T[] cs)
121 | {
122 | var arr = new T[p.Length + cs.Length];
123 | for (var i = 0; i < p.Length; i++)
124 | arr[i] = p[i];
125 | for (var i = 0; i < cs.Length; i++ )
126 | arr[p.Length+i] = cs[i];
127 | return arr;
128 | }
129 |
130 | static UInt32 _imin_(UInt32 a, UInt32 b)
131 | {
132 | return a <= b ? a : b;
133 | }
134 |
135 | static UInt32 _imax_(UInt32 a, UInt32 b)
136 | {
137 | return a >= b ? a : b;
138 | }
139 |
140 | static UInt32 _ibound_(UInt32 lower, UInt32 middle, UInt32 upper)
141 | {
142 | return _imin_(_imax_(lower, middle), upper);
143 | }
144 |
145 | static Int32 _itimediff(UInt32 later, UInt32 earlier)
146 | {
147 | return ((Int32)(later - earlier));
148 | }
149 |
150 | // KCP Segment Definition
151 | internal class Segment {
152 | internal UInt32 conv = 0;
153 | internal UInt32 cmd = 0;
154 | internal UInt32 frg = 0;
155 | internal UInt32 wnd = 0;
156 | internal UInt32 ts = 0;
157 | internal UInt32 sn = 0;
158 | internal UInt32 una = 0;
159 | internal UInt32 resendts = 0;
160 | internal UInt32 rto = 0;
161 | internal UInt32 fastack = 0;
162 | internal UInt32 xmit = 0;
163 | internal byte[] data;
164 |
165 | internal Segment(int size)
166 | {
167 | this.data = new byte[size];
168 | }
169 |
170 | // encode a segment into buffer
171 | internal int encode(byte[] ptr, int offset) {
172 |
173 | var offset_ = offset;
174 |
175 | offset += ikcp_encode32u(ptr, offset, conv);
176 | offset += ikcp_encode8u(ptr, offset, (byte)cmd);
177 | offset += ikcp_encode8u(ptr, offset, (byte)frg);
178 | offset += ikcp_encode16u(ptr, offset, (UInt16)wnd);
179 | offset += ikcp_encode32u(ptr, offset, ts);
180 | offset += ikcp_encode32u(ptr, offset, sn);
181 | offset += ikcp_encode32u(ptr, offset, una);
182 | offset += ikcp_encode32u(ptr, offset, (UInt32)data.Length);
183 |
184 | return offset - offset_;
185 | }
186 | }
187 |
188 | // kcp members.
189 | UInt32 conv; UInt32 mtu; UInt32 mss; UInt32 state;
190 | UInt32 snd_una; UInt32 snd_nxt; UInt32 rcv_nxt;
191 | UInt32 ts_recent; UInt32 ts_lastack; UInt32 ssthresh;
192 | UInt32 rx_rttval; UInt32 rx_srtt; UInt32 rx_rto; UInt32 rx_minrto;
193 | UInt32 snd_wnd; UInt32 rcv_wnd; UInt32 rmt_wnd; UInt32 cwnd; UInt32 probe;
194 | UInt32 current; UInt32 interval; UInt32 ts_flush; UInt32 xmit;
195 | UInt32 nodelay; UInt32 updated;
196 | UInt32 ts_probe; UInt32 probe_wait;
197 | UInt32 dead_link; UInt32 incr;
198 |
199 | Segment[] snd_queue = new Segment[0];
200 | Segment[] rcv_queue = new Segment[0];
201 | Segment[] snd_buf = new Segment[0];
202 | Segment[] rcv_buf = new Segment[0];
203 |
204 | UInt32[] acklist = new UInt32[0];
205 |
206 | byte[] buffer;
207 | Int32 fastresend;
208 | Int32 nocwnd;
209 | Int32 logmask;
210 | // buffer, size
211 | Action output;
212 |
213 | // create a new kcp control object, 'conv' must equal in two endpoint
214 | // from the same connection.
215 | public KCP(UInt32 conv_, Action output_) {
216 | conv = conv_;
217 | snd_wnd = IKCP_WND_SND;
218 | rcv_wnd = IKCP_WND_RCV;
219 | rmt_wnd = IKCP_WND_RCV;
220 | mtu = IKCP_MTU_DEF;
221 | mss = mtu - IKCP_OVERHEAD;
222 |
223 | rx_rto = IKCP_RTO_DEF;
224 | rx_minrto = IKCP_RTO_MIN;
225 | interval = IKCP_INTERVAL;
226 | ts_flush = IKCP_INTERVAL;
227 | ssthresh = IKCP_THRESH_INIT;
228 | dead_link = IKCP_DEADLINK;
229 | buffer = new byte[(mtu+IKCP_OVERHEAD)*3];
230 | output = output_;
231 | }
232 |
233 | // check the size of next message in the recv queue
234 | public int PeekSize() {
235 |
236 | if (0 == rcv_queue.Length) return -1;
237 |
238 | var seq = rcv_queue[0];
239 |
240 | if (0 == seq.frg) return seq.data.Length;
241 |
242 | if (rcv_queue.Length < seq.frg + 1) return -1;
243 |
244 | int length = 0;
245 |
246 | foreach (var item in rcv_queue) {
247 | length += item.data.Length;
248 | if (0 == item.frg)
249 | break;
250 | }
251 |
252 | return length;
253 | }
254 |
255 | // user/upper level recv: returns size, returns below zero for EAGAIN
256 | public int Recv(byte[] buffer) {
257 |
258 | if (0 == rcv_queue.Length) return -1;
259 |
260 | var peekSize = PeekSize();
261 | if (0 > peekSize) return -2;
262 |
263 | if (peekSize > buffer.Length) return -3;
264 |
265 | var fast_recover = false;
266 | if (rcv_queue.Length >= rcv_wnd) fast_recover = true;
267 |
268 | // merge fragment.
269 | var count = 0;
270 | var n = 0;
271 | foreach (var seg in rcv_queue) {
272 | Array.Copy(seg.data, 0, buffer, n, seg.data.Length);
273 | n += seg.data.Length;
274 | count++;
275 | if (0 == seg.frg) break;
276 | }
277 |
278 | if (0 < count) {
279 | rcv_queue = slice(rcv_queue, count, rcv_queue.Length);
280 | }
281 |
282 | // move available data from rcv_buf -> rcv_queue
283 | count = 0;
284 | foreach (var seg in rcv_buf) {
285 | if (seg.sn == rcv_nxt && rcv_queue.Length < rcv_wnd) {
286 | rcv_queue = append(rcv_queue, seg);
287 | rcv_nxt++;
288 | count++;
289 | } else {
290 | break;
291 | }
292 | }
293 |
294 | if(0 < count) rcv_buf = slice(rcv_buf, count, rcv_buf.Length);
295 |
296 | // fast recover
297 | if (rcv_queue.Length < rcv_wnd && fast_recover) {
298 | // ready to send back IKCP_CMD_WINS in ikcp_flush
299 | // tell remote my window size
300 | probe |= IKCP_ASK_TELL;
301 | }
302 |
303 | return n;
304 | }
305 |
306 | // user/upper level send, returns below zero for error
307 | public int Send(byte[] buffer) {
308 |
309 | if (0 == buffer.Length) return -1;
310 |
311 | var count = 0;
312 |
313 | if (buffer.Length < mss)
314 | count = 1;
315 | else
316 | count = (int)(buffer.Length + mss - 1) / (int)mss;
317 |
318 | if (255 < count) return -2;
319 |
320 | if (0 == count) count = 1;
321 |
322 | var offset = 0;
323 |
324 | for (var i = 0; i < count; i++) {
325 | var size = 0;
326 | if (buffer.Length - offset > mss)
327 | size = (int)mss;
328 | else
329 | size = buffer.Length - offset;
330 |
331 | var seg = new Segment(size);
332 | Array.Copy(buffer, offset, seg.data, 0, size);
333 | offset += size;
334 | seg.frg = (UInt32)(count - i - 1);
335 | snd_queue = append(snd_queue, seg);
336 | }
337 |
338 | return 0;
339 | }
340 |
341 | // update ack.
342 | void update_ack(Int32 rtt)
343 | {
344 | if (0 == rx_srtt)
345 | {
346 | rx_srtt = (UInt32)rtt;
347 | rx_rttval = (UInt32)rtt / 2;
348 | }
349 | else
350 | {
351 | Int32 delta = (Int32)((UInt32)rtt - rx_srtt);
352 | if (0 > delta) delta = -delta;
353 |
354 | rx_rttval = (3 * rx_rttval + (uint)delta) / 4;
355 | rx_srtt = (UInt32)((7 * rx_srtt + rtt) / 8);
356 | if (rx_srtt < 1) rx_srtt = 1;
357 | }
358 |
359 | var rto = (int)(rx_srtt + _imax_(1, 4 * rx_rttval));
360 | rx_rto = _ibound_(rx_minrto, (UInt32)rto, IKCP_RTO_MAX);
361 | }
362 |
363 | void shrink_buf() {
364 | if (snd_buf.Length > 0)
365 | snd_una = snd_buf[0].sn;
366 | else
367 | snd_una = snd_nxt;
368 | }
369 |
370 | void parse_ack(UInt32 sn) {
371 |
372 | if (_itimediff(sn, snd_una) < 0 || _itimediff(sn, snd_nxt) >= 0) return;
373 |
374 | var index = 0;
375 | foreach (var seg in snd_buf) {
376 | if (sn == seg.sn)
377 | {
378 | snd_buf = append(slice(snd_buf, 0, index), slice(snd_buf, index + 1, snd_buf.Length));
379 | break;
380 | }
381 | else
382 | {
383 | seg.fastack++;
384 | }
385 |
386 | index++;
387 | }
388 | }
389 |
390 | void parse_una(UInt32 una) {
391 | var count = 0;
392 | foreach (var seg in snd_buf) {
393 | if (_itimediff(una, seg.sn) > 0)
394 | count++;
395 | else
396 | break;
397 | }
398 |
399 | if (0 < count) snd_buf = slice(snd_buf, count, snd_buf.Length);
400 | }
401 |
402 | void ack_push(UInt32 sn, UInt32 ts) {
403 | acklist = append(acklist, new UInt32[2]{sn, ts});
404 | }
405 |
406 | void ack_get(int p, ref UInt32 sn, ref UInt32 ts) {
407 | sn = acklist[p * 2 + 0];
408 | ts = acklist[p * 2 + 1];
409 | }
410 |
411 | void parse_data(Segment newseg) {
412 | var sn = newseg.sn;
413 | if (_itimediff(sn, rcv_nxt + rcv_wnd) >= 0 || _itimediff(sn, rcv_nxt) < 0) return;
414 |
415 | var n = rcv_buf.Length - 1;
416 | var after_idx = -1;
417 | var repeat = false;
418 | for (var i = n; i >= 0; i--) {
419 | var seg = rcv_buf[i];
420 | if (seg.sn == sn) {
421 | repeat = true;
422 | break;
423 | }
424 |
425 | if (_itimediff(sn, seg.sn) > 0) {
426 | after_idx = i;
427 | break;
428 | }
429 | }
430 |
431 | if (!repeat) {
432 | if (after_idx == -1)
433 | rcv_buf = append(new Segment[1] { newseg }, rcv_buf);
434 | else
435 | rcv_buf = append(slice(rcv_buf, 0, after_idx + 1), append(new Segment[1] { newseg }, slice(rcv_buf, after_idx + 1, rcv_buf.Length)));
436 | }
437 |
438 | // move available data from rcv_buf -> rcv_queue
439 | var count = 0;
440 | foreach (var seg in rcv_buf) {
441 | if (seg.sn == rcv_nxt && rcv_queue.Length < rcv_wnd)
442 | {
443 | rcv_queue = append(rcv_queue, seg);
444 | rcv_nxt++;
445 | count++;
446 | }
447 | else
448 | {
449 | break;
450 | }
451 | }
452 |
453 | if (0 < count) {
454 | rcv_buf = slice(rcv_buf, count, rcv_buf.Length);
455 | }
456 | }
457 |
458 | // when you received a low level packet (eg. UDP packet), call it
459 | public int Input(byte[] data) {
460 |
461 | var s_una = snd_una;
462 | if (data.Length < IKCP_OVERHEAD) return 0;
463 |
464 | var offset = 0;
465 |
466 | while (true)
467 | {
468 | UInt32 ts = 0;
469 | UInt32 sn = 0;
470 | UInt32 length = 0;
471 | UInt32 una = 0;
472 | UInt32 conv_ = 0;
473 |
474 | UInt16 wnd = 0;
475 |
476 | byte cmd = 0;
477 | byte frg = 0;
478 |
479 | if (data.Length - offset < IKCP_OVERHEAD) break;
480 |
481 | offset += ikcp_decode32u(data, offset, ref conv_);
482 |
483 | if (conv != conv_) return -1;
484 |
485 | offset += ikcp_decode8u(data, offset, ref cmd);
486 | offset += ikcp_decode8u(data, offset, ref frg);
487 | offset += ikcp_decode16u(data, offset, ref wnd);
488 | offset += ikcp_decode32u(data, offset, ref ts);
489 | offset += ikcp_decode32u(data, offset, ref sn);
490 | offset += ikcp_decode32u(data, offset, ref una);
491 | offset += ikcp_decode32u(data, offset, ref length);
492 |
493 | if (data.Length - offset < length) return -2;
494 |
495 | switch (cmd) {
496 | case IKCP_CMD_PUSH:
497 | case IKCP_CMD_ACK:
498 | case IKCP_CMD_WASK:
499 | case IKCP_CMD_WINS:
500 | break;
501 | default:
502 | return -3;
503 | }
504 |
505 | rmt_wnd = (UInt32)wnd;
506 | parse_una(una);
507 | shrink_buf();
508 |
509 | if (IKCP_CMD_ACK == cmd) {
510 | if (_itimediff(current, ts) >= 0) {
511 | update_ack(_itimediff(current, ts));
512 | }
513 | parse_ack(sn);
514 | shrink_buf();
515 | }
516 | else if (IKCP_CMD_PUSH == cmd) {
517 | if (_itimediff(sn, rcv_nxt + rcv_wnd) < 0) {
518 | ack_push(sn, ts);
519 | if (_itimediff(sn, rcv_nxt) >= 0) {
520 | var seg = new Segment((int)length);
521 | seg.conv = conv_;
522 | seg.cmd = (UInt32)cmd;
523 | seg.frg = (UInt32)frg;
524 | seg.wnd = (UInt32)wnd;
525 | seg.ts = ts;
526 | seg.sn = sn;
527 | seg.una = una;
528 |
529 | if (length > 0) Array.Copy(data, offset, seg.data, 0, length);
530 |
531 | parse_data(seg);
532 | }
533 | }
534 | }
535 | else if (IKCP_CMD_WASK == cmd) {
536 | // ready to send back IKCP_CMD_WINS in Ikcp_flush
537 | // tell remote my window size
538 | probe |= IKCP_ASK_TELL;
539 | }
540 | else if (IKCP_CMD_WINS == cmd)
541 | {
542 | // do nothing
543 | }
544 | else {
545 | return -3;
546 | }
547 |
548 | offset += (int)length;
549 | }
550 |
551 | if (_itimediff(snd_una, s_una) > 0) {
552 | if (cwnd < rmt_wnd) {
553 | var mss_ = mss;
554 | if (cwnd < ssthresh)
555 | {
556 | cwnd++;
557 | incr += mss_;
558 | }
559 | else {
560 | if(incr < mss_) {
561 | incr = mss_;
562 | }
563 | incr += (mss_ * mss_) / incr + (mss_ / 16);
564 | if ((cwnd + 1) * mss_ <= incr) cwnd++;
565 | }
566 | if (cwnd > rmt_wnd) {
567 | cwnd = rmt_wnd;
568 | incr = rmt_wnd * mss_;
569 | }
570 | }
571 | }
572 |
573 | return 0;
574 | }
575 |
576 | Int32 wnd_unused() {
577 | if (rcv_queue.Length < rcv_wnd)
578 | return (Int32)(int)rcv_wnd - rcv_queue.Length;
579 | return 0;
580 | }
581 |
582 | // flush pending data
583 | void flush() {
584 | var current_ = current;
585 | var buffer_ = buffer;
586 | var change = 0;
587 | var lost = 0;
588 |
589 | if (0 == updated) return;
590 |
591 | var seg = new Segment(0);
592 | seg.conv = conv;
593 | seg.cmd = IKCP_CMD_ACK;
594 | seg.wnd = (UInt32)wnd_unused();
595 | seg.una = rcv_nxt;
596 |
597 | // flush acknowledges
598 | var count = acklist.Length / 2;
599 | var offset = 0;
600 | for (var i = 0; i < count; i++) {
601 | if (offset + IKCP_OVERHEAD > mtu)
602 | {
603 | output(buffer, offset);
604 | //Array.Clear(buffer, 0, offset);
605 | offset = 0;
606 | }
607 | ack_get(i, ref seg.sn, ref seg.ts);
608 | offset += seg.encode(buffer, offset);
609 | }
610 | acklist = new UInt32[0];
611 |
612 | // probe window size (if remote window size equals zero)
613 | if (0 == rmt_wnd)
614 | {
615 | if (0 == probe_wait)
616 | {
617 | probe_wait = IKCP_PROBE_INIT;
618 | ts_probe = current + probe_wait;
619 | }
620 | else
621 | {
622 | if (_itimediff(current, ts_probe) >= 0)
623 | {
624 | if (probe_wait < IKCP_PROBE_INIT)
625 | probe_wait = IKCP_PROBE_INIT;
626 | probe_wait += probe_wait / 2;
627 | if (probe_wait > IKCP_PROBE_LIMIT)
628 | probe_wait = IKCP_PROBE_LIMIT;
629 | ts_probe = current + probe_wait;
630 | probe |= IKCP_ASK_SEND;
631 | }
632 | }
633 | }
634 | else {
635 | ts_probe = 0;
636 | probe_wait = 0;
637 | }
638 |
639 | // flush window probing commands
640 | if ((probe & IKCP_ASK_SEND) != 0) {
641 | seg.cmd = IKCP_CMD_WASK;
642 | if (offset + IKCP_OVERHEAD > (int)mtu) {
643 | output(buffer, offset);
644 | //Array.Clear(buffer, 0, offset);
645 | offset = 0;
646 | }
647 | offset += seg.encode(buffer, offset);
648 | }
649 |
650 | probe = 0;
651 |
652 | // calculate window size
653 | var cwnd_ = _imin_(snd_wnd, rmt_wnd);
654 | if (0 == nocwnd)
655 | cwnd_ = _imin_(cwnd, cwnd_);
656 |
657 | count = 0;
658 | for (var k = 0; k < snd_queue.Length; k++ )
659 | {
660 | if (_itimediff(snd_nxt, snd_una + cwnd_) >= 0) break;
661 |
662 | var newseg = snd_queue[k];
663 | newseg.conv = conv;
664 | newseg.cmd = IKCP_CMD_PUSH;
665 | newseg.wnd = seg.wnd;
666 | newseg.ts = current_;
667 | newseg.sn = snd_nxt;
668 | newseg.una = rcv_nxt;
669 | newseg.resendts = current_;
670 | newseg.rto = rx_rto;
671 | newseg.fastack = 0;
672 | newseg.xmit = 0;
673 | snd_buf = append(snd_buf, newseg);
674 | snd_nxt++;
675 | count++;
676 | }
677 |
678 | if (0 < count) {
679 | snd_queue = slice(snd_queue, count, snd_queue.Length);
680 | }
681 |
682 | // calculate resent
683 | var resent = (UInt32)fastresend;
684 | if (fastresend <= 0) resent = 0xffffffff;
685 | var rtomin = rx_rto >> 3;
686 | if(nodelay != 0) rtomin = 0;
687 |
688 | // flush data segments
689 | foreach (var segment in snd_buf) {
690 | var needsend = false;
691 | var debug = _itimediff(current_, segment.resendts);
692 | if (0 == segment.xmit) {
693 | needsend = true;
694 | segment.xmit++;
695 | segment.rto = rx_rto;
696 | segment.resendts = current_ + segment.rto + rtomin;
697 | }
698 | else if (_itimediff(current_, segment.resendts) >= 0) {
699 | needsend = true;
700 | segment.xmit++;
701 | xmit++;
702 | if (0 == nodelay)
703 | segment.rto += rx_rto;
704 | else
705 | segment.rto += rx_rto / 2;
706 | segment.resendts = current_ + segment.rto;
707 | lost = 1;
708 | }
709 | else if (segment.fastack >= resent) {
710 | needsend = true;
711 | segment.xmit++;
712 | segment.fastack = 0;
713 | segment.resendts = current_ + segment.rto;
714 | change++;
715 | }
716 |
717 | if (needsend) {
718 | segment.ts = current_;
719 | segment.wnd = seg.wnd;
720 | segment.una = rcv_nxt;
721 |
722 | var need = IKCP_OVERHEAD + segment.data.Length;
723 | if (offset + need > mtu) {
724 | output(buffer, offset);
725 | //Array.Clear(buffer, 0, offset);
726 | offset = 0;
727 | }
728 |
729 | offset += segment.encode(buffer, offset);
730 | if (segment.data.Length > 0) {
731 | Array.Copy(segment.data, 0, buffer, offset, segment.data.Length);
732 | offset += segment.data.Length;
733 | }
734 |
735 | if (segment.xmit >= dead_link) {
736 | state = 0;
737 | }
738 | }
739 | }
740 |
741 | // flash remain segments
742 | if (offset > 0) {
743 | output(buffer, offset);
744 | //Array.Clear(buffer, 0, offset);
745 | offset = 0;
746 | }
747 |
748 | // update ssthresh
749 | if (change != 0) {
750 | var inflight = snd_nxt - snd_una;
751 | ssthresh = inflight / 2;
752 | if (ssthresh < IKCP_THRESH_MIN)
753 | ssthresh = IKCP_THRESH_MIN;
754 | cwnd = ssthresh + resent;
755 | incr = cwnd * mss;
756 | }
757 |
758 | if (lost != 0) {
759 | ssthresh = cwnd / 2;
760 | if (ssthresh < IKCP_THRESH_MIN)
761 | ssthresh = IKCP_THRESH_MIN;
762 | cwnd = 1;
763 | incr = mss;
764 | }
765 |
766 | if (cwnd < 1) {
767 | cwnd = 1;
768 | incr = mss;
769 | }
770 | }
771 |
772 | // update state (call it repeatedly, every 10ms-100ms), or you can ask
773 | // ikcp_check when to call it again (without ikcp_input/_send calling).
774 | // 'current' - current timestamp in millisec.
775 | public void Update(UInt32 current_)
776 | {
777 |
778 | current = current_;
779 |
780 | if (0 == updated) {
781 | updated = 1;
782 | ts_flush = current;
783 | }
784 |
785 | var slap = _itimediff(current, ts_flush);
786 |
787 | if (slap >= 10000 || slap < -10000) {
788 | ts_flush = current;
789 | slap = 0;
790 | }
791 |
792 | if (slap >= 0) {
793 | ts_flush += interval;
794 | if (_itimediff(current, ts_flush) >= 0)
795 | ts_flush = current + interval;
796 | flush();
797 | }
798 | }
799 |
800 | // Determine when should you invoke ikcp_update:
801 | // returns when you should invoke ikcp_update in millisec, if there
802 | // is no ikcp_input/_send calling. you can call ikcp_update in that
803 | // time, instead of call update repeatly.
804 | // Important to reduce unnacessary ikcp_update invoking. use it to
805 | // schedule ikcp_update (eg. implementing an epoll-like mechanism,
806 | // or optimize ikcp_update when handling massive kcp connections)
807 | public UInt32 Check(UInt32 current_)
808 | {
809 |
810 | if (0 == updated) return current_;
811 |
812 | var ts_flush_ = ts_flush;
813 | var tm_flush_ = 0x7fffffff;
814 | var tm_packet = 0x7fffffff;
815 | var minimal = 0;
816 |
817 | if (_itimediff(current_, ts_flush_) >= 10000 || _itimediff(current_, ts_flush_) < -10000)
818 | {
819 | ts_flush_ = current_;
820 | }
821 |
822 | if (_itimediff(current_, ts_flush_) >= 0) return current_;
823 |
824 | tm_flush_ = (int)_itimediff(ts_flush_, current_);
825 |
826 | foreach (var seg in snd_buf) {
827 | var diff = _itimediff(seg.resendts, current_);
828 | if (diff <= 0) return current_;
829 | if (diff < tm_packet) tm_packet = (int)diff;
830 | }
831 |
832 | minimal = (int)tm_packet;
833 | if (tm_packet >= tm_flush_) minimal = (int)tm_flush_;
834 | if (minimal >= interval) minimal = (int)interval;
835 |
836 | return current_ + (UInt32)minimal;
837 | }
838 |
839 | // change MTU size, default is 1400
840 | public int SetMtu(Int32 mtu_)
841 | {
842 | if (mtu_ < 50 || mtu_ < (Int32)IKCP_OVERHEAD) return -1;
843 |
844 | var buffer_ = new byte[(mtu_ + IKCP_OVERHEAD) * 3];
845 | if (null == buffer_) return -2;
846 |
847 | mtu = (UInt32)mtu_;
848 | mss = mtu - IKCP_OVERHEAD;
849 | buffer = buffer_;
850 | return 0;
851 | }
852 |
853 | public int Interval(Int32 interval_)
854 | {
855 | if (interval_ > 5000) {
856 | interval_ = 5000;
857 | }
858 | else if (interval_ < 10) {
859 | interval_ = 10;
860 | }
861 | interval = (UInt32)interval_;
862 | return 0;
863 | }
864 |
865 | // fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
866 | // nodelay: 0:disable(default), 1:enable
867 | // interval: internal update timer interval in millisec, default is 100ms
868 | // resend: 0:disable fast resend(default), 1:enable fast resend
869 | // nc: 0:normal congestion control(default), 1:disable congestion control
870 | public int NoDelay(int nodelay_, int interval_, int resend_, int nc_)
871 | {
872 |
873 | if (nodelay_ > 0) {
874 | nodelay = (UInt32)nodelay_;
875 | if (nodelay_ != 0)
876 | rx_minrto = IKCP_RTO_NDL;
877 | else
878 | rx_minrto = IKCP_RTO_MIN;
879 | }
880 |
881 | if (interval_ >= 0) {
882 | if (interval_ > 5000)
883 | {
884 | interval_ = 5000;
885 | }
886 | else if (interval_ < 10)
887 | {
888 | interval_ = 10;
889 | }
890 | interval = (UInt32)interval_;
891 | }
892 |
893 | if (resend_ >= 0) fastresend = resend_;
894 |
895 | if (nc_ >= 0) nocwnd = nc_;
896 |
897 | return 0;
898 | }
899 |
900 | // set maximum window size: sndwnd=32, rcvwnd=32 by default
901 | public int WndSize(int sndwnd, int rcvwnd)
902 | {
903 | if (sndwnd > 0)
904 | snd_wnd = (UInt32)sndwnd;
905 |
906 | if (rcvwnd > 0)
907 | rcv_wnd = (UInt32)rcvwnd;
908 | return 0;
909 | }
910 |
911 | // get how many packet is waiting to be sent
912 | public int WaitSnd()
913 | {
914 | return snd_buf.Length + snd_queue.Length;
915 | }
916 | }
917 |
--------------------------------------------------------------------------------
/Unity3D/KCP_LE.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | namespace Network_Kcp
7 | {
8 | ///
9 | /// c#实现的默认是 小端的数据
10 | ///
11 | public partial class KCP_LE
12 | {
13 | public const int IKCP_RTO_NDL = 30; // no delay min rto
14 | public const int IKCP_RTO_MIN = 100; // normal min rto
15 | public const int IKCP_RTO_DEF = 200;
16 | public const int IKCP_RTO_MAX = 60000;
17 | public const int IKCP_CMD_PUSH = 81; // cmd: push data
18 | public const int IKCP_CMD_ACK = 82; // cmd: ack
19 | public const int IKCP_CMD_WASK = 83; // cmd: window probe (ask)
20 | public const int IKCP_CMD_WINS = 84; // cmd: window size (tell)
21 | public const int IKCP_ASK_SEND = 1; // need to send IKCP_CMD_WASK
22 | public const int IKCP_ASK_TELL = 2; // need to send IKCP_CMD_WINS
23 | public const int IKCP_WND_SND = 32;
24 | public const int IKCP_WND_RCV = 32;
25 | public const int IKCP_MTU_DEF = 1400;
26 | public const int IKCP_ACK_FAST = 3;
27 | public const int IKCP_INTERVAL = 100;
28 | public const int IKCP_OVERHEAD = 24;
29 | public const int IKCP_DEADLINK = 10;
30 | public const int IKCP_THRESH_INIT = 2;
31 | public const int IKCP_THRESH_MIN = 2;
32 | public const int IKCP_PROBE_INIT = 7000; // 7 secs to probe window size
33 | public const int IKCP_PROBE_LIMIT = 120000; // up to 120 secs to probe window
34 |
35 |
36 | // encode 8 bits unsigned int
37 | public static int ikcp_encode8u(byte[] p, int offset, byte c) {
38 | p[0 + offset] = c;
39 | return 1;
40 | }
41 |
42 | // decode 8 bits unsigned int
43 | public static int ikcp_decode8u(byte[] p, int offset, ref byte c) {
44 | c = p[0 + offset];
45 | return 1;
46 | }
47 |
48 | /* encode 16 bits unsigned int (lsb) */
49 | public static int ikcp_encode16u(byte[] p, int offset, UInt16 w) {
50 | p[0 + offset] = (byte)(w >> 0);
51 | p[1 + offset] = (byte)(w >> 8);
52 | return 2;
53 | }
54 |
55 | /* decode 16 bits unsigned int (lsb) */
56 | public static int ikcp_decode16u(byte[] p, int offset, ref UInt16 c) {
57 | UInt16 result = 0;
58 | result |= (UInt16)p[0 + offset];
59 | result |= (UInt16)(p[1 + offset] << 8);
60 | c = result;
61 | return 2;
62 | }
63 |
64 | /* encode 32 bits unsigned int (lsb) */
65 | public static int ikcp_encode32u(byte[] p, int offset, UInt32 l) {
66 | p[0 + offset] = (byte)(l >> 0);
67 | p[1 + offset] = (byte)(l >> 8);
68 | p[2 + offset] = (byte)(l >> 16);
69 | p[3 + offset] = (byte)(l >> 24);
70 | return 4;
71 | }
72 |
73 | /* decode 32 bits unsigned int (lsb) */
74 | public static int ikcp_decode32u(byte[] p, int offset, ref UInt32 c) {
75 | UInt32 result = 0;
76 | result |= (UInt32)p[0 + offset];
77 | result |= (UInt32)(p[1 + offset] << 8);
78 | result |= (UInt32)(p[2 + offset] << 16);
79 | result |= (UInt32)(p[3 + offset] << 24);
80 | c = result;
81 | return 4;
82 | }
83 |
84 | public static byte[] slice(byte[] p, int start, int stop) {
85 | var bytes = new byte[stop - start];
86 | Array.Copy(p, start, bytes, 0, bytes.Length);
87 | return bytes;
88 | }
89 |
90 | public static T[] slice(T[] p, int start, int stop) {
91 | var arr = new T[stop - start];
92 | var index = 0;
93 | for (var i = start; i < stop; i++) {
94 | arr[index] = p[i];
95 | index++;
96 | }
97 |
98 | return arr;
99 | }
100 |
101 | public static byte[] append(byte[] p, byte c) {
102 | var bytes = new byte[p.Length + 1];
103 | Array.Copy(p, bytes, p.Length);
104 | bytes[p.Length] = c;
105 | return bytes;
106 | }
107 |
108 | public static T[] append(T[] p, T c) {
109 | var arr = new T[p.Length + 1];
110 | for (var i = 0; i < p.Length; i++)
111 | arr[i] = p[i];
112 | arr[p.Length] = c;
113 | return arr;
114 | }
115 |
116 | public static T[] append(T[] p, T[] cs) {
117 | var arr = new T[p.Length + cs.Length];
118 | for (var i = 0; i < p.Length; i++)
119 | arr[i] = p[i];
120 | for (var i = 0; i < cs.Length; i++)
121 | arr[p.Length + i] = cs[i];
122 | return arr;
123 | }
124 |
125 | static UInt32 _imin_(UInt32 a, UInt32 b) {
126 | return a <= b ? a : b;
127 | }
128 |
129 | static UInt32 _imax_(UInt32 a, UInt32 b) {
130 | return a >= b ? a : b;
131 | }
132 |
133 | static UInt32 _ibound_(UInt32 lower, UInt32 middle, UInt32 upper) {
134 | return _imin_(_imax_(lower, middle), upper);
135 | }
136 |
137 | static Int32 _itimediff(UInt32 later, UInt32 earlier) {
138 | return ((Int32)(later - earlier));
139 | }
140 |
141 | // KCP Segment Definition
142 | internal class Segment
143 | {
144 | internal UInt32 conv = 0;
145 | internal UInt32 cmd = 0;
146 | internal UInt32 frg = 0;
147 | internal UInt32 wnd = 0;
148 | internal UInt32 ts = 0;
149 | internal UInt32 sn = 0;
150 | internal UInt32 una = 0;
151 | internal UInt32 resendts = 0;
152 | internal UInt32 rto = 0;
153 | internal UInt32 fastack = 0;
154 | internal UInt32 xmit = 0;
155 | internal byte[] data;
156 |
157 | internal Segment(int size) {
158 | this.data = new byte[size];
159 | }
160 |
161 | // encode a segment into buffer
162 | internal int encode(byte[] ptr, int offset) {
163 |
164 | var offset_ = offset;
165 |
166 | offset += ikcp_encode32u(ptr, offset, conv);
167 | offset += ikcp_encode8u(ptr, offset, (byte)cmd);
168 | offset += ikcp_encode8u(ptr, offset, (byte)frg);
169 | offset += ikcp_encode16u(ptr, offset, (UInt16)wnd);
170 | offset += ikcp_encode32u(ptr, offset, ts);
171 | offset += ikcp_encode32u(ptr, offset, sn);
172 | offset += ikcp_encode32u(ptr, offset, una);
173 | offset += ikcp_encode32u(ptr, offset, (UInt32)data.Length);
174 |
175 | return offset - offset_;
176 | }
177 | }
178 |
179 | // kcp members.
180 | UInt32 conv; UInt32 mtu; UInt32 mss; UInt32 state;
181 | UInt32 snd_una; UInt32 snd_nxt; UInt32 rcv_nxt;
182 | UInt32 ts_recent; UInt32 ts_lastack; UInt32 ssthresh;
183 | UInt32 rx_rttval; UInt32 rx_srtt; UInt32 rx_rto; UInt32 rx_minrto;
184 | UInt32 snd_wnd; UInt32 rcv_wnd; UInt32 rmt_wnd; UInt32 cwnd; UInt32 probe;
185 | UInt32 current; UInt32 interval; UInt32 ts_flush; UInt32 xmit;
186 | UInt32 nodelay; UInt32 updated;
187 | UInt32 ts_probe; UInt32 probe_wait;
188 | UInt32 dead_link; UInt32 incr;
189 |
190 | Segment[] snd_queue = new Segment[0];
191 | Segment[] rcv_queue = new Segment[0];
192 | Segment[] snd_buf = new Segment[0];
193 | Segment[] rcv_buf = new Segment[0];
194 |
195 | UInt32[] acklist = new UInt32[0];
196 |
197 | byte[] buffer;
198 | Int32 fastresend;
199 | Int32 nocwnd;
200 | Int32 logmask;
201 | // buffer, size
202 | Action output;
203 |
204 | // create a new kcp control object, 'conv' must equal in two endpoint
205 | // from the same connection.
206 | public KCP_LE(UInt32 conv_, Action output_) {
207 | conv = conv_;
208 | snd_wnd = IKCP_WND_SND;
209 | rcv_wnd = IKCP_WND_RCV;
210 | rmt_wnd = IKCP_WND_RCV;
211 | mtu = IKCP_MTU_DEF;
212 | mss = mtu - IKCP_OVERHEAD;
213 |
214 | rx_rto = IKCP_RTO_DEF;
215 | rx_minrto = IKCP_RTO_MIN;
216 | interval = IKCP_INTERVAL;
217 | ts_flush = IKCP_INTERVAL;
218 | ssthresh = IKCP_THRESH_INIT;
219 | dead_link = IKCP_DEADLINK;
220 | buffer = new byte[(mtu + IKCP_OVERHEAD) * 3];
221 | output = output_;
222 | }
223 |
224 | // check the size of next message in the recv queue
225 | public int PeekSize() {
226 |
227 | if (0 == rcv_queue.Length) return -1;
228 |
229 | var seq = rcv_queue[0];
230 |
231 | if (0 == seq.frg) return seq.data.Length;
232 |
233 | if (rcv_queue.Length < seq.frg + 1) return -1;
234 |
235 | int length = 0;
236 |
237 | foreach (var item in rcv_queue) {
238 | length += item.data.Length;
239 | if (0 == item.frg)
240 | break;
241 | }
242 |
243 | return length;
244 | }
245 |
246 | // user/upper level recv: returns size, returns below zero for EAGAIN
247 | public int Recv(byte[] buffer) {
248 |
249 | if (0 == rcv_queue.Length) return -1;
250 |
251 | var peekSize = PeekSize();
252 | if (0 > peekSize) return -2;
253 |
254 | if (peekSize > buffer.Length) return -3;
255 |
256 | var fast_recover = false;
257 | if (rcv_queue.Length >= rcv_wnd) fast_recover = true;
258 |
259 | // merge fragment.
260 | var count = 0;
261 | var n = 0;
262 | foreach (var seg in rcv_queue) {
263 | Array.Copy(seg.data, 0, buffer, n, seg.data.Length);
264 | n += seg.data.Length;
265 | count++;
266 | if (0 == seg.frg) break;
267 | }
268 |
269 | if (0 < count) {
270 | rcv_queue = slice(rcv_queue, count, rcv_queue.Length);
271 | }
272 |
273 | // move available data from rcv_buf -> rcv_queue
274 | count = 0;
275 | foreach (var seg in rcv_buf) {
276 | if (seg.sn == rcv_nxt && rcv_queue.Length < rcv_wnd) {
277 | rcv_queue = append(rcv_queue, seg);
278 | rcv_nxt++;
279 | count++;
280 | }
281 | else {
282 | break;
283 | }
284 | }
285 |
286 | if (0 < count) rcv_buf = slice(rcv_buf, count, rcv_buf.Length);
287 |
288 | // fast recover
289 | if (rcv_queue.Length < rcv_wnd && fast_recover) {
290 | // ready to send back IKCP_CMD_WINS in ikcp_flush
291 | // tell remote my window size
292 | probe |= IKCP_ASK_TELL;
293 | }
294 |
295 | return n;
296 | }
297 |
298 | // user/upper level send, returns below zero for error
299 | public int Send(byte[] buffer) {
300 | int bufferSize = buffer.Length;
301 | if (0 == bufferSize) return -1;
302 |
303 | var count = 0;
304 |
305 | if (bufferSize < mss)
306 | count = 1;
307 | else
308 | count = (int)(bufferSize + mss - 1) / (int)mss;
309 |
310 | if (255 < count) return -2;
311 |
312 | if (0 == count) count = 1;
313 |
314 | var offset = 0;
315 |
316 | for (var i = 0; i < count; i++) {
317 | var size = 0;
318 | if (bufferSize - offset > mss)
319 | size = (int)mss;
320 | else
321 | size = bufferSize - offset;
322 |
323 | var seg = new Segment(size);
324 | Array.Copy(buffer, offset, seg.data, 0, size);
325 | offset += size;
326 | seg.frg = (UInt32)(count - i - 1);
327 | snd_queue = append(snd_queue, seg);
328 | }
329 |
330 | return 0;
331 | }
332 |
333 | // update ack.
334 | void update_ack(Int32 rtt) {
335 | if (0 == rx_srtt) {
336 | rx_srtt = (UInt32)rtt;
337 | rx_rttval = (UInt32)rtt / 2;
338 | }
339 | else {
340 | Int32 delta = (Int32)((UInt32)rtt - rx_srtt);
341 | if (0 > delta) delta = -delta;
342 |
343 | rx_rttval = (3 * rx_rttval + (uint)delta) / 4;
344 | rx_srtt = (UInt32)((7 * rx_srtt + rtt) / 8);
345 | if (rx_srtt < 1) rx_srtt = 1;
346 | }
347 |
348 | var rto = (int)(rx_srtt + _imax_(1, 4 * rx_rttval));
349 | rx_rto = _ibound_(rx_minrto, (UInt32)rto, IKCP_RTO_MAX);
350 | }
351 |
352 | void shrink_buf() {
353 | if (snd_buf.Length > 0)
354 | snd_una = snd_buf[0].sn;
355 | else
356 | snd_una = snd_nxt;
357 | }
358 |
359 | void parse_ack(UInt32 sn) {
360 |
361 | if (_itimediff(sn, snd_una) < 0 || _itimediff(sn, snd_nxt) >= 0) return;
362 |
363 | var index = 0;
364 | foreach (var seg in snd_buf) {
365 | if (sn == seg.sn) {
366 | snd_buf = append(slice(snd_buf, 0, index), slice(snd_buf, index + 1, snd_buf.Length));
367 | break;
368 | }
369 | else {
370 | seg.fastack++;
371 | }
372 |
373 | index++;
374 | }
375 | }
376 |
377 | void parse_una(UInt32 una) {
378 | var count = 0;
379 | foreach (var seg in snd_buf) {
380 | if (_itimediff(una, seg.sn) > 0)
381 | count++;
382 | else
383 | break;
384 | }
385 |
386 | if (0 < count) snd_buf = slice(snd_buf, count, snd_buf.Length);
387 | }
388 |
389 | void ack_push(UInt32 sn, UInt32 ts) {
390 | acklist = append(acklist, new UInt32[2] { sn, ts });
391 | }
392 |
393 | void ack_get(int p, ref UInt32 sn, ref UInt32 ts) {
394 | sn = acklist[p * 2 + 0];
395 | ts = acklist[p * 2 + 1];
396 | }
397 |
398 | void parse_data(Segment newseg) {
399 | var sn = newseg.sn;
400 | if (_itimediff(sn, rcv_nxt + rcv_wnd) >= 0 || _itimediff(sn, rcv_nxt) < 0) return;
401 |
402 | var n = rcv_buf.Length - 1;
403 | var after_idx = -1;
404 | var repeat = false;
405 | for (var i = n; i >= 0; i--) {
406 | var seg = rcv_buf[i];
407 | if (seg.sn == sn) {
408 | repeat = true;
409 | break;
410 | }
411 |
412 | if (_itimediff(sn, seg.sn) > 0) {
413 | after_idx = i;
414 | break;
415 | }
416 | }
417 |
418 | if (!repeat) {
419 | if (after_idx == -1)
420 | rcv_buf = append(new Segment[1] { newseg }, rcv_buf);
421 | else
422 | rcv_buf = append(slice(rcv_buf, 0, after_idx + 1), append(new Segment[1] { newseg }, slice(rcv_buf, after_idx + 1, rcv_buf.Length)));
423 | }
424 |
425 | // move available data from rcv_buf -> rcv_queue
426 | var count = 0;
427 | foreach (var seg in rcv_buf) {
428 | if (seg.sn == rcv_nxt && rcv_queue.Length < rcv_wnd) {
429 | rcv_queue = append(rcv_queue, seg);
430 | rcv_nxt++;
431 | count++;
432 | }
433 | else {
434 | break;
435 | }
436 | }
437 |
438 | if (0 < count) {
439 | rcv_buf = slice(rcv_buf, count, rcv_buf.Length);
440 | }
441 | }
442 |
443 | // when you received a low level packet (eg. UDP packet), call it
444 | public int Input(byte[] data) {
445 |
446 | var s_una = snd_una;
447 | if (data.Length < IKCP_OVERHEAD) return 0;
448 |
449 | var offset = 0;
450 |
451 | while (true) {
452 | UInt32 ts = 0;
453 | UInt32 sn = 0;
454 | UInt32 length = 0;
455 | UInt32 una = 0;
456 | UInt32 conv_ = 0;
457 |
458 | UInt16 wnd = 0;
459 |
460 | byte cmd = 0;
461 | byte frg = 0;
462 |
463 | if (data.Length - offset < IKCP_OVERHEAD) break;
464 |
465 | offset += ikcp_decode32u(data, offset, ref conv_);
466 |
467 | if (conv != conv_) return -1;
468 |
469 | offset += ikcp_decode8u(data, offset, ref cmd);
470 | offset += ikcp_decode8u(data, offset, ref frg);
471 | offset += ikcp_decode16u(data, offset, ref wnd);
472 | offset += ikcp_decode32u(data, offset, ref ts);
473 | offset += ikcp_decode32u(data, offset, ref sn);
474 | offset += ikcp_decode32u(data, offset, ref una);
475 | offset += ikcp_decode32u(data, offset, ref length);
476 |
477 | if (data.Length - offset < length) return -2;
478 |
479 | switch (cmd) {
480 | case IKCP_CMD_PUSH:
481 | case IKCP_CMD_ACK:
482 | case IKCP_CMD_WASK:
483 | case IKCP_CMD_WINS:
484 | break;
485 | default:
486 | return -3;
487 | }
488 |
489 | rmt_wnd = (UInt32)wnd;
490 | parse_una(una);
491 | shrink_buf();
492 |
493 | if (IKCP_CMD_ACK == cmd) {
494 | if (_itimediff(current, ts) >= 0) {
495 | update_ack(_itimediff(current, ts));
496 | }
497 | parse_ack(sn);
498 | shrink_buf();
499 | }
500 | else if (IKCP_CMD_PUSH == cmd) {
501 | if (_itimediff(sn, rcv_nxt + rcv_wnd) < 0) {
502 | ack_push(sn, ts);
503 | if (_itimediff(sn, rcv_nxt) >= 0) {
504 | var seg = new Segment((int)length);
505 | seg.conv = conv_;
506 | seg.cmd = (UInt32)cmd;
507 | seg.frg = (UInt32)frg;
508 | seg.wnd = (UInt32)wnd;
509 | seg.ts = ts;
510 | seg.sn = sn;
511 | seg.una = una;
512 |
513 | if (length > 0) Array.Copy(data, offset, seg.data, 0, length);
514 |
515 | parse_data(seg);
516 | }
517 | }
518 | }
519 | else if (IKCP_CMD_WASK == cmd) {
520 | // ready to send back IKCP_CMD_WINS in Ikcp_flush
521 | // tell remote my window size
522 | probe |= IKCP_ASK_TELL;
523 | }
524 | else if (IKCP_CMD_WINS == cmd) {
525 | // do nothing
526 | }
527 | else {
528 | return -3;
529 | }
530 |
531 | offset += (int)length;
532 | }
533 |
534 | if (_itimediff(snd_una, s_una) > 0) {
535 | if (cwnd < rmt_wnd) {
536 | var mss_ = mss;
537 | if (cwnd < ssthresh) {
538 | cwnd++;
539 | incr += mss_;
540 | }
541 | else {
542 | if (incr < mss_) {
543 | incr = mss_;
544 | }
545 | incr += (mss_ * mss_) / incr + (mss_ / 16);
546 | if ((cwnd + 1) * mss_ <= incr) cwnd++;
547 | }
548 | if (cwnd > rmt_wnd) {
549 | cwnd = rmt_wnd;
550 | incr = rmt_wnd * mss_;
551 | }
552 | }
553 | }
554 |
555 | return 0;
556 | }
557 |
558 | Int32 wnd_unused() {
559 | if (rcv_queue.Length < rcv_wnd)
560 | return (Int32)(int)rcv_wnd - rcv_queue.Length;
561 | return 0;
562 | }
563 |
564 | // flush pending data
565 | void flush() {
566 | var current_ = current;
567 | var buffer_ = buffer;
568 | var change = 0;
569 | var lost = 0;
570 |
571 | if (0 == updated) return;
572 |
573 | var seg = new Segment(0);
574 | seg.conv = conv;
575 | seg.cmd = IKCP_CMD_ACK;
576 | seg.wnd = (UInt32)wnd_unused();
577 | seg.una = rcv_nxt;
578 |
579 | // flush acknowledges
580 | var count = acklist.Length / 2;
581 | var offset = 0;
582 | for (var i = 0; i < count; i++) {
583 | if (offset + IKCP_OVERHEAD > mtu) {
584 | output(buffer, offset);
585 | //Array.Clear(buffer, 0, offset);
586 | offset = 0;
587 | }
588 | ack_get(i, ref seg.sn, ref seg.ts);
589 | offset += seg.encode(buffer, offset);
590 | }
591 | acklist = new UInt32[0];
592 |
593 | // probe window size (if remote window size equals zero)
594 | if (0 == rmt_wnd) {
595 | if (0 == probe_wait) {
596 | probe_wait = IKCP_PROBE_INIT;
597 | ts_probe = current + probe_wait;
598 | }
599 | else {
600 | if (_itimediff(current, ts_probe) >= 0) {
601 | if (probe_wait < IKCP_PROBE_INIT)
602 | probe_wait = IKCP_PROBE_INIT;
603 | probe_wait += probe_wait / 2;
604 | if (probe_wait > IKCP_PROBE_LIMIT)
605 | probe_wait = IKCP_PROBE_LIMIT;
606 | ts_probe = current + probe_wait;
607 | probe |= IKCP_ASK_SEND;
608 | }
609 | }
610 | }
611 | else {
612 | ts_probe = 0;
613 | probe_wait = 0;
614 | }
615 |
616 | // flush window probing commands
617 | if ((probe & IKCP_ASK_SEND) != 0) {
618 | seg.cmd = IKCP_CMD_WASK;
619 | if (offset + IKCP_OVERHEAD > (int)mtu) {
620 | output(buffer, offset);
621 | //Array.Clear(buffer, 0, offset);
622 | offset = 0;
623 | }
624 | offset += seg.encode(buffer, offset);
625 | }
626 |
627 | probe = 0;
628 |
629 | // calculate window size
630 | var cwnd_ = _imin_(snd_wnd, rmt_wnd);
631 | if (0 == nocwnd)
632 | cwnd_ = _imin_(cwnd, cwnd_);
633 |
634 | count = 0;
635 | for (var k = 0; k < snd_queue.Length; k++) {
636 | if (_itimediff(snd_nxt, snd_una + cwnd_) >= 0) break;
637 |
638 | var newseg = snd_queue[k];
639 | newseg.conv = conv;
640 | newseg.cmd = IKCP_CMD_PUSH;
641 | newseg.wnd = seg.wnd;
642 | newseg.ts = current_;
643 | newseg.sn = snd_nxt;
644 | newseg.una = rcv_nxt;
645 | newseg.resendts = current_;
646 | newseg.rto = rx_rto;
647 | newseg.fastack = 0;
648 | newseg.xmit = 0;
649 | snd_buf = append(snd_buf, newseg);
650 | snd_nxt++;
651 | count++;
652 | }
653 |
654 | if (0 < count) {
655 | snd_queue = slice(snd_queue, count, snd_queue.Length);
656 | }
657 |
658 | // calculate resent
659 | var resent = (UInt32)fastresend;
660 | if (fastresend <= 0) resent = 0xffffffff;
661 | var rtomin = rx_rto >> 3;
662 | if (nodelay != 0) rtomin = 0;
663 |
664 | // flush data segments
665 | foreach (var segment in snd_buf) {
666 | var needsend = false;
667 | var debug = _itimediff(current_, segment.resendts);
668 | if (0 == segment.xmit) {
669 | needsend = true;
670 | segment.xmit++;
671 | segment.rto = rx_rto;
672 | segment.resendts = current_ + segment.rto + rtomin;
673 | }
674 | else if (_itimediff(current_, segment.resendts) >= 0) {
675 | needsend = true;
676 | segment.xmit++;
677 | xmit++;
678 | if (0 == nodelay)
679 | segment.rto += rx_rto;
680 | else
681 | segment.rto += rx_rto / 2;
682 | segment.resendts = current_ + segment.rto;
683 | lost = 1;
684 | }
685 | else if (segment.fastack >= resent) {
686 | needsend = true;
687 | segment.xmit++;
688 | segment.fastack = 0;
689 | segment.resendts = current_ + segment.rto;
690 | change++;
691 | }
692 |
693 | if (needsend) {
694 | segment.ts = current_;
695 | segment.wnd = seg.wnd;
696 | segment.una = rcv_nxt;
697 |
698 | var need = IKCP_OVERHEAD + segment.data.Length;
699 | if (offset + need > mtu) {
700 | output(buffer, offset);
701 | //Array.Clear(buffer, 0, offset);
702 | offset = 0;
703 | }
704 |
705 | offset += segment.encode(buffer, offset);
706 | if (segment.data.Length > 0) {
707 | Array.Copy(segment.data, 0, buffer, offset, segment.data.Length);
708 | offset += segment.data.Length;
709 | }
710 |
711 | if (segment.xmit >= dead_link) {
712 | state = 0;
713 | }
714 | }
715 | }
716 |
717 | // flash remain segments
718 | if (offset > 0) {
719 | output(buffer, offset);
720 | //Array.Clear(buffer, 0, offset);
721 | offset = 0;
722 | }
723 |
724 | // update ssthresh
725 | if (change != 0) {
726 | var inflight = snd_nxt - snd_una;
727 | ssthresh = inflight / 2;
728 | if (ssthresh < IKCP_THRESH_MIN)
729 | ssthresh = IKCP_THRESH_MIN;
730 | cwnd = ssthresh + resent;
731 | incr = cwnd * mss;
732 | }
733 |
734 | if (lost != 0) {
735 | ssthresh = cwnd / 2;
736 | if (ssthresh < IKCP_THRESH_MIN)
737 | ssthresh = IKCP_THRESH_MIN;
738 | cwnd = 1;
739 | incr = mss;
740 | }
741 |
742 | if (cwnd < 1) {
743 | cwnd = 1;
744 | incr = mss;
745 | }
746 | }
747 |
748 | // update state (call it repeatedly, every 10ms-100ms), or you can ask
749 | // ikcp_check when to call it again (without ikcp_input/_send calling).
750 | // 'current' - current timestamp in millisec.
751 | public void Update(long current_L_) {
752 | UInt32 current_ = (UInt32)current_L_;
753 |
754 | current = current_;
755 |
756 | if (0 == updated) {
757 | updated = 1;
758 | ts_flush = current;
759 | }
760 |
761 | var slap = _itimediff(current, ts_flush);
762 |
763 | if (slap >= 10000 || slap < -10000) {
764 | ts_flush = current;
765 | slap = 0;
766 | }
767 |
768 | if (slap >= 0) {
769 | ts_flush += interval;
770 | if (_itimediff(current, ts_flush) >= 0)
771 | ts_flush = current + interval;
772 | flush();
773 | }
774 | }
775 |
776 | // Determine when should you invoke ikcp_update:
777 | // returns when you should invoke ikcp_update in millisec, if there
778 | // is no ikcp_input/_send calling. you can call ikcp_update in that
779 | // time, instead of call update repeatly.
780 | // Important to reduce unnacessary ikcp_update invoking. use it to
781 | // schedule ikcp_update (eg. implementing an epoll-like mechanism,
782 | // or optimize ikcp_update when handling massive kcp connections)
783 | public UInt32 Check(long current_L_) {
784 | UInt32 current_ = (UInt32)current_L_;
785 |
786 | if (0 == updated) return current_;
787 |
788 | var ts_flush_ = ts_flush;
789 | var tm_flush_ = 0x7fffffff;
790 | var tm_packet = 0x7fffffff;
791 | var minimal = 0;
792 |
793 | if (_itimediff(current_, ts_flush_) >= 10000 || _itimediff(current_, ts_flush_) < -10000) {
794 | ts_flush_ = current_;
795 | }
796 |
797 | if (_itimediff(current_, ts_flush_) >= 0) return current_;
798 |
799 | tm_flush_ = (int)_itimediff(ts_flush_, current_);
800 |
801 | foreach (var seg in snd_buf) {
802 | var diff = _itimediff(seg.resendts, current_);
803 | if (diff <= 0) return current_;
804 | if (diff < tm_packet) tm_packet = (int)diff;
805 | }
806 |
807 | minimal = (int)tm_packet;
808 | if (tm_packet >= tm_flush_) minimal = (int)tm_flush_;
809 | if (minimal >= interval) minimal = (int)interval;
810 |
811 | return current_ + (UInt32)minimal;
812 | }
813 |
814 | // change MTU size, default is 1400
815 | public int SetMtu(Int32 mtu_) {
816 | if (mtu_ < 50 || mtu_ < (Int32)IKCP_OVERHEAD) return -1;
817 |
818 | var buffer_ = new byte[(mtu_ + IKCP_OVERHEAD) * 3];
819 | if (null == buffer_) return -2;
820 |
821 | mtu = (UInt32)mtu_;
822 | mss = mtu - IKCP_OVERHEAD;
823 | buffer = buffer_;
824 | return 0;
825 | }
826 |
827 | public int Interval(Int32 interval_) {
828 | if (interval_ > 5000) {
829 | interval_ = 5000;
830 | }
831 | else if (interval_ < 10) {
832 | interval_ = 10;
833 | }
834 | interval = (UInt32)interval_;
835 | return 0;
836 | }
837 |
838 | // fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
839 | // nodelay: 0:disable(default), 1:enable
840 | // interval: internal update timer interval in millisec, default is 100ms
841 | // resend: 0:disable fast resend(default), 1:enable fast resend
842 | // nc: 0:normal congestion control(default), 1:disable congestion control
843 | public int NoDelay(int nodelay_, int interval_, int resend_, int nc_) {
844 |
845 | if (nodelay_ > 0) {
846 | nodelay = (UInt32)nodelay_;
847 | if (nodelay_ != 0)
848 | rx_minrto = IKCP_RTO_NDL;
849 | else
850 | rx_minrto = IKCP_RTO_MIN;
851 | }
852 |
853 | if (interval_ >= 0) {
854 | if (interval_ > 5000) {
855 | interval_ = 5000;
856 | }
857 | else if (interval_ < 10) {
858 | interval_ = 10;
859 | }
860 | interval = (UInt32)interval_;
861 | }
862 |
863 | if (resend_ >= 0) fastresend = resend_;
864 |
865 | if (nc_ >= 0) nocwnd = nc_;
866 |
867 | return 0;
868 | }
869 |
870 | // set maximum window size: sndwnd=32, rcvwnd=32 by default
871 | public int WndSize(int sndwnd, int rcvwnd) {
872 | if (sndwnd > 0)
873 | snd_wnd = (UInt32)sndwnd;
874 |
875 | if (rcvwnd > 0)
876 | rcv_wnd = (UInt32)rcvwnd;
877 | return 0;
878 | }
879 |
880 | // get how many packet is waiting to be sent
881 | public int WaitSnd() {
882 | return snd_buf.Length + snd_queue.Length;
883 | }
884 | }
885 |
886 | }
--------------------------------------------------------------------------------