提交 36edc4d2 编写于 作者: 若汝棋茗

修复资源释放问题。增加连接超时设置

上级 e765ae30
此差异已折叠。
......@@ -14,6 +14,7 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace RRQMCore.ByteManager
{
......@@ -24,18 +25,23 @@ namespace RRQMCore.ByteManager
{
private static ConcurrentDictionary<long, BytesQueue> bytesDictionary = new ConcurrentDictionary<long, BytesQueue>();
private static bool autoZero;
private static long fullSize;
private static int keyCapacity;
private static int maxBlockSize;
private static long maxSize;
private static int minBlockSize;
private static bool m_autoZero;
private static long m_fullSize;
private static int m_keyCapacity;
private static int m_maxBlockSize;
private static long m_maxSize;
private static int m_minBlockSize;
private static Timer m_timer;
static BytePool()
{
keyCapacity = 100;
autoZero = false;
maxSize = 1024 * 1024 * 512;
m_timer = new Timer((o)=>
{
BytePool.Clear();
},null,1000*60*60, 1000 * 60 * 60);
m_keyCapacity = 100;
m_autoZero = false;
m_maxSize = 1024 * 1024 * 512;
SetBlockSize(1024, 1024 * 1024 * 20);
AddSizeKey(10240);
}
......@@ -45,8 +51,8 @@ namespace RRQMCore.ByteManager
/// </summary>
public static bool AutoZero
{
get => autoZero;
set => autoZero = value;
get => m_autoZero;
set => m_autoZero = value;
}
/// <summary>
......@@ -54,35 +60,35 @@ namespace RRQMCore.ByteManager
/// </summary>
public static int KeyCapacity
{
get => keyCapacity;
set => keyCapacity = value;
get => m_keyCapacity;
set => m_keyCapacity = value;
}
/// <summary>
/// 单个块最大值
/// </summary>
public static int MaxBlockSize => maxBlockSize;
public static int MaxBlockSize => m_maxBlockSize;
/// <summary>
/// 允许的内存池最大值
/// </summary>
public static long MaxSize
{
get => maxSize;
get => m_maxSize;
set
{
if (value < 1024)
{
value = 1024;
}
maxSize = value;
m_maxSize = value;
}
}
/// <summary>
/// 单个块最小值
/// </summary>
public static int MinBlockSize => minBlockSize;
public static int MinBlockSize => m_minBlockSize;
/// <summary>
/// 添加尺寸键
......@@ -134,7 +140,7 @@ namespace RRQMCore.ByteManager
/// <returns></returns>
public static ByteBlock GetByteBlock(int byteSize, bool equalSize)
{
ByteBlock byteBlock = new ByteBlock(GetByteCore(byteSize, equalSize));
ByteBlock byteBlock = new ByteBlock(byteSize, equalSize);
return byteBlock;
}
......@@ -145,9 +151,9 @@ namespace RRQMCore.ByteManager
/// <returns></returns>
public static ByteBlock GetByteBlock(int byteSize)
{
if (byteSize < minBlockSize)
if (byteSize < m_minBlockSize)
{
byteSize = minBlockSize;
byteSize = m_minBlockSize;
}
return GetByteBlock(byteSize, false);
}
......@@ -158,7 +164,7 @@ namespace RRQMCore.ByteManager
/// <returns></returns>
public static ByteBlock GetByteBlock()
{
return GetByteBlock(maxBlockSize, true);
return GetByteBlock(m_maxBlockSize, true);
}
/// <summary>
......@@ -197,8 +203,8 @@ namespace RRQMCore.ByteManager
/// <param name="maxBlockSize"></param>
public static void SetBlockSize(int minBlockSize, int maxBlockSize)
{
BytePool.maxBlockSize = maxBlockSize;
BytePool.minBlockSize = minBlockSize;
BytePool.m_maxBlockSize = maxBlockSize;
BytePool.m_minBlockSize = minBlockSize;
bytesDictionary.Clear();
}
......@@ -218,7 +224,7 @@ namespace RRQMCore.ByteManager
{
if (bytesCollection.TryGet(out byte[] bytes))
{
fullSize -= byteSize;
m_fullSize -= byteSize;
return bytes;
}
}
......@@ -236,7 +242,7 @@ namespace RRQMCore.ByteManager
{
if (bytesCollection.TryGet(out byte[] bytes))
{
fullSize -= byteSize;
m_fullSize -= byteSize;
return bytes;
}
}
......@@ -254,15 +260,15 @@ namespace RRQMCore.ByteManager
/// <param name="bytes"></param>
public static void Recycle(byte[] bytes)
{
if (maxSize > fullSize)
if (m_maxSize > m_fullSize)
{
if (bytesDictionary.TryGetValue(bytes.Length, out BytesQueue bytesQueue))
{
if (autoZero)
if (m_autoZero)
{
Array.Clear(bytes, 0, bytes.Length);
}
fullSize += bytes.Length;
m_fullSize += bytes.Length;
bytesQueue.Add(bytes);
}
}
......@@ -273,17 +279,17 @@ namespace RRQMCore.ByteManager
{
size += collection.FullSize;
}
fullSize = size;
m_fullSize = size;
}
}
private static void CheckKeyCapacity(int byteSize)
{
if (byteSize < minBlockSize || byteSize > maxBlockSize)
if (byteSize < m_minBlockSize || byteSize > m_maxBlockSize)
{
return;
}
if (bytesDictionary.Count < keyCapacity)
if (bytesDictionary.Count < m_keyCapacity)
{
bytesDictionary.TryAdd(byteSize, new BytesQueue(byteSize));
}
......@@ -387,81 +393,53 @@ namespace RRQMCore.ByteManager
//U3D无法编译时替换。
if (num <= 1024)
{
return 1024;
}
else if (num <= 2048)
{
return 2048;
}
else if (num <= 4096)
{
return 4096;
}
else if (num <= 8192)
{
return 8192;
}
else if (num <= 10240)
if (num <= 10240)//10k
{
return 10240;
}
else if (num <= 16384)
{
return 16384;
}
else if (num <= 32768)
{
return 32768;
}
else if (num <= 65536)
else if (num <= 65536)//64k
{
return 65536;
}
else if (num <= 131072)
{
return 131072;
}
else if (num <= 262144)
else if (num <= 102400)//100k
{
return 262144;
return 102400;
}
else if (num <= 524288)
else if (num <= 524288) //512k
{
return 524288;
}
else if (num <= 1048576)
else if (num <= 1048576)//1Mb
{
return 1048576;
}
else if (num <= 2097152)
else if (num <= 5242880)//5Mb
{
return 2097152;
return 5242880;
}
else if (num <= 4194304)
else if (num <= 10485760)//10Mb
{
return 4194304;
return 10485760;
}
else if (num <= 8388608)
else if (num <= 1024*1024*20)//20Mb
{
return 8388608;
return 1024 * 1024 * 20;
}
else if (num <= 16777216)
else if (num <= 1024 * 1024 * 50)//50Mb
{
return 16777216;
return 1024 * 1024 * 50;
}
else if (num <= 33554432)
else if (num <= 1024 * 1024 * 100)//100Mb
{
return 33554432;
return 1024 * 1024 * 100;
}
else if (num <= 67108864)
else if (num <= 1024 * 1024 * 500)//500Mb
{
return 67108864;
return 1024 * 1024 * 500;
}
else if (num <= 134217728)
else if (num <= 1024 * 1024 * 1024)//1Gb
{
return 134217728;
return 1024 * 1024 * 1024;
}
else
{
......
......@@ -19,27 +19,27 @@ namespace RRQMCore.Collections.Concurrent
/// </summary>
public class ConcurrentDoublyDictionary<TKey, TValue>
{
private ConcurrentDictionary<TKey, TValue> keyToValue;
private ConcurrentDictionary<TValue, TKey> valueToKey;
private ConcurrentDictionary<TKey, TValue> m_keyToValue;
private ConcurrentDictionary<TValue, TKey> m_valueToKey;
/// <summary>
/// 构造函数
/// </summary>
public ConcurrentDoublyDictionary()
{
this.keyToValue = new ConcurrentDictionary<TKey, TValue>();
this.valueToKey = new ConcurrentDictionary<TValue, TKey>();
this.m_keyToValue = new ConcurrentDictionary<TKey, TValue>();
this.m_valueToKey = new ConcurrentDictionary<TValue, TKey>();
}
/// <summary>
/// 由键指向值得集合
/// </summary>
public ConcurrentDictionary<TKey, TValue> KeyToValue => this.keyToValue;
public ConcurrentDictionary<TKey, TValue> KeyToValue => this.m_keyToValue;
/// <summary>
/// 由值指向键的集合
/// </summary>
public ConcurrentDictionary<TValue, TKey> ValueToKey => this.valueToKey;
public ConcurrentDictionary<TValue, TKey> ValueToKey => this.m_valueToKey;
/// <summary>
/// 尝试将指定的键和值添加到字典中。
......@@ -49,15 +49,15 @@ namespace RRQMCore.Collections.Concurrent
/// <returns></returns>
public bool TryAdd(TKey key, TValue value)
{
if (this.keyToValue.TryAdd(key, value))
if (this.m_keyToValue.TryAdd(key, value))
{
if (this.valueToKey.TryAdd(value, key))
if (this.m_valueToKey.TryAdd(value, key))
{
return true;
}
else
{
this.keyToValue.TryRemove(key, out _);
this.m_keyToValue.TryRemove(key, out _);
return false;
}
}
......@@ -72,9 +72,9 @@ namespace RRQMCore.Collections.Concurrent
/// <returns></returns>
public bool TryRemoveFromKey(TKey key, out TValue value)
{
if (this.keyToValue.TryRemove(key, out value))
if (this.m_keyToValue.TryRemove(key, out value))
{
if (this.valueToKey.TryRemove(value, out _))
if (this.m_valueToKey.TryRemove(value, out _))
{
return true;
}
......@@ -90,9 +90,9 @@ namespace RRQMCore.Collections.Concurrent
/// <returns></returns>
public bool TryRemoveFromValue(TValue value, out TKey key)
{
if (this.valueToKey.TryRemove(value, out key))
if (this.m_valueToKey.TryRemove(value, out key))
{
if (this.keyToValue.TryRemove(key, out _))
if (this.m_keyToValue.TryRemove(key, out _))
{
return true;
}
......@@ -108,7 +108,7 @@ namespace RRQMCore.Collections.Concurrent
/// <returns></returns>
public bool TryGetFromKey(TKey key, out TValue value)
{
return this.keyToValue.TryGetValue(key, out value);
return this.m_keyToValue.TryGetValue(key, out value);
}
/// <summary>
......@@ -119,7 +119,7 @@ namespace RRQMCore.Collections.Concurrent
/// <returns></returns>
public bool TryGetFromValue(TValue value, out TKey key)
{
return this.valueToKey.TryGetValue(value, out key);
return this.m_valueToKey.TryGetValue(value, out key);
}
}
}
\ No newline at end of file
......@@ -21,9 +21,9 @@ namespace RRQMCore.Collections.Concurrent
/// <typeparam name="T"></typeparam>
public class IntelligentConcurrentQueue<T> : ConcurrentQueue<T>
{
private int count;
private int m_count;
private int maxCount;
private int m_maxCount;
/// <summary>
/// 构造函数
......@@ -31,18 +31,18 @@ namespace RRQMCore.Collections.Concurrent
/// <param name="maxCount"></param>
public IntelligentConcurrentQueue(int maxCount)
{
this.maxCount = maxCount;
this.m_maxCount = maxCount;
}
/// <summary>
/// 允许的最大长度
/// </summary>
public int MaxCount => this.maxCount;
public int MaxCount => this.m_maxCount;
/// <summary>
/// 长度
/// </summary>
public new int Count => this.count;
public new int Count => this.m_count;
/// <summary>
/// 入队
......@@ -51,7 +51,7 @@ namespace RRQMCore.Collections.Concurrent
public new void Enqueue(T item)
{
SpinWait.SpinUntil(this.Check);
Interlocked.Increment(ref this.count);
Interlocked.Increment(ref this.m_count);
base.Enqueue(item);
}
......@@ -64,7 +64,7 @@ namespace RRQMCore.Collections.Concurrent
{
if (base.TryDequeue(out result))
{
Interlocked.Decrement(ref this.count);
Interlocked.Decrement(ref this.m_count);
return true;
}
return false;
......@@ -72,7 +72,7 @@ namespace RRQMCore.Collections.Concurrent
private bool Check()
{
return this.count < this.maxCount;
return this.m_count < this.m_maxCount;
}
}
}
\ No newline at end of file
......@@ -22,38 +22,38 @@ namespace RRQMCore.Collections.Concurrent
/// <typeparam name="T"></typeparam>
public class IntelligentDataQueue<T> : ConcurrentQueue<T> where T : IQueueData
{
private bool overflowWait;
private bool m_overflowWait;
/// <summary>
/// 溢出等待
/// </summary>
public bool OverflowWait
{
get => this.overflowWait;
set => this.overflowWait = value;
get => this.m_overflowWait;
set => this.m_overflowWait = value;
}
private Action<bool> onQueueChanged;
private Action<bool> m_onQueueChanged;
/// <summary>
/// 在队列修改时
/// </summary>
public Action<bool> OnQueueChanged
{
get => this.onQueueChanged;
set => this.onQueueChanged = value;
get => this.m_onQueueChanged;
set => this.m_onQueueChanged = value;
}
private bool free;
private bool m_free;
/// <summary>
/// 是否有空位允许入队
/// </summary>
public bool Free => this.free;
public bool Free => this.m_free;
private long actualSize;
private long m_actualSize;
private long maxSize;
private long m_maxSize;
/// <summary>
/// 构造函数
......@@ -61,8 +61,8 @@ namespace RRQMCore.Collections.Concurrent
/// <param name="maxSize"></param>
public IntelligentDataQueue(long maxSize)
{
this.free = true;
this.overflowWait = true;
this.m_free = true;
this.m_overflowWait = true;
this.MaxSize = maxSize;
}
......@@ -78,21 +78,21 @@ namespace RRQMCore.Collections.Concurrent
/// </summary>
public long MaxSize
{
get => this.maxSize;
get => this.m_maxSize;
set
{
if (value < 1)
{
value = 1;
}
this.maxSize = value;
this.m_maxSize = value;
}
}
/// <summary>
/// 实际尺寸
/// </summary>
public long ActualSize => this.actualSize;
public long ActualSize => this.m_actualSize;
/// <summary>
/// 清空队列
......@@ -111,20 +111,24 @@ namespace RRQMCore.Collections.Concurrent
/// <param name="item"></param>
public new void Enqueue(T item)
{
bool free = this.actualSize < this.maxSize;
if (this.free != free)
lock (this)
{
this.free = free;
this.onQueueChanged?.Invoke(this.free);
}
bool free = this.m_actualSize < this.m_maxSize;
if (this.m_free != free)
{
this.m_free = free;
this.m_onQueueChanged?.Invoke(this.m_free);
}
if (this.overflowWait)
{
SpinWait.SpinUntil(this.Check);
}
if (this.m_overflowWait)
{
SpinWait.SpinUntil(this.Check);
}
Interlocked.Add(ref this.actualSize, item.Size);
base.Enqueue(item);
Interlocked.Add(ref this.m_actualSize, item.Size);
base.Enqueue(item);
}
}
/// <summary>
......@@ -136,12 +140,12 @@ namespace RRQMCore.Collections.Concurrent
{
if (base.TryDequeue(out result))
{
Interlocked.Add(ref this.actualSize, -result.Size);
bool free = this.actualSize < this.maxSize;
if (this.free != free)
Interlocked.Add(ref this.m_actualSize, -result.Size);
bool free = this.m_actualSize < this.m_maxSize;
if (this.m_free != free)
{
this.free = free;
this.onQueueChanged?.Invoke(this.free);
this.m_free = free;
this.m_onQueueChanged?.Invoke(this.m_free);
}
return true;
}
......@@ -150,7 +154,7 @@ namespace RRQMCore.Collections.Concurrent
private bool Check()
{
return this.actualSize < this.maxSize;
return this.m_actualSize < this.m_maxSize;
}
}
......@@ -164,4 +168,52 @@ namespace RRQMCore.Collections.Concurrent
/// </summary>
int Size { get; }
}
/// <summary>
/// 传输字节
/// </summary>
public readonly struct QueueDataBytes : IQueueData
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="buffer"></param>
/// <param name="offset"></param>
/// <param name="length"></param>
public QueueDataBytes(byte[] buffer, int offset, int length)
{
this.Offset = offset;
this.Length = length;
this.Buffer = buffer;
this.Size = length;
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="buffer"></param>
public QueueDataBytes(byte[] buffer) : this(buffer, 0, buffer.Length)
{
}
/// <summary>
/// 数据内存
/// </summary>
public byte[] Buffer { get; }
/// <summary>
/// 偏移
/// </summary>
public int Offset { get; }
/// <summary>
/// 长度
/// </summary>
public int Length { get; }
/// <summary>
/// 尺寸
/// </summary>
public int Size { get; }
}
}
\ No newline at end of file
......@@ -24,7 +24,7 @@ namespace RRQMCore
/// </summary>
public abstract class AppConfigBase
{
private readonly string fullPath;
private readonly string m_fullPath;
/// <summary>
/// 构造函数
......@@ -37,7 +37,7 @@ namespace RRQMCore
throw new ArgumentException($"“{nameof(fullPath)}”不能为 null 或空。", nameof(fullPath));
}
this.fullPath = fullPath;
this.m_fullPath = fullPath;
}
/// <summary>
......@@ -48,14 +48,14 @@ namespace RRQMCore
/// <returns></returns>
public bool Save(bool overwrite, out string msg)
{
if (overwrite == false && File.Exists(this.fullPath))
if (overwrite == false && File.Exists(this.m_fullPath))
{
msg = null;
return true;
}
try
{
File.WriteAllBytes(this.fullPath, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(this, Formatting.Indented)));
File.WriteAllBytes(this.m_fullPath, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(this, Formatting.Indented)));
msg = null;
return true;
}
......@@ -75,11 +75,11 @@ namespace RRQMCore
{
try
{
if (!File.Exists(this.fullPath))
if (!File.Exists(this.m_fullPath))
{
this.Save(false, out _);
}
var obj = File.ReadAllText(this.fullPath).ToJsonObject(this.GetType());
var obj = File.ReadAllText(this.m_fullPath).ToJsonObject(this.GetType());
var ps = this.GetType().GetProperties();
foreach (var item in ps)
......
......@@ -134,12 +134,6 @@ namespace RRQMCore
[Description("Token消息为‘{0}’的未注册。")]
MessageNotFound,
/// <summary>
/// 内存块已释放
/// </summary>
[Description("内存块已释放。")]
ByteBlockDisposed,
/// <summary>
/// 数据处理适配器为空
/// </summary>
......
......@@ -20,7 +20,7 @@ namespace RRQMCore
/// </summary>
public struct ReadLock : IDisposable
{
private ReaderWriterLockSlim _locks;
private ReaderWriterLockSlim m_locks;
/// <summary>
/// 构造函数
......@@ -28,8 +28,8 @@ namespace RRQMCore
/// <param name="locks"></param>
public ReadLock(ReaderWriterLockSlim locks)
{
this._locks = locks;
this._locks.EnterReadLock();
this.m_locks = locks;
this.m_locks.EnterReadLock();
}
/// <summary>
......@@ -37,7 +37,7 @@ namespace RRQMCore
/// </summary>
public void Dispose()
{
this._locks.ExitReadLock();
this.m_locks.ExitReadLock();
}
}
......@@ -46,7 +46,7 @@ namespace RRQMCore
/// </summary>
public struct WriteLock : IDisposable
{
private ReaderWriterLockSlim _locks;
private ReaderWriterLockSlim m_locks;
/// <summary>
/// 构造函数
......@@ -54,8 +54,8 @@ namespace RRQMCore
/// <param name="locks"></param>
public WriteLock(ReaderWriterLockSlim locks)
{
this._locks = locks;
this._locks.EnterWriteLock();
this.m_locks = locks;
this.m_locks.EnterWriteLock();
}
/// <summary>
......@@ -63,7 +63,7 @@ namespace RRQMCore
/// </summary>
public void Dispose()
{
this._locks.ExitWriteLock();
this.m_locks.ExitWriteLock();
}
}
}
\ No newline at end of file
......@@ -85,41 +85,6 @@ namespace RRQMCore
}
}
/// <summary>
/// 内存块已释放
/// </summary>
[Serializable]
public class ByteBlockDisposedException : RRQMException
{
/// <summary>
/// 构造函数
/// </summary>
public ByteBlockDisposedException()
{ }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="message"></param>
public ByteBlockDisposedException(string message) : base(message) { }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="message"></param>
/// <param name="inner"></param>
public ByteBlockDisposedException(string message, Exception inner) : base(message, inner) { }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="info"></param>
/// <param name="context"></param>
protected ByteBlockDisposedException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}
/// <summary>
/// 构造函数
/// </summary>
......
using System;
using System.IO;
using System.Threading;
namespace RRQMCore.IO
{
/// <summary>
/// 阻塞式单项读取流。
/// </summary>
public abstract class BlockReadStream : Stream
{
private byte[] m_buffer;
private AutoResetEvent m_inputEvent;
private volatile int m_offset;
private AutoResetEvent m_readEvent;
private volatile int m_surLength;
/// <summary>
/// 构造函数
/// </summary>
public BlockReadStream()
{
this.m_readEvent = new AutoResetEvent(false);
this.m_inputEvent = new AutoResetEvent(false);
this.ReadTimeout = 5000;
}
/// <summary>
/// 可读
/// </summary>
public override bool CanRead => true;
/// <summary>
/// 不可使用
/// </summary>
public override bool CanSeek => throw new NotImplementedException();
/// <summary>
/// 不可使用
/// </summary>
public override long Length => throw new NotImplementedException();
/// <summary>
/// 不可使用
/// </summary>
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
/// <summary>
/// <inheritdoc/>
/// </summary>
public override int ReadTimeout { get; set; }
/// <summary>
/// 阻塞读取。
/// </summary>
/// <param name="buffer"></param>
/// <param name="offset"></param>
/// <param name="count"></param>
/// <returns></returns>
public override int Read(byte[] buffer, int offset, int count)
{
if (!this.CanRead)
{
throw new RRQMException("该流不允许读取。");
}
int r;
if (this.m_surLength > 0)
{
if (this.m_surLength > count)
{
//按count读取
Array.Copy(m_buffer, m_offset, buffer, offset, count);
m_surLength -= count;
m_offset += count;
r = count;
}
else
{
//会读完本次
Array.Copy(m_buffer, m_offset, buffer, offset, m_surLength);
r = m_surLength;
this.Reset();
}
}
else
{
//无数据,须等待
if (this.m_readEvent.WaitOne(this.ReadTimeout))
{
if (this.m_surLength == 0)
{
this.Reset();
r = 0;
}
else if (this.m_surLength > count)
{
//按count读取
Array.Copy(m_buffer, m_offset, buffer, offset, count);
m_surLength -= count;
m_offset += count;
r = count;
}
else
{
//会读完本次
Array.Copy(m_buffer, m_offset, buffer, offset, m_surLength);
r = m_surLength;
this.Reset();
}
}
else
{
throw new TimeoutException();
}
}
return r;
}
/// <summary>
/// 不可使用
/// </summary>
/// <param name="offset"></param>
/// <param name="origin"></param>
/// <returns></returns>
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
/// <summary>
/// 不可使用
/// </summary>
/// <param name="value"></param>
public override void SetLength(long value)
{
throw new NotImplementedException();
}
/// <summary>
/// 传输输入.
/// 必须以length为0结束。读取端会超时。
/// </summary>
/// <param name="buffer"></param>
/// <param name="offset"></param>
/// <param name="length"></param>
/// <returns></returns>
protected bool Input(byte[] buffer, int offset, int length)
{
this.m_inputEvent.Reset();
this.m_buffer = buffer;
this.m_offset = offset;
this.m_surLength = length;
this.m_readEvent.Set();
return this.m_inputEvent.WaitOne(this.ReadTimeout);
}
private void Reset()
{
this.m_buffer = null;
this.m_offset =0;
this.m_surLength = 0;
this.m_readEvent.Reset();
this.m_inputEvent.Set();
}
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="disposing"></param>
protected override void Dispose(bool disposing)
{
this.Reset();
this.m_readEvent.Dispose();
this.m_inputEvent.Dispose();
base.Dispose(disposing);
}
}
}
\ No newline at end of file
......@@ -5,7 +5,7 @@
<Authors>若汝棋茗</Authors>
<PackageIcon>RRQM.png</PackageIcon>
<Copyright>Copyright © 2022 若汝棋茗</Copyright>
<Version>8.0.4</Version>
<Version>8.0.5</Version>
<LangVersion>8.0</LangVersion>
<SignAssembly>true</SignAssembly>
<Description>此程序集是RRQM的核心开源库,其中包含了内存池、高性能序列化、日志接口在内的很多基本内容。
......
......@@ -77,8 +77,8 @@ DEMO:https://gitee.com/RRQM_Home/RRQMBox</Description>
</ItemGroup>
<ItemGroup>
<PackageReference Include="RRQMSocket" Version="*" />
<PackageReference Include="RRQMCore" Version="*" />
<PackageReference Include="RRQMSocket.RPC" Version="*" />
<PackageReference Include="RRQMSocket" Version="8.0.5" />
<PackageReference Include="RRQMCore" Version="8.0.5" />
<PackageReference Include="RRQMSocket.RPC" Version="8.0.3" />
</ItemGroup>
</Project>
......@@ -82,7 +82,7 @@ DEMO:https://gitee.com/RRQM_Home/RRQMBox</Description>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="RRQMSocket" Version="*" />
<PackageReference Include="RRQMCore" Version="*" />
<PackageReference Include="RRQMSocket" Version="8.0.5" />
<PackageReference Include="RRQMCore" Version="8.0.5" />
</ItemGroup>
</Project>
......@@ -75,9 +75,9 @@ DEMO:https://gitee.com/RRQM_Home/RRQMBox</Description>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="RRQMSocket" Version="*" />
<PackageReference Include="RRQMCore" Version="*" />
<PackageReference Include="RRQMSocket.Http" Version="*" />
<PackageReference Include="RRQMSocket.RPC" Version="*" />
<PackageReference Include="RRQMSocket" Version="8.0.5" />
<PackageReference Include="RRQMCore" Version="8.0.5" />
<PackageReference Include="RRQMSocket.Http" Version="8.0.3" />
<PackageReference Include="RRQMSocket.RPC" Version="8.0.3" />
</ItemGroup>
</Project>
......@@ -75,9 +75,9 @@ DEMO:https://gitee.com/RRQM_Home/RRQMBox</Description>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="RRQMSocket" Version="*" />
<PackageReference Include="RRQMCore" Version="*" />
<PackageReference Include="RRQMSocket.Http" Version="*" />
<PackageReference Include="RRQMSocket.RPC" Version="*" />
<PackageReference Include="RRQMSocket" Version="8.0.5" />
<PackageReference Include="RRQMCore" Version="8.0.5" />
<PackageReference Include="RRQMSocket.Http" Version="8.0.3" />
<PackageReference Include="RRQMSocket.RPC" Version="8.0.3" />
</ItemGroup>
</Project>
......@@ -75,9 +75,9 @@ DEMO:https://gitee.com/RRQM_Home/RRQMBox</Description>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="RRQMSocket" Version="*" />
<PackageReference Include="RRQMCore" Version="*" />
<PackageReference Include="RRQMSocket.Http" Version="*" />
<PackageReference Include="RRQMSocket.RPC" Version="*" />
<PackageReference Include="RRQMSocket" Version="8.0.5" />
<PackageReference Include="RRQMCore" Version="8.0.5" />
<PackageReference Include="RRQMSocket.Http" Version="8.0.3" />
<PackageReference Include="RRQMSocket.RPC" Version="8.0.3" />
</ItemGroup>
</Project>
......@@ -76,7 +76,7 @@ DEMO:https://gitee.com/RRQM_Home/RRQMBox </Description>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="RRQMSocket" Version="*" />
<PackageReference Include="RRQMCore" Version="8.0.4" />
<PackageReference Include="RRQMSocket" Version="8.0.5" />
<PackageReference Include="RRQMCore" Version="8.0.5" />
</ItemGroup>
</Project>
......@@ -46,49 +46,50 @@ namespace RRQMSocket.WebSocket
/// </summary>
public class WSClientBase : HttpClientBase
{
/// <summary>
/// 请求连接到WebSocket。
/// </summary>
/// <returns></returns>
public override ITcpClient Connect()
public override ITcpClient Connect(int timeout = 5000)
{
return this.Connect(default);
return this.Connect(default, timeout);
}
/// <summary>
/// 请求连接到WebSocket。
/// </summary>
/// <param name="timeout"></param>
/// <param name="token"></param>
/// <returns></returns>
public virtual ITcpClient Connect(CancellationToken token)
public virtual ITcpClient Connect(CancellationToken token, int timeout = 5000)
{
lock (this)
{
if (!this.Online)
{
base.Connect();
base.Connect(timeout);
}
string base64Key;
IPHost iPHost = this.Config.GetValue<IPHost>(RRQMConfigExtensions.RemoteIPHostProperty);
string url = iPHost.IsUri ? iPHost.Uri.PathAndQuery : string.Empty;
HttpRequest request = WSTools.GetWSRequest(this.RemoteIPHost.ToString(), url, this.GetWebSocketVersion(), out base64Key);
this.OnHandshaking(new HttpContextEventArgs(request));
var response = this.Request(request, token: token);
this.OnHandshaking(new HttpContextEventArgs(request));
var response = this.Request(request, timeout: timeout, token: token);
if (response.GetHeader("sec-websocket-accept") != WSTools.CalculateBase64Key(base64Key, Encoding.UTF8))
{
this.MainSocket.Dispose();
throw new RRQMException("返回的应答码不正确。");
}
this.SetAdapter(new WebSocketDataHandlingAdapter() { MaxPackageSize=this.MaxPackageSize});
this.SetValue(WebSocketServerPlugin.HandshakedProperty,true);
this.SetAdapter(new WebSocketDataHandlingAdapter());
this.SetValue(WebSocketServerPlugin.HandshakedProperty, true);
response.Flag = true;
this.OnHandshaked(new HttpContextEventArgs(request) { Response=response});
this.OnHandshaked(new HttpContextEventArgs(request) { Response = response });
return this;
}
}
......@@ -119,7 +120,7 @@ namespace RRQMSocket.WebSocket
return;
}
}
this.Handshaking?.Invoke(this,e);
this.Handshaking?.Invoke(this, e);
}
/// <summary>
......@@ -136,7 +137,7 @@ namespace RRQMSocket.WebSocket
return;
}
}
this.Handshaked?.Invoke(this,e);
this.Handshaked?.Invoke(this, e);
}
#endregion 事件
......@@ -149,7 +150,7 @@ namespace RRQMSocket.WebSocket
{
if (this.UsePlugin)
{
this.PluginsManager.Raise<IWebSocketPlugin>("OnHandleWSDataFrame",this,new WSDataFrameEventArgs(dataFrame));
this.PluginsManager.Raise<IWebSocketPlugin>("OnHandleWSDataFrame", this, new WSDataFrameEventArgs(dataFrame));
}
}
......@@ -185,7 +186,7 @@ namespace RRQMSocket.WebSocket
/// <param name="e"></param>
protected override void OnDisconnected(ClientDisconnectedEventArgs e)
{
this.SetValue(WebSocketServerPlugin.HandshakedProperty,false);
this.SetValue(WebSocketServerPlugin.HandshakedProperty, false);
base.OnDisconnected(e);
}
}
......
......@@ -238,7 +238,7 @@ namespace RRQMSocket.WebSocket
{
while (offset < length)
{
if (length - offset <= 4)
if (length - offset < 2)
{
if (this.tempByteBlock == null)
{
......
......@@ -4,7 +4,7 @@
<ApplicationIcon>RRQM.ico</ApplicationIcon>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>RRQM.pfx</AssemblyOriginatorKeyFile>
<Version>8.0.2</Version>
<Version>8.0.3</Version>
<LangVersion>8.0</LangVersion>
<Company>若汝棋茗</Company>
<Copyright>Copyright © 2021 若汝棋茗</Copyright>
......@@ -79,9 +79,9 @@ DEMO:https://gitee.com/RRQM_Home/RRQMBox</Description>
</ItemGroup>
<ItemGroup>
<PackageReference Include="RRQMSocket" Version="*" />
<PackageReference Include="RRQMCore" Version="*" />
<PackageReference Include="RRQMSocket.Http" Version="*" />
<PackageReference Include="RRQMSocket" Version="8.0.5" />
<PackageReference Include="RRQMCore" Version="8.0.5" />
<PackageReference Include="RRQMSocket.Http" Version="8.0.3" />
</ItemGroup>
</Project>
......@@ -20,36 +20,27 @@ namespace RRQMSocket
/// </summary>
public abstract class BaseSocket : RRQMDependencyObject, ISocket
{
/// <summary>
/// 日志记录器
/// </summary>
protected ILog logger;
private int bufferLength;
private int m_bufferLength;
/// <summary>
/// 数据交互缓存池限制,min=1024 byte
/// </summary>
public int BufferLength
{
get => this.bufferLength;
get => this.m_bufferLength;
set
{
if (value < 1024)
{
value = 1024 * 10;
}
this.bufferLength = value;
this.m_bufferLength = value;
}
}
/// <summary>
/// 日志记录器
/// </summary>
public ILog Logger
{
get => this.logger;
set => this.logger = value;
}
public ILog Logger { get; set; }
}
}
\ No newline at end of file
此差异已折叠。
......@@ -23,7 +23,7 @@ namespace RRQMSocket
/// </summary>
public class AsyncSender
{
private readonly IntelligentDataQueue<TransferByte> asyncBytes;
private readonly IntelligentDataQueue<QueueDataBytes> asyncBytes;
private readonly SocketAsyncEventArgs sendEventArgs;
......@@ -60,7 +60,7 @@ namespace RRQMSocket
this.socket = socket;
this.sendEventArgs.RemoteEndPoint = endPoint;
this.onError = onError;
this.asyncBytes = new IntelligentDataQueue<TransferByte>(1024 * 1024 * 100);
this.asyncBytes = new IntelligentDataQueue<QueueDataBytes>(1024 * 1024 * 10);
this.waitHandle = new AutoResetEvent(false);
this.sendThread = new Thread(this.BeginSend);
this.sendThread.IsBackground = true;
......@@ -70,7 +70,7 @@ namespace RRQMSocket
internal void AsyncSend(byte[] buffer, int offset, int length)
{
TransferByte asyncByte = new TransferByte(buffer, offset, length);
QueueDataBytes asyncByte = new QueueDataBytes(buffer, offset, length);
this.asyncBytes.Enqueue(asyncByte);
if (!this.sending)
{
......@@ -93,7 +93,7 @@ namespace RRQMSocket
{
try
{
if (this.tryGet(out TransferByte asyncByte))
if (this.tryGet(out QueueDataBytes asyncByte))
{
this.sendEventArgs.SetBuffer(asyncByte.Buffer, asyncByte.Offset, asyncByte.Length);
......@@ -144,17 +144,17 @@ namespace RRQMSocket
}
}
private bool tryGet(out TransferByte asyncByteDe)
private bool tryGet(out QueueDataBytes asyncByteDe)
{
int len = 0;
int surLen = this.buffer.Length;
while (true)
{
if (this.asyncBytes.TryPeek(out TransferByte asyncB))
if (this.asyncBytes.TryPeek(out QueueDataBytes asyncB))
{
if (surLen > asyncB.Length)
{
if (this.asyncBytes.TryDequeue(out TransferByte asyncByte))
if (this.asyncBytes.TryDequeue(out QueueDataBytes asyncByte))
{
Array.Copy(asyncByte.Buffer, asyncByte.Offset, this.buffer, len, asyncByte.Length);
len += asyncByte.Length;
......@@ -191,7 +191,7 @@ namespace RRQMSocket
}
}
}
asyncByteDe = new TransferByte(this.buffer, 0, len);
asyncByteDe = new QueueDataBytes(this.buffer, 0, len);
return true;
}
}
......
......@@ -17,7 +17,7 @@ namespace RRQMSocket
/// <summary>
/// 传输字节
/// </summary>
public struct TransferByte : IQueueData
public readonly struct TransferByte
{
/// <summary>
/// 构造函数
......@@ -27,9 +27,9 @@ namespace RRQMSocket
/// <param name="length"></param>
public TransferByte(byte[] buffer, int offset, int length)
{
this.offset = offset;
this.length = length;
this.buffer = buffer;
this.Offset = offset;
this.Length = length;
this.Buffer = buffer;
}
/// <summary>
......@@ -40,28 +40,19 @@ namespace RRQMSocket
{
}
private int offset;
private int length;
private byte[] buffer;
/// <summary>
/// 数据内存
/// </summary>
public byte[] Buffer => this.buffer;
public byte[] Buffer { get; }
/// <summary>
/// 偏移
/// </summary>
public int Offset => this.offset;
public int Offset { get; }
/// <summary>
/// 长度
/// </summary>
public int Length => this.length;
/// <summary>
/// 尺寸
/// </summary>
public int Size => this.length;
public int Length { get; }
}
}
\ No newline at end of file
......@@ -64,12 +64,12 @@ namespace RRQMSocket
}
catch (Exception ex)
{
this.logger.Debug(RRQMCore.Log.LogType.Error, this, ex.Message, ex);
this.Logger.Debug(RRQMCore.Log.LogType.Error, this, ex.Message, ex);
}
}
if (sockets.Count == 0)
{
this.logger.Debug(RRQMCore.Log.LogType.Error, this, "转发地址均无法建立,已拒绝本次连接。", null);
this.Logger.Debug(RRQMCore.Log.LogType.Error, this, "转发地址均无法建立,已拒绝本次连接。", null);
e.RemoveOperation(Operation.Permit);
return;
}
......
......@@ -517,7 +517,7 @@ namespace RRQMSocket
}
catch (Exception ex)
{
this.logger.Debug(LogType.Error, this, "在StreamStatusToThis中发生错误。", ex);
this.Logger.Debug(LogType.Error, this, "在StreamStatusToThis中发生错误。", ex);
}
break;
}
......@@ -530,7 +530,7 @@ namespace RRQMSocket
}
catch (Exception ex)
{
this.logger.Debug(LogType.Error, this, "在P_8_RequestStreamToThis中发生错误。", ex);
this.Logger.Debug(LogType.Error, this, "在P_8_RequestStreamToThis中发生错误。", ex);
}
}
break;
......@@ -544,7 +544,7 @@ namespace RRQMSocket
}
catch (Exception ex)
{
this.logger.Debug(LogType.Error, this, $"在{protocol}中发生错误。", ex);
this.Logger.Debug(LogType.Error, this, $"在{protocol}中发生错误。", ex);
}
break;
}
......@@ -661,7 +661,7 @@ namespace RRQMSocket
}
catch (Exception)
{
this.logger.Debug(LogType.Warning, this, "心跳包发送失败。");
this.Logger.Debug(LogType.Warning, this, "心跳包发送失败。");
}
});
this.heartbeatLoopAction.RunAsync();
......@@ -773,7 +773,7 @@ namespace RRQMSocket
waitStream.Status = 3;
waitStream.Message = ex.Message;
this.logger.Debug(LogType.Error, this, $"在{nameof(P_9_RequestStreamToThis)}中发生错误。", ex);
this.Logger.Debug(LogType.Error, this, $"在{nameof(P_9_RequestStreamToThis)}中发生错误。", ex);
}
waitStream.Metadata = null;
......
......@@ -683,7 +683,7 @@ namespace RRQMSocket
}
catch (Exception ex)
{
this.logger.Debug(LogType.Error, this, "在OnPing中发生错误。", ex);
this.Logger.Debug(LogType.Error, this, "在OnPing中发生错误。", ex);
}
break;
}
......@@ -696,7 +696,7 @@ namespace RRQMSocket
}
catch (Exception ex)
{
this.logger.Debug(LogType.Error, this, "在P_8_RequestStreamToThis中发生错误。", ex);
this.Logger.Debug(LogType.Error, this, "在P_8_RequestStreamToThis中发生错误。", ex);
}
}
break;
......@@ -711,7 +711,7 @@ namespace RRQMSocket
}
catch (Exception ex)
{
this.logger.Debug(LogType.Error, this, "在StreamStatusToThis中发生错误。", ex);
this.Logger.Debug(LogType.Error, this, "在StreamStatusToThis中发生错误。", ex);
}
break;
}
......@@ -1037,7 +1037,7 @@ namespace RRQMSocket
waitStream.Status = 3;
waitStream.Message = ex.Message;
this.logger.Debug(LogType.Error, this, $"在{nameof(P_8_RequestStreamToThis)}中发生错误。", ex);
this.Logger.Debug(LogType.Error, this, $"在{nameof(P_8_RequestStreamToThis)}中发生错误。", ex);
}
waitStream.Metadata = null;
......
......@@ -19,6 +19,7 @@ using System.Collections.Generic;
using System.IO;
using System.Net.Security;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
namespace RRQMSocket
......@@ -313,6 +314,11 @@ namespace RRQMSocket
/// <param name="disposing"></param>
protected override void Dispose(bool disposing)
{
if (disposing)
{
this.m_config = default;
this.m_adapter = default;
}
this.Close($"{nameof(Dispose)}主动断开");
base.Dispose(disposing);
}
......@@ -378,7 +384,7 @@ namespace RRQMSocket
/// <summary>
/// 请求连接到服务器。
/// </summary>
public virtual ITcpClient Connect()
public virtual ITcpClient Connect(int timeout=5000)
{
if (this.m_online)
{
......@@ -386,7 +392,7 @@ namespace RRQMSocket
}
if (this.disposedValue)
{
throw new RRQMException("无法利用已释放对象");
throw new ObjectDisposedException(this.GetType().FullName);
}
if (this.m_config == null)
{
......@@ -405,33 +411,44 @@ namespace RRQMSocket
ClientConnectingEventArgs args = new ClientConnectingEventArgs(this.m_mainSocket);
this.OnConnecting(args);
this.m_mainSocket.Connect(iPHost.EndPoint);
this.LoadSocketAndReadIpPort();
if (this.m_separateThreadSend)
var result = this.m_mainSocket.BeginConnect(iPHost.EndPoint, null, null);
if (result.AsyncWaitHandle.WaitOne(timeout))
{
if (this.m_asyncSender == null)
if (this.m_mainSocket.Connected)
{
this.m_asyncSender = new AsyncSender(this.m_mainSocket, this.m_mainSocket.RemoteEndPoint, this.OnSeparateThreadSendError);
this.m_mainSocket.EndConnect(result);
this.LoadSocketAndReadIpPort();
if (this.m_separateThreadSend)
{
if (this.m_asyncSender == null)
{
this.m_asyncSender = new AsyncSender(this.m_mainSocket, this.m_mainSocket.RemoteEndPoint, this.OnSeparateThreadSendError);
}
}
this.BeginReceive();
this.m_online = true;
Task.Run(() =>
{
this.OnConnected(new MesEventArgs("连接成功"));
});
return this;
}
}
this.BeginReceive();
this.m_online = true;
Task.Run(() =>
{
this.OnConnected(new MesEventArgs("连接成功"));
});
return this;
this.m_mainSocket.Dispose();
throw new TimeoutException();
}
/// <summary>
/// 异步连接服务器
/// </summary>
public Task<ITcpClient> ConnectAsync()
public Task<ITcpClient> ConnectAsync(int timeout = 5000)
{
return Task.Run(() =>
{
return this.Connect();
return this.Connect(timeout);
});
}
......@@ -594,11 +611,11 @@ namespace RRQMSocket
throw new ArgumentNullException(nameof(adapter));
}
if (adapter.owner != null)
if (adapter.client != null)
{
throw new RRQMException("此适配器已被其他终端使用,请重新创建对象。");
}
adapter.owner = this;
adapter.client = this;
adapter.ReceivedCallBack = this.PrivateHandleReceivedData;
adapter.SendCallBack = this.SocketSend;
if (this.Config != null)
......@@ -709,6 +726,7 @@ namespace RRQMSocket
}
catch (Exception ex)
{
e.Dispose();
this.BreakOut(ex.Message, false);
}
}
......@@ -955,6 +973,46 @@ namespace RRQMSocket
}
#endregion
#region 异步默认发送
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="buffer"><inheritdoc/></param>
/// <param name="offset"><inheritdoc/></param>
/// <param name="length"><inheritdoc/></param>
/// <exception cref="RRQMNotConnectedException"><inheritdoc/></exception>
/// <exception cref="RRQMOverlengthException"><inheritdoc/></exception>
/// <exception cref="RRQMException"><inheritdoc/></exception>
public void DefaultSendAsync(byte[] buffer, int offset, int length)
{
this.SocketSend(buffer, offset, length, true);
}
/// <summary>
///<inheritdoc/>
/// </summary>
/// <param name="buffer"><inheritdoc/></param>
/// <exception cref="RRQMNotConnectedException"><inheritdoc/></exception>
/// <exception cref="RRQMOverlengthException"><inheritdoc/></exception>
/// <exception cref="RRQMException"><inheritdoc/></exception>
public void DefaultSendAsync(byte[] buffer)
{
this.DefaultSendAsync(buffer, 0, buffer.Length);
}
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="byteBlock"><inheritdoc/></param>
/// <exception cref="RRQMNotConnectedException"><inheritdoc/></exception>
/// <exception cref="RRQMOverlengthException"><inheritdoc/></exception>
/// <exception cref="RRQMException"><inheritdoc/></exception>
public void DefaultSendAsync(ByteBlock byteBlock)
{
this.DefaultSendAsync(byteBlock.Buffer, 0, byteBlock.Len);
}
#endregion
private void LoadSocketAndReadIpPort()
{
if (this.m_mainSocket == null)
......@@ -985,6 +1043,11 @@ namespace RRQMSocket
private void ProcessReceived(SocketAsyncEventArgs e)
{
if (!this.m_online)
{
e.Dispose();
return;
}
if (e.SocketError == SocketError.Success && e.BytesTransferred > 0)
{
ByteBlock byteBlock = (ByteBlock)e.UserToken;
......@@ -1003,11 +1066,13 @@ namespace RRQMSocket
}
catch (Exception ex)
{
e.Dispose();
this.BreakOut(ex.Message, false);
}
}
else
{
e.Dispose();
this.BreakOut("远程终端主动关闭", false);
}
}
......
......@@ -33,8 +33,8 @@ namespace RRQMSocket
/// </summary>
public TcpService()
{
this.iDGenerator = new SnowflakeIDGenerator(4);
this.socketClients = new SocketClientCollection();
this.m_iDGenerator = new SnowflakeIDGenerator(4);
this.m_socketClients = new SocketClientCollection();
this.Container = new Container();
this.PluginsManager = new PluginsManager(this.Container);
this.Container.RegisterTransient<ILog, ConsoleLogger>();
......@@ -42,19 +42,18 @@ namespace RRQMSocket
#region 变量
private readonly SnowflakeIDGenerator iDGenerator;
private int backlog;
private int clearInterval;
private ClearType clearType;
private int maxCount;
private int maxPackageSize;
private NetworkMonitor[] monitors;
private ReceiveType receiveType;
private ServerState serverState;
private RRQMConfig serviceConfig;
private SocketClientCollection socketClients;
private bool usePlugin;
private bool useSsl;
private readonly SnowflakeIDGenerator m_iDGenerator;
private int m_backlog;
private int m_clearInterval;
private ClearType m_clearType;
private int m_maxCount;
private NetworkMonitor[] m_monitors;
private ReceiveType m_receiveType;
private ServerState m_serverState;
private RRQMConfig m_serviceConfig;
private SocketClientCollection m_socketClients;
private bool m_usePlugin;
private bool m_useSsl;
#endregion 变量
......@@ -63,17 +62,17 @@ namespace RRQMSocket
/// <summary>
/// 获取清理无数据交互的SocketClient,默认60。如果不想清除,可使用-1。
/// </summary>
public int ClearInterval => this.clearInterval;
public int ClearInterval => this.m_clearInterval;
/// <summary>
/// 清理类型
/// </summary>
public ClearType ClearType => this.clearType;
public ClearType ClearType => this.m_clearType;
/// <summary>
/// 获取服务器配置
/// </summary>
public override RRQMConfig Config => this.serviceConfig;
public override RRQMConfig Config => this.m_serviceConfig;
/// <summary>
/// <inheritdoc/>
......@@ -83,17 +82,12 @@ namespace RRQMSocket
/// <summary>
/// 最大可连接数
/// </summary>
public int MaxCount => this.maxCount;
public int MaxCount => this.m_maxCount;
/// <summary>
/// <inheritdoc/>
/// </summary>
public override int MaxPackageSize => maxPackageSize;
/// <summary>
/// <inheritdoc/>
/// </summary>
public override NetworkMonitor[] Monitors => this.monitors;
public override NetworkMonitor[] Monitors => this.m_monitors;
/// <summary>
/// <inheritdoc/>
......@@ -108,22 +102,22 @@ namespace RRQMSocket
/// <summary>
/// 服务器状态
/// </summary>
public override ServerState ServerState => this.serverState;
public override ServerState ServerState => this.m_serverState;
/// <summary>
/// 获取当前连接的所有客户端
/// </summary>
public override SocketClientCollection SocketClients => this.socketClients;
public override SocketClientCollection SocketClients => this.m_socketClients;
/// <summary>
/// 是否已启动插件
/// </summary>
public bool UsePlugin => this.usePlugin;
public bool UsePlugin => this.m_usePlugin;
/// <summary>
/// <inheritdoc/>
/// </summary>
public override bool UseSsl => this.useSsl;
public override bool UseSsl => this.m_useSsl;
#endregion 属性
......@@ -227,7 +221,7 @@ namespace RRQMSocket
/// <param name="e"></param>
protected virtual void OnIDChanged(TClient socketClient, RRQMEventArgs e)
{
if (this.usePlugin)
if (this.m_usePlugin)
{
this.PluginsManager.Raise<ITcpPlugin>("OnIDChanged", socketClient, e);
if (e.Handled)
......@@ -327,7 +321,7 @@ namespace RRQMSocket
/// <returns></returns>
public TClient[] GetClients()
{
var clients = this.socketClients.GetClients().ToArray();
var clients = this.m_socketClients.GetClients().ToArray();
TClient[] clients1 = new TClient[clients.Length];
for (int i = 0; i < clients.Length; i++)
......@@ -343,7 +337,7 @@ namespace RRQMSocket
/// <returns></returns>
public string GetDefaultNewID()
{
return this.iDGenerator.NextID().ToString();
return this.m_iDGenerator.NextID().ToString();
}
/// <summary>
......@@ -368,10 +362,10 @@ namespace RRQMSocket
{
return;
}
if (this.socketClients.TryRemove(waitSetID.OldID, out TClient socketClient))
if (this.m_socketClients.TryRemove(waitSetID.OldID, out TClient socketClient))
{
socketClient.id = waitSetID.NewID;
if (this.socketClients.TryAdd(socketClient))
if (this.m_socketClients.TryAdd(socketClient))
{
this.OnIDChanged(socketClient, new RRQMEventArgs());
return;
......@@ -379,7 +373,7 @@ namespace RRQMSocket
else
{
socketClient.id = waitSetID.OldID;
this.socketClients.TryAdd(socketClient);
this.m_socketClients.TryAdd(socketClient);
throw new RRQMException("ID重复");
}
}
......@@ -408,7 +402,7 @@ namespace RRQMSocket
/// <param name="serviceConfig"></param>
public override IService Setup(RRQMConfig serviceConfig)
{
this.serviceConfig = serviceConfig;
this.m_serviceConfig = serviceConfig;
this.LoadConfig(serviceConfig);
return this;
}
......@@ -447,7 +441,7 @@ namespace RRQMSocket
{
throw new RRQMException("IPHosts为空,无法绑定");
}
switch (this.serverState)
switch (this.m_serverState)
{
case ServerState.None:
{
......@@ -469,7 +463,7 @@ namespace RRQMSocket
throw new RRQMException("无法重新利用已释放对象");
}
}
this.serverState = ServerState.Running;
this.m_serverState = ServerState.Running;
return this;
}
......@@ -478,9 +472,9 @@ namespace RRQMSocket
/// </summary>
public override IService Stop()
{
if (this.monitors != null)
if (this.m_monitors != null)
{
foreach (var item in this.monitors)
foreach (var item in this.m_monitors)
{
if (item.Socket != null)
{
......@@ -488,11 +482,11 @@ namespace RRQMSocket
}
}
}
this.monitors = null;
this.m_monitors = null;
this.Clear();
this.serverState = ServerState.Stopped;
this.m_serverState = ServerState.Stopped;
return this;
}
......@@ -505,7 +499,7 @@ namespace RRQMSocket
/// <returns></returns>
public bool TryGetSocketClient(string id, out TClient socketClient)
{
return this.socketClients.TryGetSocketClient(id, out socketClient);
return this.m_socketClients.TryGetSocketClient(id, out socketClient);
}
/// <summary>
......@@ -518,19 +512,19 @@ namespace RRQMSocket
{
if (disposing)
{
if (this.monitors != null)
if (this.m_monitors != null)
{
foreach (var item in this.monitors)
foreach (var item in this.m_monitors)
{
if (item.Socket != null)
{
item.Socket.Dispose();
}
}
this.monitors = null;
this.m_monitors = null;
}
this.Clear();
this.serverState = ServerState.Disposed;
this.m_serverState = ServerState.Disposed;
this.PluginsManager.Clear();
}
this.disposedValue = true;
......@@ -562,18 +556,21 @@ namespace RRQMSocket
{
throw new RRQMException("线程数量必须大于0");
}
this.maxPackageSize = config.GetValue<int>(RRQMConfigExtensions.MaxPackageSizeProperty);
this.usePlugin = config.IsUsePlugin;
this.maxCount = config.GetValue<int>(RRQMConfigExtensions.MaxCountProperty);
this.clearInterval = config.GetValue<int>(RRQMConfigExtensions.ClearIntervalProperty);
this.backlog = config.GetValue<int>(RRQMConfigExtensions.BacklogProperty);
this.logger = this.Container.Resolve<ILog>();
this.m_usePlugin = config.IsUsePlugin;
this.m_maxCount = config.GetValue<int>(RRQMConfigExtensions.MaxCountProperty);
this.m_clearInterval = config.GetValue<int>(RRQMConfigExtensions.ClearIntervalProperty);
this.m_backlog = config.GetValue<int>(RRQMConfigExtensions.BacklogProperty);
this.BufferLength = config.GetValue<int>(RRQMConfig.BufferLengthProperty);
this.clearType = config.GetValue<ClearType>(RRQMConfigExtensions.ClearTypeProperty);
this.receiveType = config.ReceiveType;
this.m_clearType = config.GetValue<ClearType>(RRQMConfigExtensions.ClearTypeProperty);
this.m_receiveType = config.ReceiveType;
if (this.Logger==null)
{
this.Logger = this.Container.Resolve<ILog>();
}
if (config.GetValue(RRQMConfigExtensions.SslOptionProperty) != null)
{
this.useSsl = true;
this.m_useSsl = true;
}
}
......@@ -617,26 +614,26 @@ namespace RRQMSocket
socket.SendBufferSize = this.BufferLength;
this.PreviewBind(socket);
socket.Bind(iPHost.EndPoint);
socket.Listen(this.backlog);
socket.Listen(this.m_backlog);
networkMonitors.Add(new NetworkMonitor(iPHost, socket));
}
catch (Exception ex)
{
this.logger.Debug(LogType.Error, this, $"在监听{iPHost.ToString()}时发送错误。", ex);
this.Logger.Debug(LogType.Error, this, $"在监听{iPHost.ToString()}时发送错误。", ex);
}
}
if (networkMonitors.Count > 0)
{
this.monitors = networkMonitors.ToArray();
this.m_monitors = networkMonitors.ToArray();
}
else
{
throw new RRQMException("监听地址全都不可用。");
}
foreach (var networkMonitor in this.monitors)
foreach (var networkMonitor in this.m_monitors)
{
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
e.UserToken = networkMonitor.Socket;
......@@ -664,9 +661,9 @@ namespace RRQMSocket
{
if (this.SocketClients.TryGetSocketClient(token, out TClient client))
{
if (this.clearInterval > 0)
if (this.m_clearInterval > 0)
{
client.GetTimeout((int)(this.clearInterval / 1000.0), tick);
client.GetTimeout((int)(this.m_clearInterval / 1000.0), tick);
}
}
}
......@@ -682,7 +679,7 @@ namespace RRQMSocket
Socket socket = e.AcceptSocket;
socket.ReceiveBufferSize = this.BufferLength;
socket.SendBufferSize = this.BufferLength;
if (this.SocketClients.Count > this.maxCount)
if (this.SocketClients.Count > this.m_maxCount)
{
this.Logger.Debug(LogType.Warning, this, "连接客户端数量已达到设定最大值");
socket.Close();
......@@ -713,21 +710,21 @@ namespace RRQMSocket
{
try
{
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, this.Config.GetValue<bool>(RRQMConfigExtensions.NoDelayProperty));
TClient client = this.GetClientInstence();
client.maxPackageSize = this.maxPackageSize;
client.usePlugin = this.usePlugin;
client.usePlugin = this.m_usePlugin;
client.pluginsManager = this.PluginsManager;
client.lastTick = DateTime.Now.Ticks;
client.serviceConfig = this.serviceConfig;
client.config = this.m_serviceConfig;
client.service = this;
if (client.Logger == null)
{
client.Logger = this.Logger;
client.Logger = this.Container.Resolve<ILog>();
}
client.ClearType = this.clearType;
client.receiveType = this.receiveType;
client.ClearType = this.m_clearType;
client.receiveType = this.m_receiveType;
client.BufferLength = this.BufferLength;
client.useSsl = this.useSsl;
client.useSsl = this.m_useSsl;
client.LoadSocketAndReadIpPort(socket);
ClientOperationEventArgs args = new ClientOperationEventArgs();
args.ID = this.GetDefaultNewID();
......
......@@ -63,11 +63,6 @@ namespace RRQMSocket
/// </summary>
public abstract IContainer Container { get; set; }
/// <summary>
/// <inheritdoc/>
/// </summary>
public abstract int MaxPackageSize { get; }
/// <summary>
/// <inheritdoc/>
/// </summary>
......
......@@ -91,9 +91,9 @@ namespace RRQMSocket
/// <exception cref="RRQMException"></exception>
/// <exception cref="RRQMTokenVerifyException"></exception>
/// <exception cref="TimeoutException"></exception>
public override ITcpClient Connect()
public override ITcpClient Connect(int timeout = 5000)
{
return this.Connect("rrqm");
return this.Connect("rrqm",timeout:timeout);
}
private bool isHandshaked;
......@@ -140,11 +140,11 @@ namespace RRQMSocket
/// <exception cref="RRQMException"></exception>
/// <exception cref="RRQMTokenVerifyException"></exception>
/// <exception cref="TimeoutException"></exception>
public virtual ITcpClient Connect(string verifyToken, CancellationToken token = default)
public virtual ITcpClient Connect(string verifyToken, CancellationToken token = default, int timeout = 5000)
{
if (!this.Online)
{
base.Connect();
base.Connect(timeout);
}
WaitVerify waitVerify = new WaitVerify()
......@@ -158,7 +158,7 @@ namespace RRQMSocket
base.Send(data, 0, data.Length);
try
{
switch (waitData.Wait(1000 * 10))
switch (waitData.Wait(timeout))
{
case WaitDataStatus.SetRunning:
{
......
......@@ -386,7 +386,10 @@ namespace RRQMSocket
{
throw new RRQMException("配置文件为空");
}
this.logger = this.Container.Resolve<ILog>();
if (this.Logger == null)
{
this.Logger = this.Container.Resolve<ILog>();
}
this.remoteIPHost = config.GetValue<IPHost>(RRQMConfigExtensions.RemoteIPHostProperty);
this.BufferLength = config.BufferLength;
this.usePlugin = config.IsUsePlugin;
......@@ -717,7 +720,7 @@ namespace RRQMSocket
}
catch (Exception ex)
{
this.logger.Debug(LogType.Error, this, ex.Message, ex);
this.Logger.Debug(LogType.Error, this, ex.Message, ex);
}
}
}
......@@ -787,6 +790,72 @@ namespace RRQMSocket
#endregion DefaultSend
#region DefaultSendAsync
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="buffer"></param>
/// <param name="offset"></param>
/// <param name="length"></param>
public void DefaultSendAsync(byte[] buffer, int offset, int length)
{
this.SocketSend(this.remoteIPHost.EndPoint, buffer, offset, length, true);
}
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="buffer"></param>
public void DefaultSendAsync(byte[] buffer)
{
this.DefaultSendAsync(buffer, 0, buffer.Length);
}
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="byteBlock"></param>
public void DefaultSendAsync(ByteBlock byteBlock)
{
this.DefaultSendAsync(byteBlock.Buffer, 0, byteBlock.Len);
}
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="endPoint"></param>
/// <param name="buffer"></param>
/// <param name="offset"></param>
/// <param name="length"></param>
public void DefaultSendAsync(EndPoint endPoint, byte[] buffer, int offset, int length)
{
this.SocketSend(endPoint, buffer, offset, length, true);
}
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="endPoint"></param>
/// <param name="buffer"></param>
public void DefaultSendAsync(EndPoint endPoint, byte[] buffer)
{
this.DefaultSendAsync(endPoint, buffer, 0, buffer.Length);
}
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="endPoint"></param>
/// <param name="byteBlock"></param>
public void DefaultSendAsync(EndPoint endPoint, ByteBlock byteBlock)
{
this.DefaultSendAsync(endPoint, byteBlock.Buffer, 0, byteBlock.Len);
}
#endregion DefaultSend
#region 组合发送
/// <summary>
......
......@@ -26,7 +26,7 @@ namespace RRQMSocket
/// </summary>
public class DataAdapterTester : IDisposable
{
private readonly IntelligentDataQueue<TransferByte> asyncBytes;
private readonly IntelligentDataQueue<QueueDataBytes> asyncBytes;
private readonly Thread sendThread;
private DataHandlingAdapter adapter;
private int bufferLength;
......@@ -39,7 +39,7 @@ namespace RRQMSocket
private DataAdapterTester()
{
this.asyncBytes = new IntelligentDataQueue<TransferByte>(1024 * 1024 * 10);
this.asyncBytes = new IntelligentDataQueue<QueueDataBytes>(1024 * 1024 * 10);
this.sendThread = new Thread(this.BeginSend);
this.sendThread.IsBackground = true;
this.sendThread.Name = "DataAdapterTesterThread";
......@@ -150,7 +150,7 @@ namespace RRQMSocket
private void SendCallback(byte[] buffer, int offset, int length, bool isAsync)
{
TransferByte asyncByte = new TransferByte(new byte[length], 0, length);
QueueDataBytes asyncByte = new QueueDataBytes(new byte[length], 0, length);
Array.Copy(buffer, offset, asyncByte.Buffer, 0, length);
this.asyncBytes.Enqueue(asyncByte);
}
......@@ -161,7 +161,7 @@ namespace RRQMSocket
ByteBlock block = null;
while (true)
{
if (this.asyncBytes.TryDequeue(out TransferByte asyncByte))
if (this.asyncBytes.TryDequeue(out QueueDataBytes asyncByte))
{
if (block == null)
{
......@@ -208,7 +208,7 @@ namespace RRQMSocket
return true;
}
private ByteBlock Write(TransferByte transferByte, ref int offset)
private ByteBlock Write(QueueDataBytes transferByte, ref int offset)
{
ByteBlock block = BytePool.GetByteBlock(this.bufferLength, true);
int len = Math.Min(transferByte.Length - offset, this.bufferLength);
......@@ -224,7 +224,7 @@ namespace RRQMSocket
/// </summary>
public class UdpDataAdapterTester : IDisposable
{
private readonly IntelligentDataQueue<TransferByte> asyncBytes;
private readonly IntelligentDataQueue<QueueDataBytes> asyncBytes;
private UdpDataHandlingAdapter adapter;
private int count;
private bool dispose;
......@@ -235,7 +235,7 @@ namespace RRQMSocket
private UdpDataAdapterTester(int multiThread)
{
this.asyncBytes = new IntelligentDataQueue<TransferByte>(1024 * 1024 * 10);
this.asyncBytes = new IntelligentDataQueue<QueueDataBytes>(1024 * 1024 * 10);
for (int i = 0; i < multiThread; i++)
{
Task.Run(this.BeginSend);
......@@ -345,7 +345,7 @@ namespace RRQMSocket
private void SendCallback(EndPoint endPoint,byte[] buffer, int offset, int length, bool isAsync)
{
TransferByte asyncByte = new TransferByte(new byte[length], 0, length);
QueueDataBytes asyncByte = new QueueDataBytes(new byte[length], 0, length);
Array.Copy(buffer, offset, asyncByte.Buffer, 0, length);
this.asyncBytes.Enqueue(asyncByte);
}
......@@ -354,7 +354,7 @@ namespace RRQMSocket
{
byteBlocks = new List<ByteBlock>();
while (this.asyncBytes.TryDequeue(out TransferByte asyncByte))
while (this.asyncBytes.TryDequeue(out QueueDataBytes asyncByte))
{
ByteBlock block = new ByteBlock(asyncByte.Length);
block.Write(asyncByte.Buffer,asyncByte.Offset,asyncByte.Length);
......
......@@ -23,9 +23,9 @@ namespace RRQMSocket
/// </summary>
public abstract class DataHandlingAdapter
{
internal ITcpClientBase owner;
internal ITcpClientBase client;
private int maxPackageSize = 1024 * 1024;
private int m_maxPackageSize = 1024 * 1024;
/// <summary>
/// 拼接发送
......@@ -37,14 +37,14 @@ namespace RRQMSocket
/// </summary>
public int MaxPackageSize
{
get { return maxPackageSize; }
set { maxPackageSize = value; }
get { return m_maxPackageSize; }
set { m_maxPackageSize = value; }
}
/// <summary>
/// 适配器拥有者。
/// </summary>
public ITcpClientBase Owner => this.owner;
public ITcpClientBase Client => this.client;
/// <summary>
/// 当接收数据处理完成后,回调该函数执行接收
......@@ -128,9 +128,9 @@ namespace RRQMSocket
{
this.Reset();
}
if (log && this.owner != null && this.owner.Logger != null)
if (log && this.client != null && this.client.Logger != null)
{
this.owner.Logger.Error(error);
this.client.Logger.Error(error);
}
}
......
......@@ -23,7 +23,7 @@ namespace RRQMSocket
/// <summary>
/// 默认
/// </summary>
Default,
Default=0,
/// <summary>
/// 继续下移
......
......@@ -237,13 +237,13 @@ namespace RRQMSocket
/// 连接服务器
/// </summary>
/// <exception cref="RRQMException"></exception>
ITcpClient Connect();
ITcpClient Connect(int timeout = 5000);
/// <summary>
/// 异步连接服务器
/// </summary>
/// <exception cref="RRQMException"></exception>
Task<ITcpClient> ConnectAsync();
Task<ITcpClient> ConnectAsync(int timeout = 5000);
/// <summary>
/// 配置服务器
......@@ -362,7 +362,11 @@ namespace RRQMSocket
/// <exception cref="RRQMException"></exception>
/// <exception cref="RRQMTokenVerifyException"></exception>
/// <exception cref="TimeoutException"></exception>
ITcpClient Connect(string verifyToken, CancellationToken token = default);
/// <param name="verifyToken"></param>
/// <param name="token"></param>
/// <param name="timeout"></param>
/// <returns></returns>
ITcpClient Connect(string verifyToken, CancellationToken token = default, int timeout = 5000);
}
/// <summary>
......
......@@ -116,6 +116,37 @@ namespace RRQMSocket
/// <exception cref="RRQMException">其他异常</exception>
void DefaultSend(ByteBlock byteBlock);
#endregion
#region 默认发送
/// <summary>
/// 绕过适配器,直接发送字节流
/// </summary>
/// <param name="buffer">数据缓存区</param>
/// <param name="offset">偏移量</param>
/// <param name="length">数据长度</param>
/// <exception cref="RRQMNotConnectedException">客户端没有连接</exception>
/// <exception cref="RRQMOverlengthException">发送数据超长</exception>
/// <exception cref="RRQMException">其他异常</exception>
void DefaultSendAsync(byte[] buffer, int offset, int length);
/// <summary>
/// 绕过适配器,直接发送字节流
/// </summary>
/// <param name="buffer">数据缓存区</param>
/// <exception cref="RRQMNotConnectedException">客户端没有连接</exception>
/// <exception cref="RRQMOverlengthException">发送数据超长</exception>
/// <exception cref="RRQMException">其他异常</exception>
void DefaultSendAsync(byte[] buffer);
/// <summary>
/// 绕过适配器,直接发送字节流
/// </summary>
/// <param name="byteBlock">数据块载体</param>
/// <exception cref="RRQMNotConnectedException">客户端没有连接</exception>
/// <exception cref="RRQMOverlengthException">发送数据超长</exception>
/// <exception cref="RRQMException">其他异常</exception>
void DefaultSendAsync(ByteBlock byteBlock);
#endregion
}
/// <summary>
......@@ -156,6 +187,40 @@ namespace RRQMSocket
/// <exception cref="RRQMException">其他异常</exception>
void DefaultSend(EndPoint endPoint, ByteBlock byteBlock);
#endregion
#region 默认发送
/// <summary>
/// 绕过适配器,直接发送字节流
/// </summary>
/// <param name="endPoint">目的终结点</param>
/// <param name="buffer">数据缓存区</param>
/// <param name="offset">偏移量</param>
/// <param name="length">数据长度</param>
/// <exception cref="RRQMNotConnectedException">客户端没有连接</exception>
/// <exception cref="RRQMOverlengthException">发送数据超长</exception>
/// <exception cref="RRQMException">其他异常</exception>
void DefaultSendAsync(EndPoint endPoint, byte[] buffer, int offset, int length);
/// <summary>
/// 绕过适配器,直接发送字节流
/// </summary>
/// <param name="endPoint">目的终结点</param>
/// <param name="buffer">数据缓存区</param>
/// <exception cref="RRQMNotConnectedException">客户端没有连接</exception>
/// <exception cref="RRQMOverlengthException">发送数据超长</exception>
/// <exception cref="RRQMException">其他异常</exception>
void DefaultSendAsync(EndPoint endPoint, byte[] buffer);
/// <summary>
/// 绕过适配器,直接发送字节流
/// </summary>
/// <param name="endPoint">目的终结点</param>
/// <param name="byteBlock">数据块载体</param>
/// <exception cref="RRQMNotConnectedException">客户端没有连接</exception>
/// <exception cref="RRQMOverlengthException">发送数据超长</exception>
/// <exception cref="RRQMException">其他异常</exception>
void DefaultSendAsync(EndPoint endPoint, ByteBlock byteBlock);
#endregion
}
/// <summary>
......
......@@ -41,11 +41,6 @@ namespace RRQMSocket
/// </summary>
NetworkMonitor[] Monitors { get; }
/// <summary>
/// 适配器能接收的最大数据包长度。
/// </summary>
int MaxPackageSize { get; }
/// <summary>
/// 重新设置ID
/// </summary>
......
......@@ -4,7 +4,7 @@
<ApplicationIcon>RRQM.ico</ApplicationIcon>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>RRQM.pfx</AssemblyOriginatorKeyFile>
<Version>8.0.4</Version>
<Version>8.0.5</Version>
<LangVersion>8.0</LangVersion>
<Company>若汝棋茗</Company>
<Copyright>Copyright © 2022 若汝棋茗</Copyright>
......@@ -84,7 +84,7 @@ DEMO:https://gitee.com/RRQM_Home/RRQMBox</Description>
</ItemGroup>
<ItemGroup>
<PackageReference Include="RRQMCore" Version="*" />
<PackageReference Include="RRQMCore" Version="8.0.5" />
</ItemGroup>
</Project>
......@@ -88,7 +88,6 @@ namespace RRQMSocket
/// </summary>
public CancellationToken Token { get; set; }
/// <summary>
/// 从上次获取到此次获得的速度
/// </summary>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册