You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
388 lines
13 KiB
388 lines
13 KiB
//------------------------------------------------------------------------------ |
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
|
// Github源代码仓库:https://github.com/RRQM |
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
|
// 交流QQ群:234762506 |
|
// 感谢您的下载和使用 |
|
//------------------------------------------------------------------------------ |
|
//------------------------------------------------------------------------------ |
|
using System; |
|
using System.Collections.Concurrent; |
|
using System.Collections.Generic; |
|
using System.Net; |
|
using System.Threading; |
|
using TouchSocket.Core; |
|
|
|
namespace TouchSocket.Sockets |
|
{ |
|
/// <summary> |
|
/// UDP数据帧 |
|
/// </summary> |
|
public struct UdpFrame |
|
{ |
|
/// <summary> |
|
/// Crc校验 |
|
/// </summary> |
|
public byte[] Crc { get; set; } |
|
|
|
/// <summary> |
|
/// 数据 |
|
/// </summary> |
|
public byte[] Data { get; set; } |
|
|
|
/// <summary> |
|
/// 是否为终结帧 |
|
/// </summary> |
|
public bool FIN { get; set; } |
|
|
|
/// <summary> |
|
/// 数据ID |
|
/// </summary> |
|
public long ID { get; set; } |
|
|
|
/// <summary> |
|
/// 帧序号 |
|
/// </summary> |
|
public ushort SN { get; set; } |
|
|
|
/// <summary> |
|
/// 解析 |
|
/// </summary> |
|
/// <param name="buffer"></param> |
|
/// <param name="offset"></param> |
|
/// <param name="length"></param> |
|
/// <returns></returns> |
|
public bool Parse(byte[] buffer, int offset, int length) |
|
{ |
|
if (length > 11) |
|
{ |
|
this.ID = TouchSocketBitConverter.Default.ToInt64(buffer, offset); |
|
this.SN = TouchSocketBitConverter.Default.ToUInt16(buffer, 8 + offset); |
|
this.FIN = buffer[10 + offset].GetBit(7) == 1; |
|
if (this.FIN) |
|
{ |
|
if (length > 13) |
|
{ |
|
this.Data = new byte[length - 13]; |
|
} |
|
else |
|
{ |
|
this.Data = new byte[0]; |
|
} |
|
this.Crc = new byte[2] { buffer[length - 2], buffer[length - 1] }; |
|
} |
|
else |
|
{ |
|
this.Data = new byte[length - 11]; |
|
} |
|
|
|
Array.Copy(buffer, 11, this.Data, 0, this.Data.Length); |
|
return true; |
|
} |
|
return false; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// UDP数据包 |
|
/// </summary> |
|
[System.Diagnostics.DebuggerDisplay("Count={Count}")] |
|
public class UdpPackage |
|
{ |
|
private readonly ConcurrentQueue<UdpFrame> m_frames; |
|
private readonly Timer m_timer; |
|
private int m_count; |
|
private int m_length; |
|
|
|
private int m_mtu; |
|
|
|
/// <summary> |
|
/// 构造函数 |
|
/// </summary> |
|
/// <param name="id"></param> |
|
/// <param name="timeout"></param> |
|
/// <param name="revStore"></param> |
|
public UdpPackage(long id, int timeout, ConcurrentDictionary<long, UdpPackage> revStore) |
|
{ |
|
this.ID = id; |
|
this.m_frames = new ConcurrentQueue<UdpFrame>(); |
|
this.m_timer = new Timer((o) => |
|
{ |
|
if (revStore.TryRemove(this.ID, out UdpPackage udpPackage)) |
|
{ |
|
udpPackage.m_frames.Clear(); |
|
} |
|
}, null, timeout, Timeout.Infinite); |
|
} |
|
|
|
/// <summary> |
|
/// 当前长度 |
|
/// </summary> |
|
public int Count => this.m_count; |
|
|
|
/// <summary> |
|
/// Crc |
|
/// </summary> |
|
public byte[] Crc { get; private set; } |
|
|
|
/// <summary> |
|
/// 包唯一标识 |
|
/// </summary> |
|
public long ID { get; } |
|
|
|
/// <summary> |
|
/// 是否已完成 |
|
/// </summary> |
|
public bool IsComplated => this.TotalCount > 0 ? (this.TotalCount == this.m_count ? true : false) : false; |
|
|
|
/// <summary> |
|
/// 当前数据长度 |
|
/// </summary> |
|
public int Length => this.m_length; |
|
|
|
/// <summary> |
|
/// MTU |
|
/// </summary> |
|
public int MTU => this.m_mtu + 11; |
|
|
|
/// <summary> |
|
/// 总长度,在收到最后一帧之前,为-1。 |
|
/// </summary> |
|
public int TotalCount { get; private set; } = -1; |
|
|
|
/// <summary> |
|
/// 添加帧 |
|
/// </summary> |
|
/// <param name="frame"></param> |
|
public void Add(UdpFrame frame) |
|
{ |
|
Interlocked.Increment(ref this.m_count); |
|
|
|
if (frame.FIN) |
|
{ |
|
this.TotalCount = frame.SN + 1; |
|
this.Crc = frame.Crc; |
|
} |
|
Interlocked.Add(ref this.m_length, frame.Data.Length); |
|
if (frame.SN == 0) |
|
{ |
|
this.m_mtu = frame.Data.Length; |
|
} |
|
this.m_frames.Enqueue(frame); |
|
} |
|
|
|
/// <summary> |
|
/// 获得数据 |
|
/// </summary> |
|
/// <param name="byteBlock"></param> |
|
/// <returns></returns> |
|
public bool TryGetData(ByteBlock byteBlock) |
|
{ |
|
while (this.m_frames.TryDequeue(out UdpFrame frame)) |
|
{ |
|
byteBlock.Pos = frame.SN * this.m_mtu; |
|
byteBlock.Write(frame.Data); |
|
} |
|
|
|
if (byteBlock.Len != this.Length) |
|
{ |
|
return false; |
|
} |
|
byte[] crc = TouchSocket.Core.Crc.Crc16(byteBlock.Buffer, 0, byteBlock.Len); |
|
if (crc[0] != this.Crc[0] || crc[1] != this.Crc[1]) |
|
{ |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// UDP数据包的适配器 |
|
/// </summary> |
|
public class UdpPackageAdapter : UdpDataHandlingAdapter |
|
{ |
|
private readonly SnowflakeIDGenerator m_iDGenerator; |
|
private readonly ConcurrentDictionary<long, UdpPackage> revStore; |
|
private int m_mtu = 1472; |
|
|
|
/// <summary> |
|
/// 构造函数 |
|
/// </summary> |
|
public UdpPackageAdapter() |
|
{ |
|
this.revStore = new ConcurrentDictionary<long, UdpPackage>(); |
|
this.m_iDGenerator = new SnowflakeIDGenerator(4); |
|
} |
|
|
|
/// <summary> |
|
/// <inheritdoc/> |
|
/// </summary> |
|
public override bool CanSendRequestInfo => false; |
|
|
|
/// <summary> |
|
/// <inheritdoc/> |
|
/// </summary> |
|
public override bool CanSplicingSend => true; |
|
|
|
/// <summary> |
|
/// 最大传输单元 |
|
/// </summary> |
|
public int MTU |
|
{ |
|
get => this.m_mtu + 11; |
|
set => this.m_mtu = value > 11 ? value : 1472; |
|
} |
|
|
|
/// <summary> |
|
/// 接收超时时间,默认5000ms |
|
/// </summary> |
|
public int Timeout { get; set; } = 5000; |
|
|
|
/// <summary> |
|
/// <inheritdoc/> |
|
/// </summary> |
|
/// <param name="remoteEndPoint"></param> |
|
/// <param name="byteBlock"></param> |
|
protected override void PreviewReceived(EndPoint remoteEndPoint, ByteBlock byteBlock) |
|
{ |
|
var udpFrame = new UdpFrame(); |
|
if (udpFrame.Parse(byteBlock.Buffer, 0, byteBlock.Len)) |
|
{ |
|
UdpPackage udpPackage = this.revStore.GetOrAdd(udpFrame.ID, (i) => new UdpPackage(i, this.Timeout, this.revStore)); |
|
udpPackage.Add(udpFrame); |
|
if (udpPackage.Length > this.MaxPackageSize) |
|
{ |
|
this.revStore.TryRemove(udpPackage.ID, out _); |
|
this.m_owner?.Logger.Error("数据长度大于设定的最大值。"); |
|
return; |
|
} |
|
if (udpPackage.IsComplated) |
|
{ |
|
if (this.revStore.TryRemove(udpPackage.ID, out _)) |
|
{ |
|
using (var block = new ByteBlock(udpPackage.Length)) |
|
{ |
|
if (udpPackage.TryGetData(block)) |
|
{ |
|
this.GoReceived(remoteEndPoint, block, null); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// <inheritdoc/> |
|
/// </summary> |
|
/// <param name="endPoint"></param> |
|
/// <param name="buffer"></param> |
|
/// <param name="offset"></param> |
|
/// <param name="length"></param> |
|
protected override void PreviewSend(EndPoint endPoint, byte[] buffer, int offset, int length) |
|
{ |
|
if (length > this.MaxPackageSize) |
|
{ |
|
throw new OverlengthException("发送数据大于设定值,相同解析器可能无法收到有效数据,已终止发送"); |
|
} |
|
long id = this.m_iDGenerator.NextID(); |
|
int off = 0; |
|
int surLen = length; |
|
int freeRoom = this.m_mtu - 11; |
|
ushort sn = 0; |
|
/*|********|**|*|n|*/ |
|
/*|********|**|*|**|*/ |
|
while (surLen > 0) |
|
{ |
|
byte[] data = new byte[this.m_mtu]; |
|
Buffer.BlockCopy(TouchSocketBitConverter.Default.GetBytes(id), 0, data, 0, 8); |
|
Buffer.BlockCopy(TouchSocketBitConverter.Default.GetBytes(sn++), 0, data, 8, 2); |
|
if (surLen > freeRoom)//有余 |
|
{ |
|
Buffer.BlockCopy(buffer, off, data, 11, freeRoom); |
|
off += freeRoom; |
|
surLen -= freeRoom; |
|
this.GoSend(endPoint, data, 0, this.m_mtu); |
|
} |
|
else if (surLen + 2 <= freeRoom)//结束且能容纳Crc |
|
{ |
|
byte flag = 0; |
|
data[10] = flag.SetBit(7, 1);//设置终结帧 |
|
|
|
Buffer.BlockCopy(buffer, off, data, 11, surLen); |
|
Buffer.BlockCopy(Crc.Crc16(buffer, offset, length), 0, data, 11 + surLen, 2); |
|
|
|
this.GoSend(endPoint, data, 0, surLen + 11 + 2); |
|
|
|
off += surLen; |
|
surLen -= surLen; |
|
} |
|
else//结束但不能容纳Crc |
|
{ |
|
Buffer.BlockCopy(buffer, off, data, 11, surLen); |
|
this.GoSend(endPoint, data, 0, surLen + 11); |
|
off += surLen; |
|
surLen -= surLen; |
|
|
|
byte[] finData = new byte[13]; |
|
Buffer.BlockCopy(TouchSocketBitConverter.Default.GetBytes(id), 0, finData, 0, 8); |
|
Buffer.BlockCopy(TouchSocketBitConverter.Default.GetBytes(sn++), 0, finData, 8, 2); |
|
byte flag = 0; |
|
finData[10] = flag.SetBit(7, 1); |
|
Buffer.BlockCopy(Crc.Crc16(buffer, offset, length), 0, finData, 11, 2); |
|
this.GoSend(endPoint, finData, 0, finData.Length); |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// <inheritdoc/> |
|
/// </summary> |
|
/// <param name="endPoint"></param> |
|
/// <param name="transferBytes"></param> |
|
protected override void PreviewSend(EndPoint endPoint, IList<ArraySegment<byte>> transferBytes) |
|
{ |
|
int length = 0; |
|
foreach (ArraySegment<byte> item in transferBytes) |
|
{ |
|
length += item.Count; |
|
} |
|
|
|
if (length > this.MaxPackageSize) |
|
{ |
|
throw new OverlengthException("发送数据大于设定值,相同解析器可能无法收到有效数据,已终止发送"); |
|
} |
|
|
|
using (var byteBlock = new ByteBlock(length)) |
|
{ |
|
foreach (ArraySegment<byte> item in transferBytes) |
|
{ |
|
byteBlock.Write(item.Array, item.Offset, item.Count); |
|
} |
|
this.PreviewSend(endPoint, byteBlock.Buffer, 0, byteBlock.Len); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// <inheritdoc/> |
|
/// </summary> |
|
/// <param name="requestInfo"></param> |
|
protected override void PreviewSend(IRequestInfo requestInfo) |
|
{ |
|
throw new NotImplementedException(); |
|
} |
|
|
|
/// <summary> |
|
/// <inheritdoc/> |
|
/// </summary> |
|
protected override void Reset() |
|
{ |
|
} |
|
} |
|
} |