mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-07 16:45:10 +00:00
[+] 基于UniTask的Kcp实现
[+] 基于UniTask的Kcp实现
This commit is contained in:
8
Assets/TEngine/Libraries/System.meta
Normal file
8
Assets/TEngine/Libraries/System.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6cc6a0f7dfa57346baacf8e3397b1bb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Assets/TEngine/Libraries/System/System.Buffers.dll
Normal file
BIN
Assets/TEngine/Libraries/System/System.Buffers.dll
Normal file
Binary file not shown.
33
Assets/TEngine/Libraries/System/System.Buffers.dll.meta
Normal file
33
Assets/TEngine/Libraries/System/System.Buffers.dll.meta
Normal file
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 756621d6e10e8484ba8cbdc3baeab00a
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
38
Assets/TEngine/Libraries/System/System.Buffers.xml
Normal file
38
Assets/TEngine/Libraries/System/System.Buffers.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><doc>
|
||||
<assembly>
|
||||
<name>System.Buffers</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:System.Buffers.ArrayPool`1">
|
||||
<summary>Provides a resource pool that enables reusing instances of type <see cref="T[]"></see>.</summary>
|
||||
<typeparam name="T">The type of the objects that are in the resource pool.</typeparam>
|
||||
</member>
|
||||
<member name="M:System.Buffers.ArrayPool`1.#ctor">
|
||||
<summary>Initializes a new instance of the <see cref="T:System.Buffers.ArrayPool`1"></see> class.</summary>
|
||||
</member>
|
||||
<member name="M:System.Buffers.ArrayPool`1.Create">
|
||||
<summary>Creates a new instance of the <see cref="T:System.Buffers.ArrayPool`1"></see> class.</summary>
|
||||
<returns>A new instance of the <see cref="System.Buffers.ArrayPool`1"></see> class.</returns>
|
||||
</member>
|
||||
<member name="M:System.Buffers.ArrayPool`1.Create(System.Int32,System.Int32)">
|
||||
<summary>Creates a new instance of the <see cref="T:System.Buffers.ArrayPool`1"></see> class using the specifed configuration.</summary>
|
||||
<param name="maxArrayLength">The maximum length of an array instance that may be stored in the pool.</param>
|
||||
<param name="maxArraysPerBucket">The maximum number of array instances that may be stored in each bucket in the pool. The pool groups arrays of similar lengths into buckets for faster access.</param>
|
||||
<returns>A new instance of the <see cref="System.Buffers.ArrayPool`1"></see> class with the specified configuration.</returns>
|
||||
</member>
|
||||
<member name="M:System.Buffers.ArrayPool`1.Rent(System.Int32)">
|
||||
<summary>Retrieves a buffer that is at least the requested length.</summary>
|
||||
<param name="minimumLength">The minimum length of the array.</param>
|
||||
<returns>An array of type <see cref="T[]"></see> that is at least <paramref name="minimumLength">minimumLength</paramref> in length.</returns>
|
||||
</member>
|
||||
<member name="M:System.Buffers.ArrayPool`1.Return(`0[],System.Boolean)">
|
||||
<summary>Returns an array to the pool that was previously obtained using the <see cref="M:System.Buffers.ArrayPool`1.Rent(System.Int32)"></see> method on the same <see cref="T:System.Buffers.ArrayPool`1"></see> instance.</summary>
|
||||
<param name="array">A buffer to return to the pool that was previously obtained using the <see cref="M:System.Buffers.ArrayPool`1.Rent(System.Int32)"></see> method.</param>
|
||||
<param name="clearArray">Indicates whether the contents of the buffer should be cleared before reuse. If <paramref name="clearArray">clearArray</paramref> is set to true, and if the pool will store the buffer to enable subsequent reuse, the <see cref="M:System.Buffers.ArrayPool`1.Return(`0[],System.Boolean)"></see> method will clear the <paramref name="array">array</paramref> of its contents so that a subsequent caller using the <see cref="M:System.Buffers.ArrayPool`1.Rent(System.Int32)"></see> method will not see the content of the previous caller. If <paramref name="clearArray">clearArray</paramref> is set to false or if the pool will release the buffer, the array&#39;s contents are left unchanged.</param>
|
||||
</member>
|
||||
<member name="P:System.Buffers.ArrayPool`1.Shared">
|
||||
<summary>Gets a shared <see cref="T:System.Buffers.ArrayPool`1"></see> instance.</summary>
|
||||
<returns>A shared <see cref="System.Buffers.ArrayPool`1"></see> instance.</returns>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
7
Assets/TEngine/Libraries/System/System.Buffers.xml.meta
Normal file
7
Assets/TEngine/Libraries/System/System.Buffers.xml.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2287aec89d99c9d47bc787f282793669
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Assets/TEngine/Libraries/System/System.Memory.dll
Normal file
BIN
Assets/TEngine/Libraries/System/System.Memory.dll
Normal file
Binary file not shown.
33
Assets/TEngine/Libraries/System/System.Memory.dll.meta
Normal file
33
Assets/TEngine/Libraries/System/System.Memory.dll.meta
Normal file
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 437db656869ad974fb3637ca83f6ecd6
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
355
Assets/TEngine/Libraries/System/System.Memory.xml
Normal file
355
Assets/TEngine/Libraries/System/System.Memory.xml
Normal file
@@ -0,0 +1,355 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><doc>
|
||||
<assembly>
|
||||
<name>System.Memory</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:System.Span`1">
|
||||
<typeparam name="T"></typeparam>
|
||||
</member>
|
||||
<member name="M:System.Span`1.#ctor(`0[])">
|
||||
<param name="array"></param>
|
||||
</member>
|
||||
<member name="M:System.Span`1.#ctor(System.Void*,System.Int32)">
|
||||
<param name="pointer"></param>
|
||||
<param name="length"></param>
|
||||
</member>
|
||||
<member name="M:System.Span`1.#ctor(`0[],System.Int32)">
|
||||
<param name="array"></param>
|
||||
<param name="start"></param>
|
||||
</member>
|
||||
<member name="M:System.Span`1.#ctor(`0[],System.Int32,System.Int32)">
|
||||
<param name="array"></param>
|
||||
<param name="start"></param>
|
||||
<param name="length"></param>
|
||||
</member>
|
||||
<member name="M:System.Span`1.Clear">
|
||||
|
||||
</member>
|
||||
<member name="M:System.Span`1.CopyTo(System.Span{`0})">
|
||||
<param name="destination"></param>
|
||||
</member>
|
||||
<member name="M:System.Span`1.DangerousCreate(System.Object,`0@,System.Int32)">
|
||||
<param name="obj"></param>
|
||||
<param name="objectData"></param>
|
||||
<param name="length"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.Span`1.DangerousGetPinnableReference">
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="P:System.Span`1.Empty">
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.Span`1.Equals(System.Object)">
|
||||
<param name="obj"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.Span`1.Fill(`0)">
|
||||
<param name="value"></param>
|
||||
</member>
|
||||
<member name="M:System.Span`1.GetHashCode">
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="P:System.Span`1.IsEmpty">
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="P:System.Span`1.Item(System.Int32)">
|
||||
<param name="index"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="P:System.Span`1.Length">
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.Span`1.op_Equality(System.Span{`0},System.Span{`0})">
|
||||
<param name="left"></param>
|
||||
<param name="right"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.Span`1.op_Implicit(System.ArraySegment{T})~System.Span{T}">
|
||||
<param name="arraySegment"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.Span`1.op_Implicit(System.Span{T})~System.ReadOnlySpan{T}">
|
||||
<param name="span"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.Span`1.op_Implicit(T[])~System.Span{T}">
|
||||
<param name="array"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.Span`1.op_Inequality(System.Span{`0},System.Span{`0})">
|
||||
<param name="left"></param>
|
||||
<param name="right"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.Span`1.Slice(System.Int32)">
|
||||
<param name="start"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.Span`1.Slice(System.Int32,System.Int32)">
|
||||
<param name="start"></param>
|
||||
<param name="length"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.Span`1.ToArray">
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.Span`1.TryCopyTo(System.Span{`0})">
|
||||
<param name="destination"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:System.SpanExtensions">
|
||||
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.AsBytes``1(System.ReadOnlySpan{``0})">
|
||||
<param name="source"></param>
|
||||
<typeparam name="T"></typeparam>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.AsBytes``1(System.Span{``0})">
|
||||
<param name="source"></param>
|
||||
<typeparam name="T"></typeparam>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.AsSpan(System.String)">
|
||||
<param name="text"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.AsSpan``1(System.ArraySegment{``0})">
|
||||
<param name="arraySegment"></param>
|
||||
<typeparam name="T"></typeparam>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.AsSpan``1(``0[])">
|
||||
<param name="array"></param>
|
||||
<typeparam name="T"></typeparam>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.CopyTo``1(``0[],System.Span{``0})">
|
||||
<param name="array"></param>
|
||||
<param name="destination"></param>
|
||||
<typeparam name="T"></typeparam>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.IndexOf(System.Span{System.Byte},System.ReadOnlySpan{System.Byte})">
|
||||
<param name="span"></param>
|
||||
<param name="value"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.IndexOf(System.Span{System.Byte},System.Byte)">
|
||||
<param name="span"></param>
|
||||
<param name="value"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.IndexOf(System.ReadOnlySpan{System.Byte},System.Byte)">
|
||||
<param name="span"></param>
|
||||
<param name="value"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.IndexOf(System.ReadOnlySpan{System.Byte},System.ReadOnlySpan{System.Byte})">
|
||||
<param name="span"></param>
|
||||
<param name="value"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.IndexOf``1(System.ReadOnlySpan{``0},System.ReadOnlySpan{``0})">
|
||||
<param name="span"></param>
|
||||
<param name="value"></param>
|
||||
<typeparam name="T"></typeparam>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.IndexOf``1(System.ReadOnlySpan{``0},``0)">
|
||||
<param name="span"></param>
|
||||
<param name="value"></param>
|
||||
<typeparam name="T"></typeparam>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.IndexOf``1(System.Span{``0},System.ReadOnlySpan{``0})">
|
||||
<param name="span"></param>
|
||||
<param name="value"></param>
|
||||
<typeparam name="T"></typeparam>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.IndexOf``1(System.Span{``0},``0)">
|
||||
<param name="span"></param>
|
||||
<param name="value"></param>
|
||||
<typeparam name="T"></typeparam>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.IndexOfAny(System.ReadOnlySpan{System.Byte},System.Byte,System.Byte,System.Byte)">
|
||||
<param name="span"></param>
|
||||
<param name="value0"></param>
|
||||
<param name="value1"></param>
|
||||
<param name="value2"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.IndexOfAny(System.Span{System.Byte},System.Byte,System.Byte,System.Byte)">
|
||||
<param name="span"></param>
|
||||
<param name="value0"></param>
|
||||
<param name="value1"></param>
|
||||
<param name="value2"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.IndexOfAny(System.Span{System.Byte},System.Byte,System.Byte)">
|
||||
<param name="span"></param>
|
||||
<param name="value0"></param>
|
||||
<param name="value1"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.IndexOfAny(System.ReadOnlySpan{System.Byte},System.ReadOnlySpan{System.Byte})">
|
||||
<param name="span"></param>
|
||||
<param name="values"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.IndexOfAny(System.Span{System.Byte},System.ReadOnlySpan{System.Byte})">
|
||||
<param name="span"></param>
|
||||
<param name="values"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.IndexOfAny(System.ReadOnlySpan{System.Byte},System.Byte,System.Byte)">
|
||||
<param name="span"></param>
|
||||
<param name="value0"></param>
|
||||
<param name="value1"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.NonPortableCast``2(System.ReadOnlySpan{``0})">
|
||||
<param name="source"></param>
|
||||
<typeparam name="TFrom"></typeparam>
|
||||
<typeparam name="TTo"></typeparam>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.NonPortableCast``2(System.Span{``0})">
|
||||
<param name="source"></param>
|
||||
<typeparam name="TFrom"></typeparam>
|
||||
<typeparam name="TTo"></typeparam>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.SequenceEqual(System.ReadOnlySpan{System.Byte},System.ReadOnlySpan{System.Byte})">
|
||||
<param name="first"></param>
|
||||
<param name="second"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.SequenceEqual(System.Span{System.Byte},System.ReadOnlySpan{System.Byte})">
|
||||
<param name="first"></param>
|
||||
<param name="second"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.SequenceEqual``1(System.ReadOnlySpan{``0},System.ReadOnlySpan{``0})">
|
||||
<param name="first"></param>
|
||||
<param name="second"></param>
|
||||
<typeparam name="T"></typeparam>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.SequenceEqual``1(System.Span{``0},System.ReadOnlySpan{``0})">
|
||||
<param name="first"></param>
|
||||
<param name="second"></param>
|
||||
<typeparam name="T"></typeparam>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.StartsWith(System.ReadOnlySpan{System.Byte},System.ReadOnlySpan{System.Byte})">
|
||||
<param name="span"></param>
|
||||
<param name="value"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.StartsWith(System.Span{System.Byte},System.ReadOnlySpan{System.Byte})">
|
||||
<param name="span"></param>
|
||||
<param name="value"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.StartsWith``1(System.ReadOnlySpan{``0},System.ReadOnlySpan{``0})">
|
||||
<param name="span"></param>
|
||||
<param name="value"></param>
|
||||
<typeparam name="T"></typeparam>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.SpanExtensions.StartsWith``1(System.Span{``0},System.ReadOnlySpan{``0})">
|
||||
<param name="span"></param>
|
||||
<param name="value"></param>
|
||||
<typeparam name="T"></typeparam>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:System.ReadOnlySpan`1">
|
||||
<typeparam name="T"></typeparam>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.#ctor(`0[])">
|
||||
<param name="array"></param>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.#ctor(System.Void*,System.Int32)">
|
||||
<param name="pointer"></param>
|
||||
<param name="length"></param>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.#ctor(`0[],System.Int32)">
|
||||
<param name="array"></param>
|
||||
<param name="start"></param>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.#ctor(`0[],System.Int32,System.Int32)">
|
||||
<param name="array"></param>
|
||||
<param name="start"></param>
|
||||
<param name="length"></param>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.CopyTo(System.Span{`0})">
|
||||
<param name="destination"></param>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.DangerousCreate(System.Object,`0@,System.Int32)">
|
||||
<param name="obj"></param>
|
||||
<param name="objectData"></param>
|
||||
<param name="length"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.DangerousGetPinnableReference">
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="P:System.ReadOnlySpan`1.Empty">
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.Equals(System.Object)">
|
||||
<param name="obj"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.GetHashCode">
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="P:System.ReadOnlySpan`1.IsEmpty">
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="P:System.ReadOnlySpan`1.Item(System.Int32)">
|
||||
<param name="index"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="P:System.ReadOnlySpan`1.Length">
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.op_Equality(System.ReadOnlySpan{`0},System.ReadOnlySpan{`0})">
|
||||
<param name="left"></param>
|
||||
<param name="right"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.op_Implicit(System.ArraySegment{T})~System.ReadOnlySpan{T}">
|
||||
<param name="arraySegment"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.op_Implicit(T[])~System.ReadOnlySpan{T}">
|
||||
<param name="array"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.op_Inequality(System.ReadOnlySpan{`0},System.ReadOnlySpan{`0})">
|
||||
<param name="left"></param>
|
||||
<param name="right"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.Slice(System.Int32)">
|
||||
<param name="start"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.Slice(System.Int32,System.Int32)">
|
||||
<param name="start"></param>
|
||||
<param name="length"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.ToArray">
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:System.ReadOnlySpan`1.TryCopyTo(System.Span{`0})">
|
||||
<param name="destination"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
7
Assets/TEngine/Libraries/System/System.Memory.xml.meta
Normal file
7
Assets/TEngine/Libraries/System/System.Memory.xml.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d08838ffea10da9408f3fbc06d944a56
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f49dd2bd3ef32e4da0cee305406f282
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,200 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><doc>
|
||||
<assembly>
|
||||
<name>System.Runtime.CompilerServices.Unsafe</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:System.Runtime.CompilerServices.Unsafe">
|
||||
<summary>Contains generic, low-level functionality for manipulating pointers.</summary>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.Add``1(``0@,System.Int32)">
|
||||
<summary>Adds an element offset to the given reference.</summary>
|
||||
<param name="source">The reference to add the offset to.</param>
|
||||
<param name="elementOffset">The offset to add.</param>
|
||||
<typeparam name="T">The type of reference.</typeparam>
|
||||
<returns>A new reference that reflects the addition of offset to pointer.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.Add``1(``0@,System.IntPtr)">
|
||||
<summary>Adds an element offset to the given reference.</summary>
|
||||
<param name="source">The reference to add the offset to.</param>
|
||||
<param name="elementOffset">The offset to add.</param>
|
||||
<typeparam name="T">The type of reference.</typeparam>
|
||||
<returns>A new reference that reflects the addition of offset to pointer.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.AddByteOffset``1(``0@,System.IntPtr)">
|
||||
<summary>Adds a byte offset to the given reference.</summary>
|
||||
<param name="source">The reference to add the offset to.</param>
|
||||
<param name="byteOffset">The offset to add.</param>
|
||||
<typeparam name="T">The type of reference.</typeparam>
|
||||
<returns>A new reference that reflects the addition of byte offset to pointer.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.AreSame``1(``0@,``0@)">
|
||||
<summary>Determines whether the specified references point to the same location.</summary>
|
||||
<param name="left">The first reference to compare.</param>
|
||||
<param name="right">The second reference to compare.</param>
|
||||
<typeparam name="T">The type of reference.</typeparam>
|
||||
<returns>true if <paramref name="left">left</paramref> and <paramref name="right">right</paramref> point to the same location; otherwise, false.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.As``1(System.Object)">
|
||||
<summary>Casts the given object to the specified type.</summary>
|
||||
<param name="o">The object to cast.</param>
|
||||
<typeparam name="T">The type which the object will be cast to.</typeparam>
|
||||
<returns>The original object, casted to the given type.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.As``2(``0@)">
|
||||
<summary>Reinterprets the given reference as a reference to a value of type <typeparamref name="TTo">TTo</typeparamref>.</summary>
|
||||
<param name="source">The reference to reinterpret.</param>
|
||||
<typeparam name="TFrom">The type of reference to reinterpret..</typeparam>
|
||||
<typeparam name="TTo">The desired type of the reference.</typeparam>
|
||||
<returns>A reference to a value of type <typeparamref name="TTo">TTo</typeparamref>.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.AsPointer``1(``0@)">
|
||||
<summary>Returns a pointer to the given by-ref parameter.</summary>
|
||||
<param name="value">The object whose pointer is obtained.</param>
|
||||
<typeparam name="T">The type of object.</typeparam>
|
||||
<returns>A pointer to the given value.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.AsRef``1(System.Void*)">
|
||||
<summary>Reinterprets the given location as a reference to a value of type <typeparamref name="T">T</typeparamref>.</summary>
|
||||
<param name="source">The location of the value to reference.</param>
|
||||
<typeparam name="T">The type of the interpreted location.</typeparam>
|
||||
<returns>A reference to a value of type <typeparamref name="T">T</typeparamref>.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.ByteOffset``1(``0@,``0@)">
|
||||
<summary>Determines the byte offset from origin to target from the given references.</summary>
|
||||
<param name="origin">The reference to origin.</param>
|
||||
<param name="target">The reference to target.</param>
|
||||
<typeparam name="T">The type of reference.</typeparam>
|
||||
<returns>Byte offset from origin to target i.e. <paramref name="target">target</paramref> - <paramref name="origin">origin</paramref>.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.Copy``1(System.Void*,``0@)">
|
||||
<summary>Copies a value of type <typeparamref name="T">T</typeparamref> to the given location.</summary>
|
||||
<param name="destination">The location to copy to.</param>
|
||||
<param name="source">A reference to the value to copy.</param>
|
||||
<typeparam name="T">The type of value to copy.</typeparam>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.Copy``1(``0@,System.Void*)">
|
||||
<summary>Copies a value of type <typeparamref name="T">T</typeparamref> to the given location.</summary>
|
||||
<param name="destination">The location to copy to.</param>
|
||||
<param name="source">A pointer to the value to copy.</param>
|
||||
<typeparam name="T">The type of value to copy.</typeparam>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.CopyBlock(System.Byte@,System.Byte@,System.UInt32)">
|
||||
<summary>Copies bytes from the source address to the destination address.</summary>
|
||||
<param name="destination">The destination address to copy to.</param>
|
||||
<param name="source">The source address to copy from.</param>
|
||||
<param name="byteCount">The number of bytes to copy.</param>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.CopyBlock(System.Void*,System.Void*,System.UInt32)">
|
||||
<summary>Copies bytes from the source address to the destination address.</summary>
|
||||
<param name="destination">The destination address to copy to.</param>
|
||||
<param name="source">The source address to copy from.</param>
|
||||
<param name="byteCount">The number of bytes to copy.</param>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.CopyBlockUnaligned(System.Void*,System.Void*,System.UInt32)">
|
||||
<summary>Copies bytes from the source address to the destination address
|
||||
without assuming architecture dependent alignment of the addresses.</summary>
|
||||
<param name="destination">The destination address to copy to.</param>
|
||||
<param name="source">The source address to copy from.</param>
|
||||
<param name="byteCount">The number of bytes to copy.</param>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.CopyBlockUnaligned(System.Byte@,System.Byte@,System.UInt32)">
|
||||
<summary>Copies bytes from the source address to the destination address
|
||||
without assuming architecture dependent alignment of the addresses.</summary>
|
||||
<param name="destination">The destination address to copy to.</param>
|
||||
<param name="source">The source address to copy from.</param>
|
||||
<param name="byteCount">The number of bytes to copy.</param>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.InitBlock(System.Byte@,System.Byte,System.UInt32)">
|
||||
<summary>Initializes a block of memory at the given location with a given initial value.</summary>
|
||||
<param name="startAddress">The address of the start of the memory block to initialize.</param>
|
||||
<param name="value">The value to initialize the block to.</param>
|
||||
<param name="byteCount">The number of bytes to initialize.</param>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.InitBlock(System.Void*,System.Byte,System.UInt32)">
|
||||
<summary>Initializes a block of memory at the given location with a given initial value.</summary>
|
||||
<param name="startAddress">The address of the start of the memory block to initialize.</param>
|
||||
<param name="value">The value to initialize the block to.</param>
|
||||
<param name="byteCount">The number of bytes to initialize.</param>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.InitBlockUnaligned(System.Byte@,System.Byte,System.UInt32)">
|
||||
<summary>Initializes a block of memory at the given location with a given initial value
|
||||
without assuming architecture dependent alignment of the address.</summary>
|
||||
<param name="startAddress">The address of the start of the memory block to initialize.</param>
|
||||
<param name="value">The value to initialize the block to.</param>
|
||||
<param name="byteCount">The number of bytes to initialize.</param>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.InitBlockUnaligned(System.Void*,System.Byte,System.UInt32)">
|
||||
<summary>Initializes a block of memory at the given location with a given initial value
|
||||
without assuming architecture dependent alignment of the address.</summary>
|
||||
<param name="startAddress">The address of the start of the memory block to initialize.</param>
|
||||
<param name="value">The value to initialize the block to.</param>
|
||||
<param name="byteCount">The number of bytes to initialize.</param>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.Read``1(System.Void*)">
|
||||
<summary>Reads a value of type <typeparamref name="T">T</typeparamref> from the given location.</summary>
|
||||
<param name="source">The location to read from.</param>
|
||||
<typeparam name="T">The type to read.</typeparam>
|
||||
<returns>An object of type <typeparamref name="T">T</typeparamref> read from the given location.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.ReadUnaligned``1(System.Byte@)">
|
||||
<summary>Reads a value of type <typeparamref name="T">T</typeparamref> from the given location
|
||||
without assuming architecture dependent alignment of the addresses.</summary>
|
||||
<param name="source">The location to read from.</param>
|
||||
<typeparam name="T">The type to read.</typeparam>
|
||||
<returns>An object of type <typeparamref name="T">T</typeparamref> read from the given location.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.ReadUnaligned``1(System.Void*)">
|
||||
<summary>Reads a value of type <typeparamref name="T">T</typeparamref> from the given location
|
||||
without assuming architecture dependent alignment of the addresses.</summary>
|
||||
<param name="source">The location to read from.</param>
|
||||
<typeparam name="T">The type to read.</typeparam>
|
||||
<returns>An object of type <typeparamref name="T">T</typeparamref> read from the given location.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.SizeOf``1">
|
||||
<summary>Returns the size of an object of the given type parameter.</summary>
|
||||
<typeparam name="T">The type of object whose size is retrieved.</typeparam>
|
||||
<returns>The size of an object of type <typeparamref name="T">T</typeparamref>.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.Subtract``1(``0@,System.Int32)">
|
||||
<summary>Subtracts an element offset from the given reference.</summary>
|
||||
<param name="source">The reference to subtract the offset from.</param>
|
||||
<param name="elementOffset">The offset to subtract.</param>
|
||||
<typeparam name="T">The type of reference.</typeparam>
|
||||
<returns>A new reference that reflects the subraction of offset from pointer.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.Subtract``1(``0@,System.IntPtr)">
|
||||
<summary>Subtracts an element offset from the given reference.</summary>
|
||||
<param name="source">The reference to subtract the offset from.</param>
|
||||
<param name="elementOffset">The offset to subtract.</param>
|
||||
<typeparam name="T">The type of reference.</typeparam>
|
||||
<returns>A new reference that reflects the subraction of offset from pointer.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.SubtractByteOffset``1(``0@,System.IntPtr)">
|
||||
<summary>Subtracts a byte offset from the given reference.</summary>
|
||||
<param name="source">The reference to subtract the offset from.</param>
|
||||
<param name="byteOffset"></param>
|
||||
<typeparam name="T">The type of reference.</typeparam>
|
||||
<returns>A new reference that reflects the subraction of byte offset from pointer.</returns>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.Write``1(System.Void*,``0)">
|
||||
<summary>Writes a value of type <typeparamref name="T">T</typeparamref> to the given location.</summary>
|
||||
<param name="destination">The location to write to.</param>
|
||||
<param name="value">The value to write.</param>
|
||||
<typeparam name="T">The type of value to write.</typeparam>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.WriteUnaligned``1(System.Byte@,``0)">
|
||||
<summary>Writes a value of type <typeparamref name="T">T</typeparamref> to the given location
|
||||
without assuming architecture dependent alignment of the addresses.</summary>
|
||||
<param name="destination">The location to write to.</param>
|
||||
<param name="value">The value to write.</param>
|
||||
<typeparam name="T">The type of value to write.</typeparam>
|
||||
</member>
|
||||
<member name="M:System.Runtime.CompilerServices.Unsafe.WriteUnaligned``1(System.Void*,``0)">
|
||||
<summary>Writes a value of type <typeparamref name="T">T</typeparamref> to the given location
|
||||
without assuming architecture dependent alignment of the addresses.</summary>
|
||||
<param name="destination">The location to write to.</param>
|
||||
<param name="value">The value to write.</param>
|
||||
<typeparam name="T">The type of value to write.</typeparam>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b7641a560428bd45aa6bbd10d8bb4ab
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6dff8f4f5dd94df49bf6d616fb11cc9c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,71 @@
|
||||
using System.Buffers;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
/// <summary>
|
||||
/// 用于调试的KCP IO 类,没有Kcp功能
|
||||
/// </summary>
|
||||
public class FakeKcpIO : IKcpIO
|
||||
{
|
||||
QueuePipe<byte[]> recv = new QueuePipe<byte[]>();
|
||||
public int Input(ReadOnlySpan<byte> span)
|
||||
{
|
||||
byte[] buffer = new byte[span.Length];
|
||||
span.CopyTo(buffer);
|
||||
recv.Write(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int Input(ReadOnlySequence<byte> span)
|
||||
{
|
||||
byte[] buffer = new byte[span.Length];
|
||||
span.CopyTo(buffer);
|
||||
return Input(buffer);
|
||||
}
|
||||
|
||||
public async UniTask RecvAsync(IBufferWriter<byte> writer, object options = null)
|
||||
{
|
||||
var buffer = await recv.ReadAsync().ConfigureAwait(false);
|
||||
var target = writer.GetMemory(buffer.Length);
|
||||
buffer.AsSpan().CopyTo(target.Span);
|
||||
writer.Advance(buffer.Length);
|
||||
}
|
||||
|
||||
public async UniTask<int> RecvAsync(ArraySegment<byte> buffer, object options = null)
|
||||
{
|
||||
var temp = await recv.ReadAsync().ConfigureAwait(false);
|
||||
temp.AsSpan().CopyTo(buffer);
|
||||
return temp.Length;
|
||||
}
|
||||
|
||||
QueuePipe<byte[]> send = new QueuePipe<byte[]>();
|
||||
public int Send(ReadOnlySpan<byte> span, object options = null)
|
||||
{
|
||||
byte[] buffer = new byte[span.Length];
|
||||
span.CopyTo(buffer);
|
||||
send.Write(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int Send(ReadOnlySequence<byte> span, object options = null)
|
||||
{
|
||||
byte[] buffer = new byte[span.Length];
|
||||
span.CopyTo(buffer);
|
||||
return Send(buffer);
|
||||
}
|
||||
|
||||
public async UniTask OutputAsync(IBufferWriter<byte> writer, object options = null)
|
||||
{
|
||||
var buffer = await send.ReadAsync().ConfigureAwait(false);
|
||||
Write(writer, buffer);
|
||||
}
|
||||
|
||||
private static void Write(IBufferWriter<byte> writer, byte[] buffer)
|
||||
{
|
||||
var span = writer.GetSpan(buffer.Length);
|
||||
buffer.AsSpan().CopyTo(span);
|
||||
writer.Advance(buffer.Length);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3c2f9de3c34ffc409549b86ac3fc530
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,147 @@
|
||||
using BufferOwner = System.Buffers.IMemoryOwner<byte>;
|
||||
using System.Buffers;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
/// <summary>
|
||||
/// Kcp回调
|
||||
/// </summary>
|
||||
public interface IKcpCallback
|
||||
{
|
||||
/// <summary>
|
||||
/// kcp 发送方向输出
|
||||
/// </summary>
|
||||
/// <param name="buffer">kcp 交出发送缓冲区控制权,缓冲区来自<see cref="RentBuffer(int)"/></param>
|
||||
/// <param name="avalidLength">数据的有效长度</param>
|
||||
/// <returns>不需要返回值</returns>
|
||||
/// <remarks>通过增加 avalidLength 能够在协议栈中有效的减少数据拷贝</remarks>
|
||||
void Output(BufferOwner buffer, int avalidLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kcp回调
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 失败设计,<see cref="KcpOutputWriter.Output(BufferOwner, int)"/>。IMemoryOwner是没有办法代替的。
|
||||
/// 这里只相当于把 IKcpCallback 和 IRentable 和并。
|
||||
/// </remarks>
|
||||
public interface IKcpOutputWriter : IBufferWriter<byte>
|
||||
{
|
||||
int UnflushedBytes { get; }
|
||||
void Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 外部提供缓冲区,可以在外部链接一个内存池
|
||||
/// </summary>
|
||||
public interface IRentable
|
||||
{
|
||||
/// <summary>
|
||||
/// 外部提供缓冲区,可以在外部链接一个内存池
|
||||
/// </summary>
|
||||
BufferOwner RentBuffer(int length);
|
||||
}
|
||||
|
||||
public interface IKcpSetting
|
||||
{
|
||||
int Interval(int interval);
|
||||
/// <summary>
|
||||
/// fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
|
||||
/// </summary>
|
||||
/// <param name="nodelay">0:disable(default), 1:enable</param>
|
||||
/// <param name="interval">internal update timer interval in millisec, default is 100ms</param>
|
||||
/// <param name="resend">0:disable fast resend(default), 1:enable fast resend</param>
|
||||
/// <param name="nc">0:normal congestion control(default), 1:disable congestion control</param>
|
||||
/// <returns></returns>
|
||||
int NoDelay(int nodelay, int interval, int resend, int nc);
|
||||
/// <summary>
|
||||
/// change MTU size, default is 1400
|
||||
/// <para>** 这个方法不是线程安全的。请在没有发送和接收时调用 。</para>
|
||||
/// </summary>
|
||||
/// <param name="mtu"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// 如果没有必要,不要修改Mtu。过小的Mtu会导致分片数大于接收窗口,造成kcp阻塞冻结。
|
||||
/// </remarks>
|
||||
int SetMtu(int mtu = 1400);
|
||||
/// <summary>
|
||||
/// set maximum window size: sndwnd=32, rcvwnd=128 by default
|
||||
/// </summary>
|
||||
/// <param name="sndwnd"></param>
|
||||
/// <param name="rcvwnd"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// 如果没有必要请不要修改。注意确保接收窗口必须大于最大分片数。
|
||||
/// </remarks>
|
||||
int WndSize(int sndwnd = 32, int rcvwnd = 128);
|
||||
}
|
||||
|
||||
public interface IKcpUpdate
|
||||
{
|
||||
void Update(in DateTimeOffset time);
|
||||
}
|
||||
|
||||
public interface IKcpSendable
|
||||
{
|
||||
/// <summary>
|
||||
/// 将要发送到网络的数据Send到kcp协议中
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <param name="options"></param>
|
||||
int Send(ReadOnlySpan<byte> span, object options = null);
|
||||
/// <summary>
|
||||
/// 将要发送到网络的数据Send到kcp协议中
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <param name="options"></param>
|
||||
int Send(ReadOnlySequence<byte> span, object options = null);
|
||||
}
|
||||
|
||||
public interface IKcpInputable
|
||||
{
|
||||
/// <summary>
|
||||
/// 下层收到数据后添加到kcp协议中
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
int Input(ReadOnlySpan<byte> span);
|
||||
/// <summary>
|
||||
/// 下层收到数据后添加到kcp协议中
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
int Input(ReadOnlySequence<byte> span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// kcp协议输入输出标准接口
|
||||
/// </summary>
|
||||
public interface IKcpIO : IKcpSendable, IKcpInputable
|
||||
{
|
||||
/// <summary>
|
||||
/// 从kcp中取出一个整合完毕的数据包
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
UniTask RecvAsync(IBufferWriter<byte> writer, object options = null);
|
||||
|
||||
/// <summary>
|
||||
/// 从kcp中取出一个整合完毕的数据包
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns>接收数据长度</returns>
|
||||
UniTask<int> RecvAsync(ArraySegment<byte> buffer, object options = null);
|
||||
|
||||
/// <summary>
|
||||
/// 从kcp协议中取出需要发送到网络的数据。
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
UniTask OutputAsync(IBufferWriter<byte> writer, object options = null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0ca98e42147b3948bc8d30f24e9c366
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,88 @@
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
/// <summary>
|
||||
/// Kcp报头
|
||||
/// https://zhuanlan.zhihu.com/p/559191428
|
||||
/// </summary>
|
||||
public interface IKcpHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// 会话编号,两方一致才会通信
|
||||
/// </summary>
|
||||
uint conv { get; set; }
|
||||
/// <summary>
|
||||
/// 指令类型
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para/> IKCP_CMD_PUSH = 81 // cmd: push data 数据报文
|
||||
/// <para/> IKCP_CMD_ACK = 82 // cmd: ack 确认报文
|
||||
/// <para/> IKCP_CMD_WASK = 83 // cmd: window probe (ask) 窗口探测报文,询问对端剩余接收窗口的大小.
|
||||
/// <para/> IKCP_CMD_WINS = 84 // cmd: window size (tell) 窗口通知报文,通知对端剩余接收窗口的大小.
|
||||
/// </remarks>
|
||||
byte cmd { get; set; }
|
||||
/// <summary>
|
||||
/// 剩余分片数量,表示随后还有多少个报文属于同一个包。
|
||||
/// </summary>
|
||||
byte frg { get; set; }
|
||||
/// <summary>
|
||||
/// 自己可用窗口大小
|
||||
/// </summary>
|
||||
ushort wnd { get; set; }
|
||||
/// <summary>
|
||||
/// 发送时的时间戳 <seealso cref="DateTimeOffset.ToUnixTimeMilliseconds"/>
|
||||
/// </summary>
|
||||
uint ts { get; set; }
|
||||
/// <summary>
|
||||
/// 编号 确认编号或者报文编号
|
||||
/// </summary>
|
||||
uint sn { get; set; }
|
||||
/// <summary>
|
||||
/// 代表编号前面的所有报都收到了的标志
|
||||
/// </summary>
|
||||
uint una { get; set; }
|
||||
/// <summary>
|
||||
/// 数据内容长度
|
||||
/// </summary>
|
||||
uint len { get; }
|
||||
}
|
||||
public interface IKcpSegment : IKcpHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// 重传的时间戳。超过当前时间重发这个包
|
||||
/// </summary>
|
||||
uint resendts { get; set; }
|
||||
/// <summary>
|
||||
/// 超时重传时间,根据网络去定
|
||||
/// </summary>
|
||||
uint rto { get; set; }
|
||||
/// <summary>
|
||||
/// 快速重传机制,记录被跳过的次数,超过次数进行快速重传
|
||||
/// </summary>
|
||||
uint fastack { get; set; }
|
||||
/// <summary>
|
||||
/// 重传次数
|
||||
/// </summary>
|
||||
uint xmit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据内容
|
||||
/// </summary>
|
||||
Span<byte> data { get; }
|
||||
/// <summary>
|
||||
/// 将IKcpSegment编码成字节数组,并返回总长度(包括Kcp报头)
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <returns></returns>
|
||||
int Encode(Span<byte> buffer);
|
||||
}
|
||||
|
||||
public interface ISegmentManager<Segment> where Segment : IKcpSegment
|
||||
{
|
||||
Segment Alloc(int appendDateSize);
|
||||
void Free(Segment seg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9259bb6b548e46459cc766d8fe2faeb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
387
Assets/TEngine/Runtime/GameFramework/Network/Kcp/Core/Kcp.cs
Normal file
387
Assets/TEngine/Runtime/GameFramework/Network/Kcp/Core/Kcp.cs
Normal file
@@ -0,0 +1,387 @@
|
||||
using System.Buffers;
|
||||
using BufferOwner = System.Buffers.IMemoryOwner<byte>;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
public class Kcp<Segment> : KcpCore<Segment>
|
||||
where Segment : IKcpSegment
|
||||
{
|
||||
/// <summary>
|
||||
/// create a new kcp control object, 'conv' must equal in two endpoint
|
||||
/// from the same connection.
|
||||
/// </summary>
|
||||
/// <param name="conv_"></param>
|
||||
/// <param name="callback"></param>
|
||||
/// <param name="rentable">可租用内存的回调</param>
|
||||
public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null)
|
||||
: base(conv_)
|
||||
{
|
||||
callbackHandle = callback;
|
||||
this.rentable = rentable;
|
||||
}
|
||||
|
||||
|
||||
//extension 重构和新增加的部分============================================
|
||||
|
||||
IRentable rentable;
|
||||
/// <summary>
|
||||
/// 如果外部能够提供缓冲区则使用外部缓冲区,否则new byte[]
|
||||
/// </summary>
|
||||
/// <param name="needSize"></param>
|
||||
/// <returns></returns>
|
||||
internal protected override BufferOwner CreateBuffer(int needSize)
|
||||
{
|
||||
var res = rentable?.RentBuffer(needSize);
|
||||
if (res == null)
|
||||
{
|
||||
return base.CreateBuffer(needSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (res.Memory.Length < needSize)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(rentable.RentBuffer)} 指定的委托不符合标准,返回的" +
|
||||
$"BufferOwner.Memory.Length 小于 {nameof(needSize)}");
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TryRecv Recv设计上同一时刻只允许一个线程调用。
|
||||
/// <para/>因为要保证数据顺序,多个线程同时调用Recv也没有意义。
|
||||
/// <para/>所以只需要部分加锁即可。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public (BufferOwner buffer, int avalidLength) TryRecv()
|
||||
{
|
||||
var peekSize = -1;
|
||||
lock (rcv_queueLock)
|
||||
{
|
||||
if (rcv_queue.Count == 0)
|
||||
{
|
||||
///没有可用包
|
||||
return (null, -1);
|
||||
}
|
||||
|
||||
var seq = rcv_queue[0];
|
||||
|
||||
if (seq.frg == 0)
|
||||
{
|
||||
peekSize = (int)seq.len;
|
||||
}
|
||||
|
||||
if (rcv_queue.Count < seq.frg + 1)
|
||||
{
|
||||
///没有足够的包
|
||||
return (null, -1);
|
||||
}
|
||||
|
||||
uint length = 0;
|
||||
|
||||
foreach (var item in rcv_queue)
|
||||
{
|
||||
length += item.len;
|
||||
if (item.frg == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
peekSize = (int)length;
|
||||
|
||||
if (peekSize <= 0)
|
||||
{
|
||||
return (null, -2);
|
||||
}
|
||||
}
|
||||
|
||||
var buffer = CreateBuffer(peekSize);
|
||||
var recvlength = UncheckRecv(buffer.Memory.Span);
|
||||
return (buffer, recvlength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TryRecv Recv设计上同一时刻只允许一个线程调用。
|
||||
/// <para/>因为要保证数据顺序,多个线程同时调用Recv也没有意义。
|
||||
/// <para/>所以只需要部分加锁即可。
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <returns></returns>
|
||||
public int TryRecv(IBufferWriter<byte> writer)
|
||||
{
|
||||
var peekSize = -1;
|
||||
lock (rcv_queueLock)
|
||||
{
|
||||
if (rcv_queue.Count == 0)
|
||||
{
|
||||
///没有可用包
|
||||
return -1;
|
||||
}
|
||||
|
||||
var seq = rcv_queue[0];
|
||||
|
||||
if (seq.frg == 0)
|
||||
{
|
||||
peekSize = (int)seq.len;
|
||||
}
|
||||
|
||||
if (rcv_queue.Count < seq.frg + 1)
|
||||
{
|
||||
///没有足够的包
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint length = 0;
|
||||
|
||||
foreach (var item in rcv_queue)
|
||||
{
|
||||
length += item.len;
|
||||
if (item.frg == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
peekSize = (int)length;
|
||||
|
||||
if (peekSize <= 0)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
return UncheckRecv(writer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// user/upper level recv: returns size, returns below zero for EAGAIN
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <returns></returns>
|
||||
public int Recv(Span<byte> buffer)
|
||||
{
|
||||
if (0 == rcv_queue.Count)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var peekSize = PeekSize();
|
||||
if (peekSize < 0)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (peekSize > buffer.Length)
|
||||
{
|
||||
return -3;
|
||||
}
|
||||
|
||||
/// 拆分函数
|
||||
var recvLength = UncheckRecv(buffer);
|
||||
|
||||
return recvLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// user/upper level recv: returns size, returns below zero for EAGAIN
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <returns></returns>
|
||||
public int Recv(IBufferWriter<byte> writer)
|
||||
{
|
||||
if (0 == rcv_queue.Count)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var peekSize = PeekSize();
|
||||
if (peekSize < 0)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
//if (peekSize > buffer.Length)
|
||||
//{
|
||||
// return -3;
|
||||
//}
|
||||
|
||||
/// 拆分函数
|
||||
var recvLength = UncheckRecv(writer);
|
||||
|
||||
return recvLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 这个函数不检查任何参数
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <returns></returns>
|
||||
int UncheckRecv(Span<byte> buffer)
|
||||
{
|
||||
var recover = false;
|
||||
if (rcv_queue.Count >= rcv_wnd)
|
||||
{
|
||||
recover = true;
|
||||
}
|
||||
|
||||
#region merge fragment.
|
||||
/// merge fragment.
|
||||
|
||||
var recvLength = 0;
|
||||
lock (rcv_queueLock)
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var seg in rcv_queue)
|
||||
{
|
||||
seg.data.CopyTo(buffer.Slice(recvLength));
|
||||
recvLength += (int)seg.len;
|
||||
|
||||
count++;
|
||||
int frg = seg.frg;
|
||||
|
||||
SegmentManager.Free(seg);
|
||||
if (frg == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
rcv_queue.RemoveRange(0, count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Move_Rcv_buf_2_Rcv_queue();
|
||||
|
||||
#region fast recover
|
||||
/// fast recover
|
||||
if (rcv_queue.Count < rcv_wnd && recover)
|
||||
{
|
||||
// ready to send back IKCP_CMD_WINS in ikcp_flush
|
||||
// tell remote my window size
|
||||
probe |= IKCP_ASK_TELL;
|
||||
}
|
||||
#endregion
|
||||
return recvLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 这个函数不检查任何参数
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <returns></returns>
|
||||
int UncheckRecv(IBufferWriter<byte> writer)
|
||||
{
|
||||
var recover = false;
|
||||
if (rcv_queue.Count >= rcv_wnd)
|
||||
{
|
||||
recover = true;
|
||||
}
|
||||
|
||||
#region merge fragment.
|
||||
/// merge fragment.
|
||||
|
||||
var recvLength = 0;
|
||||
lock (rcv_queueLock)
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var seg in rcv_queue)
|
||||
{
|
||||
var len = (int)seg.len;
|
||||
var destination = writer.GetSpan(len);
|
||||
|
||||
seg.data.CopyTo(destination);
|
||||
writer.Advance(len);
|
||||
|
||||
recvLength += len;
|
||||
|
||||
count++;
|
||||
int frg = seg.frg;
|
||||
|
||||
SegmentManager.Free(seg);
|
||||
if (frg == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
rcv_queue.RemoveRange(0, count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Move_Rcv_buf_2_Rcv_queue();
|
||||
|
||||
#region fast recover
|
||||
/// fast recover
|
||||
if (rcv_queue.Count < rcv_wnd && recover)
|
||||
{
|
||||
// ready to send back IKCP_CMD_WINS in ikcp_flush
|
||||
// tell remote my window size
|
||||
probe |= IKCP_ASK_TELL;
|
||||
}
|
||||
#endregion
|
||||
return recvLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// check the size of next message in the recv queue
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int PeekSize()
|
||||
{
|
||||
lock (rcv_queueLock)
|
||||
{
|
||||
if (rcv_queue.Count == 0)
|
||||
{
|
||||
///没有可用包
|
||||
return -1;
|
||||
}
|
||||
|
||||
var seq = rcv_queue[0];
|
||||
|
||||
if (seq.frg == 0)
|
||||
{
|
||||
return (int)seq.len;
|
||||
}
|
||||
|
||||
if (rcv_queue.Count < seq.frg + 1)
|
||||
{
|
||||
///没有足够的包
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint length = 0;
|
||||
|
||||
foreach (var seg in rcv_queue)
|
||||
{
|
||||
length += seg.len;
|
||||
if (seg.frg == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (int)length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3840e90e37f0194bbfafb564ff80d45
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
2207
Assets/TEngine/Runtime/GameFramework/Network/Kcp/Core/KcpCore.cs
Normal file
2207
Assets/TEngine/Runtime/GameFramework/Network/Kcp/Core/KcpCore.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fcc0172c723e55a4389ad2f62b68a3fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
262
Assets/TEngine/Runtime/GameFramework/Network/Kcp/Core/KcpIO.cs
Normal file
262
Assets/TEngine/Runtime/GameFramework/Network/Kcp/Core/KcpIO.cs
Normal file
@@ -0,0 +1,262 @@
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using BufferOwner = System.Buffers.IMemoryOwner<byte>;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="IPipe{T}"/>
|
||||
/// <para></para>这是个简单的实现,更复杂使用微软官方实现<see cref="Channel.CreateBounded{T}(int)"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class QueuePipe<T> : Queue<T>
|
||||
{
|
||||
readonly object _innerLock = new object();
|
||||
private TaskCompletionSource<T> source;
|
||||
|
||||
//线程同步上下文由Task机制保证,无需额外处理
|
||||
//SynchronizationContext callbackContext;
|
||||
//public bool UseSynchronizationContext { get; set; } = true;
|
||||
|
||||
public virtual void Write(T item)
|
||||
{
|
||||
lock (_innerLock)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
Enqueue(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Count > 0)
|
||||
{
|
||||
throw new Exception("内部顺序错误,不应该出现,请联系作者");
|
||||
}
|
||||
|
||||
var next = source;
|
||||
source = null;
|
||||
next.TrySetResult(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new void Enqueue(T item)
|
||||
{
|
||||
lock (_innerLock)
|
||||
{
|
||||
base.Enqueue(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
lock (_innerLock)
|
||||
{
|
||||
if (Count > 0)
|
||||
{
|
||||
var res = Dequeue();
|
||||
var next = source;
|
||||
source = null;
|
||||
next?.TrySetResult(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Task<T> ReadAsync()
|
||||
{
|
||||
lock (_innerLock)
|
||||
{
|
||||
if (this.Count > 0)
|
||||
{
|
||||
var next = Dequeue();
|
||||
return Task.FromResult(next);
|
||||
}
|
||||
else
|
||||
{
|
||||
source = new TaskCompletionSource<T>();
|
||||
return source.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UniTask<T> ReadValueTaskAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class KcpIO<Segment> : KcpCore<Segment>, IKcpIO
|
||||
where Segment : IKcpSegment
|
||||
{
|
||||
OutputQ outq;
|
||||
|
||||
public KcpIO(uint conv_) : base(conv_)
|
||||
{
|
||||
outq = new OutputQ();
|
||||
callbackHandle = outq;
|
||||
}
|
||||
|
||||
internal override void Parse_data(Segment newseg)
|
||||
{
|
||||
base.Parse_data(newseg);
|
||||
|
||||
lock (rcv_queueLock)
|
||||
{
|
||||
var recover = false;
|
||||
if (rcv_queue.Count >= rcv_wnd)
|
||||
{
|
||||
recover = true;
|
||||
}
|
||||
|
||||
while (TryRecv(out var arraySegment) > 0)
|
||||
{
|
||||
recvSignal.Enqueue(arraySegment);
|
||||
}
|
||||
|
||||
recvSignal.Flush();
|
||||
|
||||
#region fast recover
|
||||
|
||||
/// fast recover
|
||||
if (rcv_queue.Count < rcv_wnd && recover)
|
||||
{
|
||||
// ready to send back IKCP_CMD_WINS in ikcp_flush
|
||||
// tell remote my window size
|
||||
probe |= IKCP_ASK_TELL;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
QueuePipe<ArraySegment<Segment>> recvSignal = new QueuePipe<ArraySegment<Segment>>();
|
||||
|
||||
internal int TryRecv(out ArraySegment<Segment> package)
|
||||
{
|
||||
package = default;
|
||||
lock (rcv_queueLock)
|
||||
{
|
||||
var peekSize = -1;
|
||||
if (rcv_queue.Count == 0)
|
||||
{
|
||||
///没有可用包
|
||||
return -1;
|
||||
}
|
||||
|
||||
var seq = rcv_queue[0];
|
||||
|
||||
if (seq.frg == 0)
|
||||
{
|
||||
peekSize = (int)seq.len;
|
||||
}
|
||||
|
||||
if (rcv_queue.Count < seq.frg + 1)
|
||||
{
|
||||
///没有足够的包
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint length = 0;
|
||||
|
||||
Segment[] kcpSegments = ArrayPool<Segment>.Shared.Rent(seq.frg + 1);
|
||||
|
||||
var index = 0;
|
||||
foreach (var item in rcv_queue)
|
||||
{
|
||||
kcpSegments[index] = item;
|
||||
index++;
|
||||
length += item.len;
|
||||
if (item.frg == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index > 0)
|
||||
{
|
||||
rcv_queue.RemoveRange(0, index);
|
||||
}
|
||||
|
||||
package = new ArraySegment<Segment>(kcpSegments, 0, index);
|
||||
|
||||
peekSize = (int)length;
|
||||
|
||||
if (peekSize <= 0)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
return peekSize;
|
||||
}
|
||||
}
|
||||
|
||||
public async UniTask RecvAsync(IBufferWriter<byte> writer, object options = null)
|
||||
{
|
||||
var arraySegment = await recvSignal.ReadAsync().ConfigureAwait(false);
|
||||
for (int i = arraySegment.Offset; i < arraySegment.Count; i++)
|
||||
{
|
||||
WriteRecv(writer, arraySegment.Array[i]);
|
||||
}
|
||||
|
||||
ArrayPool<Segment>.Shared.Return(arraySegment.Array, true);
|
||||
}
|
||||
|
||||
private void WriteRecv(IBufferWriter<byte> writer, Segment seg)
|
||||
{
|
||||
var curCount = (int)seg.len;
|
||||
var target = writer.GetSpan(curCount);
|
||||
seg.data.CopyTo(target);
|
||||
SegmentManager.Free(seg);
|
||||
writer.Advance(curCount);
|
||||
}
|
||||
|
||||
public async UniTask<int> RecvAsync(ArraySegment<byte> buffer, object options = null)
|
||||
{
|
||||
var arraySegment = await recvSignal.ReadAsync().ConfigureAwait(false);
|
||||
int start = buffer.Offset;
|
||||
for (int i = arraySegment.Offset; i < arraySegment.Count; i++)
|
||||
{
|
||||
var target = new Memory<byte>(buffer.Array, start, buffer.Array.Length - start);
|
||||
|
||||
var seg = arraySegment.Array[i];
|
||||
seg.data.CopyTo(target.Span);
|
||||
start += seg.data.Length;
|
||||
|
||||
SegmentManager.Free(seg);
|
||||
}
|
||||
|
||||
ArrayPool<Segment>.Shared.Return(arraySegment.Array, true);
|
||||
return start - buffer.Offset;
|
||||
}
|
||||
|
||||
public async UniTask OutputAsync(IBufferWriter<byte> writer, object options = null)
|
||||
{
|
||||
var (Owner, Count) = await outq.ReadAsync().ConfigureAwait(false);
|
||||
WriteOut(writer, Owner, Count);
|
||||
}
|
||||
|
||||
private static void WriteOut(IBufferWriter<byte> writer, BufferOwner Owner, int Count)
|
||||
{
|
||||
var target = writer.GetSpan(Count);
|
||||
Owner.Memory.Span.Slice(0, Count).CopyTo(target);
|
||||
writer.Advance(Count);
|
||||
Owner.Dispose();
|
||||
}
|
||||
|
||||
protected internal override BufferOwner CreateBuffer(int needSize)
|
||||
{
|
||||
return MemoryPool<byte>.Shared.Rent(needSize);
|
||||
}
|
||||
|
||||
internal class OutputQ : QueuePipe<(BufferOwner Owner, int Count)>,
|
||||
IKcpCallback
|
||||
{
|
||||
public void Output(BufferOwner buffer, int avalidLength)
|
||||
{
|
||||
Write((buffer, avalidLength));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c30d30fa46372948b60bd56eec47fe6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,50 @@
|
||||
using System.Buffers;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
public abstract class KcpOutputWriter : IKcpOutputWriter
|
||||
{
|
||||
public int UnflushedBytes { get; set; }
|
||||
public IMemoryOwner<byte> MemoryOwner { get; set; }
|
||||
public void Flush()
|
||||
{
|
||||
Output(MemoryOwner, UnflushedBytes);
|
||||
MemoryOwner = null;
|
||||
UnflushedBytes = 0;
|
||||
}
|
||||
|
||||
public void Advance(int count)
|
||||
{
|
||||
UnflushedBytes += count;
|
||||
}
|
||||
|
||||
public Memory<byte> GetMemory(int sizeHint = 0)
|
||||
{
|
||||
if (MemoryOwner == null)
|
||||
{
|
||||
MemoryOwner = MemoryPool<byte>.Shared.Rent(2048);
|
||||
}
|
||||
return MemoryOwner.Memory.Slice(UnflushedBytes);
|
||||
}
|
||||
|
||||
public Span<byte> GetSpan(int sizeHint = 0)
|
||||
{
|
||||
if (MemoryOwner == null)
|
||||
{
|
||||
MemoryOwner = MemoryPool<byte>.Shared.Rent(2048);
|
||||
}
|
||||
return MemoryOwner.Memory.Span.Slice(UnflushedBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Socket发送是要pin byte[],为了不阻塞KcpFlush,动态缓存是必须的。
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="avalidLength"></param>
|
||||
public abstract void Output(IMemoryOwner<byte> buffer, int avalidLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a058d8fa8a92dc94395bfd6e1f6fdb8f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,402 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
/// <summary>
|
||||
/// 调整了没存布局,直接拷贝块提升性能。
|
||||
/// <para>结构体保存内容只有一个指针,不用担心参数传递过程中的性能</para>
|
||||
/// https://github.com/skywind3000/kcp/issues/118#issuecomment-338133930
|
||||
/// <para>不要对没有初始化的KcpSegment(内部指针为0,所有属性都将指向位置区域) 进行任何赋值操作,可能导致内存损坏。
|
||||
/// 出于性能考虑,没有对此项进行安全检查。</para>
|
||||
/// </summary>
|
||||
public struct KcpSegment : IKcpSegment
|
||||
{
|
||||
internal readonly unsafe byte* ptr;
|
||||
public unsafe KcpSegment(byte* intPtr, uint appendDateSize)
|
||||
{
|
||||
this.ptr = intPtr;
|
||||
len = appendDateSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用完必须显示释放,否则内存泄漏
|
||||
/// </summary>
|
||||
/// <param name="appendDateSize"></param>
|
||||
/// <returns></returns>
|
||||
public static KcpSegment AllocHGlobal(int appendDateSize)
|
||||
{
|
||||
var total = LocalOffset + HeadOffset + appendDateSize;
|
||||
IntPtr intPtr = Marshal.AllocHGlobal(total);
|
||||
unsafe
|
||||
{
|
||||
///清零 不知道是不是有更快的清0方法?
|
||||
Span<byte> span = new Span<byte>(intPtr.ToPointer(), total);
|
||||
span.Clear();
|
||||
|
||||
return new KcpSegment((byte*)intPtr.ToPointer(), (uint)appendDateSize);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放非托管内存
|
||||
/// </summary>
|
||||
/// <param name="seg"></param>
|
||||
public static void FreeHGlobal(KcpSegment seg)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
Marshal.FreeHGlobal((IntPtr)seg.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/// 以下为本机使用的参数
|
||||
/// <summary>
|
||||
/// offset = 0
|
||||
/// </summary>
|
||||
public uint resendts
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(ptr + 0);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(ptr + 0) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = 4
|
||||
/// </summary>
|
||||
public uint rto
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(ptr + 4);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(ptr + 4) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = 8
|
||||
/// </summary>
|
||||
public uint fastack
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(ptr + 8);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(ptr + 8) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = 12
|
||||
/// </summary>
|
||||
public uint xmit
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(ptr + 12);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(ptr + 12) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///以下为需要网络传输的参数
|
||||
public const int LocalOffset = 4 * 4;
|
||||
public const int HeadOffset = KcpConst.IKCP_OVERHEAD;
|
||||
|
||||
/// <summary>
|
||||
/// offset = <see cref="LocalOffset"/>
|
||||
/// </summary>
|
||||
/// https://github.com/skywind3000/kcp/issues/134
|
||||
public uint conv
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(LocalOffset + 0 + ptr);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(LocalOffset + 0 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = <see cref="LocalOffset"/> + 4
|
||||
/// </summary>
|
||||
public byte cmd
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(LocalOffset + 4 + ptr);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(LocalOffset + 4 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = <see cref="LocalOffset"/> + 5
|
||||
/// </summary>
|
||||
public byte frg
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(LocalOffset + 5 + ptr);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(LocalOffset + 5 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = <see cref="LocalOffset"/> + 6
|
||||
/// </summary>
|
||||
public ushort wnd
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(ushort*)(LocalOffset + 6 + ptr);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(ushort*)(LocalOffset + 6 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = <see cref="LocalOffset"/> + 8
|
||||
/// </summary>
|
||||
public uint ts
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(LocalOffset + 8 + ptr);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(LocalOffset + 8 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para> SendNumber? </para>
|
||||
/// offset = <see cref="LocalOffset"/> + 12
|
||||
/// </summary>
|
||||
public uint sn
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(LocalOffset + 12 + ptr);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(LocalOffset + 12 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = <see cref="LocalOffset"/> + 16
|
||||
/// </summary>
|
||||
public uint una
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(LocalOffset + 16 + ptr);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(LocalOffset + 16 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para> AppendDateSize </para>
|
||||
/// offset = <see cref="LocalOffset"/> + 20
|
||||
/// </summary>
|
||||
public uint len
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(LocalOffset + 20 + ptr);
|
||||
}
|
||||
}
|
||||
private set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(LocalOffset + 20 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// https://github.com/skywind3000/kcp/issues/35#issuecomment-263770736
|
||||
public Span<byte> data
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Span<byte>(LocalOffset + HeadOffset + ptr, (int)len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 将片段中的要发送的数据拷贝到指定缓冲区
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <returns></returns>
|
||||
public int Encode(Span<byte> buffer)
|
||||
{
|
||||
var datelen = (int)(HeadOffset + len);
|
||||
|
||||
///备用偏移值 现阶段没有使用
|
||||
const int offset = 0;
|
||||
|
||||
if (KcpConst.IsLittleEndian)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
///小端可以一次拷贝
|
||||
unsafe
|
||||
{
|
||||
///要发送的数据从LocalOffset开始。
|
||||
///本结构体调整了要发送字段和单机使用字段的位置,让报头数据和数据连续,节约一次拷贝。
|
||||
Span<byte> sendDate = new Span<byte>(ptr + LocalOffset, datelen);
|
||||
sendDate.CopyTo(buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset), conv);
|
||||
buffer[offset + 4] = cmd;
|
||||
buffer[offset + 5] = frg;
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(offset + 6), wnd);
|
||||
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 8), ts);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 12), sn);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 16), una);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 20), len);
|
||||
|
||||
data.CopyTo(buffer.Slice(HeadOffset));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset), conv);
|
||||
buffer[offset + 4] = cmd;
|
||||
buffer[offset + 5] = frg;
|
||||
BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(offset + 6), wnd);
|
||||
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 8), ts);
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 12), sn);
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 16), una);
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 20), len);
|
||||
|
||||
data.CopyTo(buffer.Slice(HeadOffset));
|
||||
}
|
||||
else
|
||||
{
|
||||
///大端可以一次拷贝
|
||||
unsafe
|
||||
{
|
||||
///要发送的数据从LocalOffset开始。
|
||||
///本结构体调整了要发送字段和单机使用字段的位置,让报头数据和数据连续,节约一次拷贝。
|
||||
Span<byte> sendDate = new Span<byte>(ptr + LocalOffset, datelen);
|
||||
sendDate.CopyTo(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return datelen;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: daab08fc1f00355429c23a1e36993942
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
public partial class KcpCore<Segment>
|
||||
{
|
||||
public KcpLogMask LogMask { get; set; } = KcpLogMask.IKCP_LOG_PARSE_DATA | KcpLogMask.IKCP_LOG_NEED_SEND | KcpLogMask.IKCP_LOG_DEAD_LINK;
|
||||
|
||||
public virtual bool CanLog(KcpLogMask mask)
|
||||
{
|
||||
if ((mask & LogMask) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
||||
if (TraceListener != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
||||
public System.Diagnostics.TraceListener TraceListener { get; set; }
|
||||
#endif
|
||||
|
||||
public virtual void LogFail(string message)
|
||||
{
|
||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
||||
TraceListener?.Fail(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
public virtual void LogWriteLine(string message, string category)
|
||||
{
|
||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
||||
TraceListener?.WriteLine(message, category);
|
||||
#endif
|
||||
}
|
||||
|
||||
[Obsolete("一定要先判断CanLog 内部判断是否存在TraceListener,避免在没有TraceListener时生成字符串", true)]
|
||||
public virtual void LogWriteLine(string message, KcpLogMask mask)
|
||||
{
|
||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
||||
if (CanLog(mask))
|
||||
{
|
||||
LogWriteLine(message, mask.ToString());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum KcpLogMask
|
||||
{
|
||||
IKCP_LOG_OUTPUT = 1 << 0,
|
||||
IKCP_LOG_INPUT = 1 << 1,
|
||||
IKCP_LOG_SEND = 1 << 2,
|
||||
IKCP_LOG_RECV = 1 << 3,
|
||||
IKCP_LOG_IN_DATA = 1 << 4,
|
||||
IKCP_LOG_IN_ACK = 1 << 5,
|
||||
IKCP_LOG_IN_PROBE = 1 << 6,
|
||||
IKCP_LOG_IN_WINS = 1 << 7,
|
||||
IKCP_LOG_OUT_DATA = 1 << 8,
|
||||
IKCP_LOG_OUT_ACK = 1 << 9,
|
||||
IKCP_LOG_OUT_PROBE = 1 << 10,
|
||||
IKCP_LOG_OUT_WINS = 1 << 11,
|
||||
|
||||
IKCP_LOG_PARSE_DATA = 1 << 12,
|
||||
IKCP_LOG_NEED_SEND = 1 << 13,
|
||||
IKCP_LOG_DEAD_LINK = 1 << 14,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39791b0c45bf4864e87385c477d9b50c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,265 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
/// <summary>
|
||||
/// 动态申请非托管内存
|
||||
/// </summary>
|
||||
public class SimpleSegManager : ISegmentManager<KcpSegment>
|
||||
{
|
||||
public static SimpleSegManager Default { get; } = new SimpleSegManager();
|
||||
public KcpSegment Alloc(int appendDateSize)
|
||||
{
|
||||
return KcpSegment.AllocHGlobal(appendDateSize);
|
||||
}
|
||||
|
||||
public void Free(KcpSegment seg)
|
||||
{
|
||||
KcpSegment.FreeHGlobal(seg);
|
||||
}
|
||||
|
||||
public class Kcp : Kcp<KcpSegment>
|
||||
{
|
||||
public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null)
|
||||
: base(conv_, callback, rentable)
|
||||
{
|
||||
SegmentManager = Default;
|
||||
}
|
||||
}
|
||||
|
||||
public class KcpIO : KcpIO<KcpSegment>
|
||||
{
|
||||
public KcpIO(uint conv_)
|
||||
: base(conv_)
|
||||
{
|
||||
SegmentManager = Default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 申请固定大小非托管内存。使用这个就不能SetMtu了,大小已经写死。
|
||||
/// </summary>
|
||||
/// <remarks>需要大量测试</remarks>
|
||||
public unsafe class UnSafeSegManager : ISegmentManager<KcpSegment>
|
||||
{
|
||||
public static UnSafeSegManager Default { get; } = new UnSafeSegManager();
|
||||
/// <summary>
|
||||
/// 因为默认mtu是1400,并且内存需要内存行/内存页对齐。这里直接512对齐。
|
||||
/// </summary>
|
||||
public const int blockSize = 512 * 3;
|
||||
public HashSet<IntPtr> header = new HashSet<IntPtr>();
|
||||
public Stack<IntPtr> blocks = new Stack<IntPtr>();
|
||||
public readonly object locker = new object();
|
||||
public UnSafeSegManager()
|
||||
{
|
||||
Alloc();
|
||||
}
|
||||
|
||||
void Alloc()
|
||||
{
|
||||
int count = 50;
|
||||
IntPtr intPtr = Marshal.AllocHGlobal(blockSize * count);
|
||||
header.Add(intPtr);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
blocks.Push(intPtr + blockSize * i);
|
||||
}
|
||||
}
|
||||
|
||||
~UnSafeSegManager()
|
||||
{
|
||||
foreach (var item in header)
|
||||
{
|
||||
Marshal.FreeHGlobal(item);
|
||||
}
|
||||
}
|
||||
|
||||
public KcpSegment Alloc(int appendDateSize)
|
||||
{
|
||||
lock (locker)
|
||||
{
|
||||
var total = KcpSegment.LocalOffset + KcpSegment.HeadOffset + appendDateSize;
|
||||
if (total > blockSize)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (blocks.Count > 0)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Alloc();
|
||||
}
|
||||
|
||||
var ptr = blocks.Pop();
|
||||
Span<byte> span = new Span<byte>(ptr.ToPointer(), blockSize);
|
||||
span.Clear();
|
||||
return new KcpSegment((byte*)ptr.ToPointer(), (uint)appendDateSize);
|
||||
}
|
||||
}
|
||||
|
||||
public void Free(KcpSegment seg)
|
||||
{
|
||||
IntPtr ptr = (IntPtr)seg.ptr;
|
||||
blocks.Push(ptr);
|
||||
}
|
||||
|
||||
public class Kcp : Kcp<KcpSegment>
|
||||
{
|
||||
public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null)
|
||||
: base(conv_, callback, rentable)
|
||||
{
|
||||
SegmentManager = Default;
|
||||
}
|
||||
}
|
||||
|
||||
public class KcpIO : KcpIO<KcpSegment>
|
||||
{
|
||||
public KcpIO(uint conv_)
|
||||
: base(conv_)
|
||||
{
|
||||
SegmentManager = Default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 使用内存池,而不是非托管内存,有内存alloc,但是不多。可以解决Marshal.AllocHGlobal 内核调用带来的性能问题
|
||||
/// </summary>
|
||||
public class PoolSegManager : ISegmentManager<PoolSegManager.Seg>
|
||||
{
|
||||
public static PoolSegManager Default { get; } = new PoolSegManager();
|
||||
|
||||
/// <summary>
|
||||
/// 因为默认mtu是1400,并且内存需要内存行/内存页对齐。这里直接512对齐。
|
||||
/// </summary>
|
||||
public const int blockSize = 512 * 3;
|
||||
public class Seg : IKcpSegment
|
||||
{
|
||||
byte[] cache;
|
||||
public Seg(int blockSize)
|
||||
{
|
||||
cache = Buffers.ArrayPool<byte>.Shared.Rent(blockSize);
|
||||
}
|
||||
|
||||
///以下为需要网络传输的参数
|
||||
public const int LocalOffset = 4 * 4;
|
||||
public const int HeadOffset = Kcp.IKCP_OVERHEAD;
|
||||
|
||||
public byte cmd { get; set; }
|
||||
public uint conv { get; set; }
|
||||
public Span<byte> data => cache.AsSpan().Slice(0, (int)len);
|
||||
public uint fastack { get; set; }
|
||||
public byte frg { get; set; }
|
||||
public uint len { get; internal set; }
|
||||
public uint resendts { get; set; }
|
||||
public uint rto { get; set; }
|
||||
public uint sn { get; set; }
|
||||
public uint ts { get; set; }
|
||||
public uint una { get; set; }
|
||||
public ushort wnd { get; set; }
|
||||
public uint xmit { get; set; }
|
||||
|
||||
public int Encode(Span<byte> buffer)
|
||||
{
|
||||
var datelen = (int)(HeadOffset + len);
|
||||
|
||||
///备用偏移值 现阶段没有使用
|
||||
const int offset = 0;
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset), conv);
|
||||
buffer[offset + 4] = cmd;
|
||||
buffer[offset + 5] = frg;
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(offset + 6), wnd);
|
||||
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 8), ts);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 12), sn);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 16), una);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 20), len);
|
||||
|
||||
data.CopyTo(buffer.Slice(HeadOffset));
|
||||
}
|
||||
else
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset), conv);
|
||||
buffer[offset + 4] = cmd;
|
||||
buffer[offset + 5] = frg;
|
||||
BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(offset + 6), wnd);
|
||||
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 8), ts);
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 12), sn);
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 16), una);
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 20), len);
|
||||
|
||||
data.CopyTo(buffer.Slice(HeadOffset));
|
||||
}
|
||||
|
||||
return datelen;
|
||||
}
|
||||
}
|
||||
ConcurrentStack<Seg> Pool = new ConcurrentStack<Seg>();
|
||||
public Seg Alloc(int appendDateSize)
|
||||
{
|
||||
if (appendDateSize > blockSize)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
if (Pool.TryPop(out var ret))
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = new Seg(blockSize);
|
||||
}
|
||||
ret.len = (uint)appendDateSize;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void Free(Seg seg)
|
||||
{
|
||||
seg.cmd = 0;
|
||||
seg.conv = 0;
|
||||
seg.fastack = 0;
|
||||
seg.frg = 0;
|
||||
seg.len = 0;
|
||||
seg.resendts = 0;
|
||||
seg.rto = 0;
|
||||
seg.sn = 0;
|
||||
seg.ts = 0;
|
||||
seg.una = 0;
|
||||
seg.wnd = 0;
|
||||
seg.xmit = 0;
|
||||
Pool.Push(seg);
|
||||
}
|
||||
|
||||
public class Kcp : Kcp<Seg>
|
||||
{
|
||||
public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null)
|
||||
: base(conv_, callback, rentable)
|
||||
{
|
||||
SegmentManager = Default;
|
||||
}
|
||||
}
|
||||
|
||||
public class KcpIO : KcpIO<Seg>
|
||||
{
|
||||
public KcpIO(uint conv_)
|
||||
: base(conv_)
|
||||
{
|
||||
SegmentManager = Default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e7f0d71aa1362b6488c3173d525fd284
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,65 @@
|
||||
using System.Buffers;
|
||||
using System.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace System.Net.Sockets.Kcp.Simple
|
||||
{
|
||||
/// <summary>
|
||||
/// 简单例子。
|
||||
/// </summary>
|
||||
public class SimpleKcpClient : IKcpCallback
|
||||
{
|
||||
UdpClient client;
|
||||
|
||||
public SimpleKcpClient(int port)
|
||||
: this(port, null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public SimpleKcpClient(int port, IPEndPoint endPoint)
|
||||
{
|
||||
client = new UdpClient(port);
|
||||
kcp = new SimpleSegManager.Kcp(2001, this);
|
||||
this.EndPoint = endPoint;
|
||||
BeginRecv();
|
||||
}
|
||||
|
||||
public SimpleSegManager.Kcp kcp { get; }
|
||||
public IPEndPoint EndPoint { get; set; }
|
||||
|
||||
public void Output(IMemoryOwner<byte> buffer, int avalidLength)
|
||||
{
|
||||
var s = buffer.Memory.Span.Slice(0, avalidLength).ToArray();
|
||||
client.SendAsync(s, s.Length, EndPoint);
|
||||
buffer.Dispose();
|
||||
}
|
||||
|
||||
public async void SendAsync(byte[] datagram, int bytes)
|
||||
{
|
||||
kcp.Send(datagram.AsSpan().Slice(0, bytes));
|
||||
}
|
||||
|
||||
public async UniTask<byte[]> ReceiveAsync()
|
||||
{
|
||||
var (buffer, avalidLength) = kcp.TryRecv();
|
||||
while (buffer == null)
|
||||
{
|
||||
await Task.Delay(10);
|
||||
(buffer, avalidLength) = kcp.TryRecv();
|
||||
}
|
||||
|
||||
var s = buffer.Memory.Span.Slice(0, avalidLength).ToArray();
|
||||
return s;
|
||||
}
|
||||
|
||||
private async void BeginRecv()
|
||||
{
|
||||
var res = await client.ReceiveAsync();
|
||||
EndPoint = res.RemoteEndPoint;
|
||||
kcp.Input(res.Buffer);
|
||||
BeginRecv();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff16ed9b89525df4aa6501c0edc679d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,48 @@
|
||||
using System.Net.Sockets.Kcp.Simple;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
namespace TestServer
|
||||
{
|
||||
/// <summary>
|
||||
/// 简单例子。
|
||||
/// </summary>
|
||||
class SimpleKcpServer
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Hello World!");
|
||||
|
||||
SimpleKcpClient kcpClient = new SimpleKcpClient(40001);
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
kcpClient.kcp.Update(DateTimeOffset.UtcNow);
|
||||
await Task.Delay(10);
|
||||
}
|
||||
});
|
||||
|
||||
StartRecv(kcpClient);
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
static async void StartRecv(SimpleKcpClient client)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var res = await client.ReceiveAsync();
|
||||
var str = System.Text.Encoding.UTF8.GetString(res);
|
||||
if ("发送一条消息" == str)
|
||||
{
|
||||
Console.WriteLine(str);
|
||||
|
||||
var buffer = System.Text.Encoding.UTF8.GetBytes("回复一条消息");
|
||||
client.SendAsync(buffer, buffer.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97e221b7388e412da59b0f4d200cb891
|
||||
timeCreated: 1682095200
|
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
//[assembly: InternalsVisibleTo("UnitTestProject1")]
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
public static class KcpExtension_FDF71D0BC31D49C48EEA8FAA51F017D4
|
||||
{
|
||||
private static readonly DateTime utc_time = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
[Obsolete("", true)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint ConvertTime(this in DateTime time)
|
||||
{
|
||||
return (uint)(Convert.ToInt64(time.Subtract(utc_time).TotalMilliseconds) & 0xffffffff);
|
||||
}
|
||||
|
||||
private static readonly DateTimeOffset utc1970 = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint ConvertTimeOld(this in DateTimeOffset time)
|
||||
{
|
||||
return (uint)(Convert.ToInt64(time.Subtract(utc1970).TotalMilliseconds) & 0xffffffff);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint ConvertTime2(this in DateTimeOffset time)
|
||||
{
|
||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
||||
return (uint)(time.ToUnixTimeMilliseconds() & 0xffffffff);
|
||||
#else
|
||||
return (uint)(Convert.ToInt64(time.Subtract(utc1970).TotalMilliseconds) & 0xffffffff);
|
||||
#endif
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint ConvertTime(this in DateTimeOffset time)
|
||||
{
|
||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
||||
return (uint)(time.ToUnixTimeMilliseconds());
|
||||
#else
|
||||
return (uint)(Convert.ToInt64(time.Subtract(utc1970).TotalMilliseconds) & 0xffffffff);
|
||||
#endif
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string ToLogString<T>(this T segment, bool local = false)
|
||||
where T : IKcpSegment
|
||||
{
|
||||
if (local)
|
||||
{
|
||||
return $"sn:{segment.sn,2} una:{segment.una,2} frg:{segment.frg,2} cmd:{segment.cmd,2} len:{segment.len,2} wnd:{segment.wnd} [ LocalValue: xmit:{segment.xmit} fastack:{segment.fastack} rto:{segment.rto} ]";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"sn:{segment.sn,2} una:{segment.una,2} frg:{segment.frg,2} cmd:{segment.cmd,2} len:{segment.len,2} wnd:{segment.wnd}";
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Encode<T>(this T Seg, IBufferWriter<byte> writer)
|
||||
where T : IKcpSegment
|
||||
{
|
||||
var totalLength = (int)(KcpSegment.HeadOffset + Seg.len);
|
||||
var span = writer.GetSpan(totalLength);
|
||||
Seg.Encode(span);
|
||||
writer.Advance(totalLength);
|
||||
return totalLength;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd298c4aa310a7e448c4694ddc41337e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user