using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using NewLife.Data;
using NewLife.Log;
using NewLife.Messaging;
using NewLife.Model;
namespace NewLife.Net;
/// <summary>Udp会è¯</summary>
/// <remarks>
/// <para>仅用于æœåŠ¡ç«¯ä¸ŽæŸä¸€å›ºå®šè¿œç¨‹åœ°å€é€šä¿¡ã€‚</para>
/// <para>特性:</para>
/// <list type="bullet">
/// <item>绑定到固定的远程地å€</item>
/// <item>共享UdpServer的底层Socket</item>
/// <item>䏿”¯æŒä¼šè¯çº§æ‹‰å–:Socket 为多会è¯å…±äº«ï¼ŒæŒ‰ä¼šè¯è¿‡æ»¤æ‹‰å–会丢弃其他会è¯çš„æ•°æ®æŠ¥ï¼Œ<see cref="Receive()"/> 与 <see cref="ReceiveAsync(CancellationToken)"/> 直接抛 <see cref="NotSupportedException"/>;接收请订阅 <see cref="Received"/> 事件,拉å–请使用 <see cref="UdpServer"/> 继承自 SessionBase çš„ Receive/ReceiveAsync(需 AutoReceive=false)</item>
/// <item>收到空数æ®åŒ…时自动结æŸä¼šè¯ã€‚åˆ¤å®šä»¥æ”¶åˆ°çš„åŽŸå§‹æ•°æ®æŠ¥ä¸ºå‡†ï¼Œåœ¨è¿›å…¥äº‹ä»¶é“¾ä¹‹å‰å®šä¸‹ï¼›ä¸šåŠ¡åœ¨äº‹ä»¶å†…é‡Šæ”¾æˆ–ç½®ç©º <see cref="ReceivedEventArgs.Packet"/> ä¸å½±å“判定,è¦ä¸»åŠ¨ç»“æŸä¼šè¯è¯· <see cref="DisposeBase.Dispose()"/></item>
/// </list>
/// </remarks>
public class UdpSession : DisposeBase, ISocketSession, ITransport, ILogFeature
{
#region 属性
/// <summary>会è¯ç¼–å·</summary>
/// <remarks>用于在多会è¯çŽ¯å¢ƒä¸å”¯ä¸€æ ‡è¯†å½“å‰ä¼šè¯</remarks>
public Int32 ID { get; set; }
/// <summary>åç§°</summary>
/// <remarks>主è¦ç”¨äºŽæ—¥å¿—输出,默认继承自æœåС噍åç§°</remarks>
public String Name { get; set; }
/// <summary>æœåС噍</summary>
/// <remarks>所属的UdpServer实例</remarks>
public UdpServer Server { get; set; }
/// <summary>底层Socket</summary>
/// <remarks>返回UdpServer的Socket</remarks>
Socket? ISocket.Client => Server?.Client;
/// <summary>本地地å€</summary>
/// <remarks>接收数æ®çš„æœ¬åœ°ç½‘络地å€</remarks>
public NetUri Local { get; set; }
/// <summary>端å£</summary>
/// <remarks>本地端å£å·</remarks>
public Int32 Port { get => Local.Port; set => Local.Port = value; }
/// <summary>远程地å€</summary>
/// <remarks>é€šä¿¡çš„ç›®æ ‡è¿œç¨‹åœ°å€</remarks>
public NetUri Remote { get; set; }
/// <summary>超时时间(毫秒)</summary>
/// <remarks>仅为 <see cref="ITransport"/> 契约ä¿ç•™ï¼Œä¸å‚ä¸Žä¼šè¯æ”¶å‘超时;接收超时由共享 Socket 的所属æœåС噍䏋å‘——<see cref="UdpServer"/> çš„ Timeout(默认3000ms)在打开时写入</remarks>
public Int32 Timeout { get; set; }
/// <summary>å议编解ç 器。éžç©ºæ—¶ä¸ºå议模å¼ï¼šæ•°æ®æŠ¥æŒ‰å®Œæ•´å¸§å®šç•Œå¹¶æž„é€ æ¶ˆæ¯åˆ†å‘ï¼ˆæ— ç²˜åŒ…/åŠåŒ…处ç†ï¼Œæ— 需泵任务)</summary>
public IMessageCodec? Protocol { get; set; }
/// <summary>最大并å‘å¤„ç†æ•°ã€‚å议模å¼ä¸‹æ¶ˆæ¯å¤„ç†å¹¶å‘åº¦ï¼ˆéšæœåС噍䏋å‘):1=串行(默认),大于1=并行派å‘(兼作并å‘上é™ï¼‰</summary>
/// <remarks>并行下åŒä¸€ä¼šè¯çš„多个消æ¯å¤„ç†é¡ºåºä¸å®šï¼ˆSRMP 按åºåˆ—å·é…å¯¹ï¼‰ï¼›å¹¶è¡Œè¦æ±‚业务处ç†å™¨çº¿ç¨‹å®‰å…¨ã€‚</remarks>
public Int32 MaxConcurrency { get; set; } = 1;
/// <summary>请求-å“应匹é…队列。å议模å¼ä¸‹ç‰å¾…å“应时使用,首次ç‰å¾…æ—¶è‡ªåŠ¨åˆ›å»ºï¼Œå¯æ³¨å…¥å…±äº«æˆ–自定义实现</summary>
/// <remarks>与æµå¼ä¼šè¯å…±ç”¨åŒä¸€åŒ¹é…å®žçŽ°ï¼šæ•°æ®æŠ¥æ¯åŒ…å³å®Œæ•´å¸§ï¼Œäº¤ä»˜æ—¶ç›´æŽ¥é…å¯¹ï¼Œæ— ç²˜åŒ…å¤„ç†</remarks>
public IMatchQueue? MatchQueue
{
get => _matcher.Queue;
set => _matcher.Queue = value;
}
/// <summary>请求-å“应匹é…ç‰å¾…超时(毫秒)。默认30_000</summary>
public Int32 MatchTimeout
{
get => _matcher.Timeout;
set => _matcher.Timeout = value;
}
/// <summary>å“应匹é…器。与æµå¼ä¼šè¯å…±ç”¨ï¼šè¯·æ±‚-å“应é…对在报文交付阶段完æˆ</summary>
private readonly ResponseMatcher _matcher = new();
/// <summary>SocketæœåС噍</summary>
/// <remarks>当å‰é€šè®¯æ‰€åœ¨çš„SocketæœåС噍</remarks>
ISocketServer ISocketSession.Server => Server;
/// <summary>最åŽä¸€æ¬¡é€šä¿¡æ—¶é—´</summary>
/// <remarks>主è¦è¡¨ç¤ºæ´»è·ƒæ—¶é—´ï¼ŒåŒ…æ‹¬æ”¶å‘æ“作</remarks>
public DateTime LastTime { get; private set; } = DateTime.Now;
/// <summary>APM性能追踪器</summary>
/// <remarks>用于记录关键æ“作的性能追踪</remarks>
public ITracer? Tracer { get; set; }
#endregion
#region æž„é€
/// <summary>实例化Udp会è¯</summary>
/// <param name="server">所属æœåС噍</param>
/// <param name="local">接收数æ®çš„æœ¬åœ°åœ°å€</param>
/// <param name="remote">远程终结点</param>
public UdpSession(UdpServer server, IPAddress? local, IPEndPoint remote)
{
Name = server.Name;
Server = server;
Remote = new NetUri(NetType.Udp, remote);
Tracer = server.Tracer;
Local = server.Local.Clone();
if (local != null) Local.Address = local;
// 检查并开å¯å¹¿æ’
server.Client?.CheckBroadcast(remote.Address);
}
/// <summary>开始数æ®äº¤æ¢</summary>
public void Start()
{
if (Disposed || Server == null) return;
Protocol = Server.Protocol;
MaxConcurrency = Server.MaxConcurrency;
MatchTimeout = Server.MatchTimeout;
Server.Open();
WriteLog("New {0}", Remote.EndPoint);
}
private void Stop(String reason)
{
if (Server == null) return;
WriteLog("Close {0} {1}", Remote.EndPoint, reason);
Server = null!;
}
/// <summary>销æ¯èµ„æº</summary>
/// <param name="disposing">是å¦é‡Šæ”¾æ‰˜ç®¡èµ„æº</param>
protected override void Dispose(Boolean disposing)
{
base.Dispose(disposing);
Stop(disposing ? "Dispose" : "GC");
// å–æ¶ˆæŒ‚起的请求-å“应ç‰å¾…,é¿å…调用方悬挂(与æµå¼ä¼šè¯å£å¾„一致)
_matcher.Clear();
// åªé‡Šæ”¾ã€ä¸ç½®ç©ºï¼šç½®ç©ºåŽ ??= 会为åŽç»æ´¾å‘新建“满é¢â€ä¿¡å·é‡ï¼Œå³ä¾¿ä¼šè¯å·²é”€æ¯ä¹Ÿæ”¾è¡Œæ¶ˆæ¯è¿›å…¥å¤„ç†é“¾ã€‚
// 已派å‘çš„æ¶ˆæ¯æŒæœ‰è‡ªå·±ç‰å¾…到的实例,其 Release 命ä¸å·²é‡Šæ”¾å®žä¾‹æŠ› ObjectDisposedExceptionï¼Œç”±è°ƒç”¨ç‚¹å¿½ç•¥ï¼ˆåŸºç±»åŒæ ·åªé‡Šæ”¾ä¸ç½®ç©ºï¼‰
_concurrency?.Dispose();
//// 释放对æœåŠ¡å¯¹è±¡çš„å¼•ç”¨ï¼Œå¦‚æžœæ²¡æœ‰å…¶å®ƒå¼•ç”¨ï¼ŒæœåŠ¡å¯¹è±¡å°†ä¼šè¢«å›žæ”¶
//Server = null;
}
#endregion
#region å‘é€
/// <summary>å‘逿•°æ®</summary>
/// <param name="data">æ•°æ®åŒ…</param>
/// <returns>实际å‘é€çš„å—节数</returns>
/// <exception cref="ObjectDisposedException">会è¯å·²é”€æ¯</exception>
public Int32 Send(IPacket data)
{
if (Disposed) throw new ObjectDisposedException(GetType().Name);
return Server.OnSend(data, Remote.EndPoint);
}
/// <summary>å‘逿•°æ®</summary>
/// <param name="data">å—节数组</param>
/// <param name="offset">åç§»</param>
/// <param name="count">å—节数</param>
/// <returns>实际å‘é€çš„å—节数</returns>
/// <exception cref="ObjectDisposedException">会è¯å·²é”€æ¯</exception>
public Int32 Send(Byte[] data, Int32 offset = 0, Int32 count = -1)
{
if (Disposed) throw new ObjectDisposedException(GetType().Name);
// 全部å‘é€
if (count < 0) count = data.Length - offset;
#if NET6_0_OR_GREATER
return Server.OnSend(new ReadOnlySpan<Byte>(data, offset, count), Remote.EndPoint);
#else
return Server.OnSend(new ArraySegment<Byte>(data, offset, count), Remote.EndPoint);
#endif
}
/// <summary>å‘逿•°æ®</summary>
/// <param name="data">数组段</param>
/// <returns>实际å‘é€çš„å—节数</returns>
/// <exception cref="ObjectDisposedException">会è¯å·²é”€æ¯</exception>
public Int32 Send(ArraySegment<Byte> data)
{
if (Disposed) throw new ObjectDisposedException(GetType().Name);
return Server.OnSend(data, Remote.EndPoint);
}
/// <summary>å‘逿•°æ®</summary>
/// <param name="data">åªè¯»å†…å˜æ®µ</param>
/// <returns>实际å‘é€çš„å—节数</returns>
/// <exception cref="ObjectDisposedException">会è¯å·²é”€æ¯</exception>
public Int32 Send(ReadOnlySpan<Byte> data)
{
if (Disposed) throw new ObjectDisposedException(GetType().Name);
return Server.OnSend(data, Remote.EndPoint);
}
/// <summary>å‘逿¶ˆæ¯ï¼Œä¸ç‰å¾…å“应。ç»å议构建整帧åŽå‘é€</summary>
/// <param name="message">消æ¯å¯¹è±¡ï¼ˆé¡»å®žçް <see cref="IMessage"/>)</param>
/// <returns>实际å‘é€çš„å—节数</returns>
/// <exception cref="InvalidOperationException">未设置å议或消æ¯ç±»åž‹ä¸æ”¯æŒ</exception>
public virtual Int32 SendMessage(Object message)
{
// å议模å¼ï¼šæ¶ˆæ¯ç»å议构建整帧åŽå‘é€
if (Protocol is { } codec && message is IMessage msg)
{
using var data = codec.Build(msg);
if (data == null) return 0;
return Send(data);
}
throw new InvalidOperationException(L.T("Protocol not set or message is not IMessage for session [{0}]", Name));
}
/// <summary>å‘逿¶ˆæ¯å¹¶ç‰å¾…匹é…çš„å“应(å议模å¼ï¼‰ã€‚请求入匹é…队列,å“åº”æ•°æ®æŠ¥åˆ°è¾¾æ—¶å®Œæˆ</summary>
/// <param name="request">请求消æ¯</param>
/// <param name="cancellationToken">å–æ¶ˆé€šçŸ¥</param>
/// <returns>å“应消æ¯ï¼›è°ƒç”¨æ–¹è´Ÿè´£æ¶ˆè´¹è´Ÿè½½å¹¶é‡Šæ”¾ï¼ˆ<see cref="IDisposable.Dispose"/> 或读满负载)</returns>
/// <exception cref="InvalidOperationException">未设置åè®®</exception>
/// <exception cref="NotSupportedException">å议未实现请求-å“应é…对(<see cref="IMessageMatcher"/>)</exception>
/// <remarks>
/// <para>与æµå¼ä¼šè¯å…±ç”¨åŒä¸€å“应匹é…实现(<see cref="ResponseMatcher"/>ï¼‰ï¼šæ•°æ®æŠ¥æ¯åŒ…å³å®Œæ•´å¸§ï¼Œäº¤ä»˜æ—¶ç›´æŽ¥é…å¯¹ï¼Œæ— ç²˜åŒ…å¤„ç†ã€‚
/// å“应消æ¯å…ˆè¿›å…¥ <see cref="Received"/> 事件链(å¯è§‚测),éšåŽå‘½ä¸é…对的交付本ç‰å¾…方;é…对è¯ä¹‰ç”±å议的 <see cref="IMessageMatcher.Match"/> 决定。</para>
/// <para>ç‰å¾…超时为 <see cref="MatchTimeout"/>ï¼›UDP ä¸å¯é ,未收到å“应å³è¶…时,é‡ä¼ 与å¦ç”±è°ƒç”¨æ–¹å†³å®šã€‚</para>
/// </remarks>
public virtual ValueTask<IMessage> SendMessageAsync(IMessage request, CancellationToken cancellationToken = default)
{
if (request == null) throw new ArgumentNullException(nameof(request));
var codec = Protocol ?? throw new InvalidOperationException(L.T("Protocol not set for session [{0}]", Name));
if (ResponseMatcher.Resolve(codec) == null) throw new NotSupportedException(L.T("Protocol [{0}] does not implement request/response matching (IMessageMatcher); cannot wait for a response.", codec.GetType().Name));
var span = Tracer?.NewSpan($"net:{Name}:SendMessageAsync", request);
var source = _matcher.Register(this, request, span);
try
{
SendMessage(request);
}
catch (Exception ex)
{
// 请求未能入队或é€å‡ºï¼šå¼‚常交给ç‰å¾…方(await 时抛出),资æºéš GetResult 归还
source.TrySetException(ex);
}
source.RegisterCancellation(cancellationToken);
return source.ValueTask;
}
/// <summary>å‘逿¶ˆæ¯å¹¶ç‰å¾…匹é…çš„å“应(å议模å¼ï¼‰</summary>
/// <param name="message">消æ¯å¯¹è±¡ï¼ˆé¡»å®žçް <see cref="IMessage"/>)</param>
/// <param name="cancellationToken">å–æ¶ˆé€šçŸ¥</param>
/// <returns>å“应消æ¯</returns>
/// <exception cref="InvalidOperationException">未设置å议或消æ¯ç±»åž‹ä¸æ”¯æŒ</exception>
public virtual ValueTask<Object> SendMessageAsync(Object message, CancellationToken cancellationToken = default)
{
if (Protocol != null && message is IMessage msg) return SendMessageAsyncForObject(msg, cancellationToken);
throw new InvalidOperationException(L.T("Protocol not set or message is not IMessage for session [{0}]", Name));
}
/// <summary>å议模å¼çš„å“应ç‰å¾…包装(Object 版返回)</summary>
/// <param name="message">请求消æ¯</param>
/// <param name="cancellationToken">å–æ¶ˆé€šçŸ¥</param>
/// <returns>å“应消æ¯</returns>
/// <remarks>
/// <para>ValueTask 为结构体且ä¸å¯åå˜ï¼Œ<c>ValueTask<IMessage></c> æ— æ³•ç›´æŽ¥è½¬ä¸º <c>ValueTask<Object></c>,åªèƒ½ç» async 包装转æ¢ï¼ˆåŒæ¥å®Œæˆæ—¶ä¸è£…ç®±ï¼Œæ— é¢å¤–分é…)。</para>
/// <para>ç‹¬ç«‹æˆæ–¹æ³•也让公开的 Object é‡è½½ä¿æŒéž async:å议未设或消æ¯ç±»åž‹ä¸æ”¯æŒæ—¶åŒæ¥æŠ›å‡ºå¼‚å¸¸ï¼Œè€Œä¸æ˜¯æŠŠå¼‚常æ•获进返回的 ValueTaskï¼Œä¸ await 的调用方也能立刻å‘现用法错误。</para>
/// </remarks>
private async ValueTask<Object> SendMessageAsyncForObject(IMessage message, CancellationToken cancellationToken)
=> await SendMessageAsync(message, cancellationToken).ConfigureAwait(false);
#endregion
#region 接收
/// <summary>åŒæ¥æŽ¥æ”¶æ•°æ®ã€‚UDP 会è¯å…±äº«æœåС噍 Socketï¼Œä¸æ”¯æŒä¼šè¯çº§æ‹‰å–</summary>
/// <returns>æ— ï¼Œæœ¬æ–¹æ³•æ€»æ˜¯æŠ›å‡ºå¼‚å¸¸</returns>
/// <exception cref="NotSupportedException">UDP 会è¯ä¸æ”¯æŒæ‹‰å–(共享 Socket 上按会è¯è¿‡æ»¤ä¼šä¸¢å¼ƒå…¶ä»–会è¯çš„æ•°æ®æŠ¥ï¼‰</exception>
public IOwnerPacket Receive() => throw new NotSupportedException(NoPullMessage);
/// <summary>å¼‚æ¥æŽ¥æ”¶æ•°æ®ã€‚UDP 会è¯å…±äº«æœåС噍 Socketï¼Œä¸æ”¯æŒä¼šè¯çº§æ‹‰å–</summary>
/// <param name="cancellationToken">å–æ¶ˆé€šçŸ¥</param>
/// <returns>æ— ï¼Œæœ¬æ–¹æ³•æ€»æ˜¯æŠ›å‡ºå¼‚å¸¸</returns>
/// <exception cref="NotSupportedException">UDP 会è¯ä¸æ”¯æŒæ‹‰å–(共享 Socket 上按会è¯è¿‡æ»¤ä¼šä¸¢å¼ƒå…¶ä»–会è¯çš„æ•°æ®æŠ¥ï¼‰</exception>
public virtual Task<IOwnerPacket?> ReceiveAsync(CancellationToken cancellationToken = default) => throw new NotSupportedException(NoPullMessage);
/// <summary>æ•°æ®æŽ¥æ”¶äº‹ä»¶</summary>
public event EventHandler<ReceivedEventArgs>? Received;
internal Boolean OnReceive(ReceivedEventArgs e)
{
LastTime = DateTime.Now;
// å议模å¼ï¼šæ•°æ®æŠ¥å³å®Œæ•´å¸§ï¼Œå®šç•ŒåŽé€æ¡åˆ†å‘ï¼›ç©ºæ•°æ®æŠ¥ä»æŒ‰åœä¼šè¯çº¦å®šå¤„ç†
if (Protocol is { } codec && e?.Packet is { Length: > 0 } pk)
{
ProcessDatagram(codec, pk);
return true;
}
// 我们约定,UDP收到空数æ®åŒ…时,结æŸä¼šè¯ã€‚åˆ¤å®šä»¥æ”¶åˆ°çš„åŽŸå§‹æ•°æ®æŠ¥ä¸ºå‡†ï¼Œåœ¨è¿›å…¥äº‹ä»¶é“¾ä¹‹å‰å®šä¸‹ï¼š
// 业务按契约在事件内消费/é‡Šæ”¾æœ¬è½®å¥æŸ„(交å‘é€é“¾è·¯ã€æˆ–ç½®ç©ºåšæ ‡è®°ï¼‰åŽ Length 已归零,
// 事件之åŽå†åˆ¤ä¼šæŠŠæ£å¸¸æ•°æ®æŠ¥å½“æˆç©ºåŒ…,误åœä¼šè¯å¹¶æ¸…掉会è¯çº§çжæ€ã€‚è¦ä¸»åŠ¨ç»“æŸä¼šè¯ï¼Œè¯· Dispose 会è¯ã€‚
var empty = e != null && e.Packet is not { Length: > 0 };
if (e != null) Received?.Invoke(this, e);
// 我们约定,UDP收到空数æ®åŒ…时,结æŸä¼šè¯
if (empty)
{
Stop("Finish");
Dispose();
}
return false;
}
/// <summary>处ç†åè®®æ•°æ®æŠ¥ã€‚ä¸€ä¸ªæ•°æ®æŠ¥å¯èƒ½åŒ…å«å¤šä¸ªæ¶ˆæ¯å¸§ï¼Œé€æ¡å®šç•Œåˆ†å‘ï¼›å帧截æ–丢弃</summary>
/// <param name="codec">å议编解ç 器</param>
/// <param name="pk">æ•°æ®æŠ¥ï¼ˆæŽ¥æ”¶çŽ¯è½®å†…æœ‰æ•ˆï¼Œæ¶ˆæ¯ä½“为零拷è´å…±äº«åˆ‡ç‰‡ï¼‰</param>
private void ProcessDatagram(IMessageCodec codec, IPacket pk)
{
var seq = pk.AsReadOnlySequence();
var pos = 0L;
while (pos < seq.Length)
{
var rs = codec.TryParse(seq.Slice(pos));
if (rs == null) break;
// æŸåå¸§ï¼šæ•°æ®æŠ¥å†…å·²æ— æ³•ç»§ç»å®šç•Œï¼Œä¸¢å¼ƒæœ¬æŠ¥æ–‡ç‰ä¸‹ä¸€åŒ…ï¼›
// ä¸èƒ½æŒ‰å¼‚常处ç†ï¼Œå¦åˆ™ä¸€ä¸ªå报文就会下线整个 UDP æœåŠ¡
if (rs.Value.Invalid) break;
// 已定界但需整帧(装饰åè®®ï¼Œå¦‚åŽ‹ç¼©ï¼‰ï¼šæ•°æ®æŠ¥æ˜¯åŽŸåå•ä½ï¼Œæ— 法在包内补é½ï¼Œä¸¢å¼ƒæœ¬æŠ¥æ–‡ç‰ä¸‹ä¸€åŒ…
if (rs.Value.NeedFullFrame) break;
// æ— æ¶ˆæ¯å¸§ï¼ˆå¿ƒè·³/空行/分隔符):跳过å—节åŽç»§ç»è§£æž
if (rs.Value.Message == null)
{
var skip = rs.Value.HeaderSize;
if (skip <= 0) break;
pos += skip;
continue;
}
var message = rs.Value.Message;
var headerSize = rs.Value.HeaderSize;
var bodyLength = rs.Value.BodyLength;
// 零进展防护(å议契约“æˆåŠŸå³æœ‰è¿›å±•â€ï¼‰ï¼šäº§å‡ºæ¶ˆæ¯å´ä¸æ¶ˆè´¹ä»»ä½•å—节会让本循环原地打转,丢弃本报文ç‰ä¸‹ä¸€åŒ…ï¼›
// 内置åè®®ä¸ä¼šå‡ºçŽ°ï¼Œé˜²çš„æ˜¯è¿å契约的自定义åè®®
if (headerSize + bodyLength <= 0)
{
message.Dispose();
break;
}
// å£°æ˜Žé•¿åº¦è¶…å‡ºæ•°æ®æŠ¥çª—å£ï¼šå帧截æ–丢弃
if (headerSize + bodyLength > seq.Length - pos)
{
message.Dispose();
break;
}
// 体绑定:å议未预绑定体时æ‰ä»Žæ•°æ®æŠ¥çª—å£åˆ‡é›¶æ‹·è´å…±äº«åˆ‡ç‰‡ã€‚
// 预绑定体(如压缩å议解压åŽé‡ç»‘)必须ä¿ç•™ï¼Œå¦åˆ™ä¼šæŠŠè§£åŽ‹ç»“æžœæ›¿æ¢æˆçº¿ä¸ŠåŽ‹ç¼©å—节(é™é»˜äº¤å‡ºé”™è¯¯æ•°æ®ï¼‰â€”â€”ä¸Žå¸§æ³µåˆ†æ”¯ä¿æŒä¸€è‡´
if (message.Payload == null && bodyLength > 0)
message.SetBody(pk.Slice((Int32)pos + headerSize, (Int32)bodyLength));
// 并行模å¼ï¼šæ•°æ®æŠ¥ä½“为零拷è´å…±äº«åˆ‡ç‰‡ï¼ˆå¤©ç„¶ç‹¬ç«‹ï¼‰ï¼Œä¿¡å·é‡çº¦æŸåŽæ´¾å‘;处ç†é¡ºåºä¸å®šï¼ˆSRMP 按åºåˆ—å·é…对)
if (MaxConcurrency > 1)
{
// ç‰å¾…ä¿¡å·é‡æ”¾åˆ°çº¿ç¨‹æ± 上:接收环è¿è¡Œåœ¨ IOCP/çº¿ç¨‹æ± çº¿ç¨‹ï¼Œå°±åœ°é˜»å¡žä¼šæ‹–æ…¢æ•´æ¡æŽ¥æ”¶é“¾å¹¶æŒ¤çˆ†å†…æ ¸ç¼“å†²
var concurrency = Concurrency;
_ = Task.Run(async () =>
{
try
{
await concurrency.WaitAsync().ConfigureAwait(false);
}
catch (ObjectDisposedException)
{
// 会è¯é”€æ¯ï¼šä¿¡å·é‡å·²é‡Šæ”¾ï¼Œæ¶ˆæ¯æ— 人处ç†ï¼Œå°±åœ°é‡Šæ”¾
message.TryDispose();
return;
}
ProcessMessage(message, concurrency);
});
pos += headerSize + bodyLength;
continue;
}
// 串行处ç†ï¼šä¸Žå¹¶è¡Œåˆ†æ”¯å…±ç”¨åŒä¸€äº¤ä»˜é€»è¾‘(信å·é‡ä¼ nullï¼Œä¸æ¶‰åŠå¹¶å‘æ§½ä½ï¼‰
ProcessMessage(message, null);
pos += headerSize + bodyLength;
}
}
/// <summary>处ç†å•个消æ¯ï¼šè¿›å…¥äº‹ä»¶é“¾å¹¶å°è¯•交付é…对,未命ä¸åˆ™é‡Šæ”¾ï¼›å¹¶è¡Œæ´¾å‘时完æˆåŽé‡Šæ”¾å¹¶å‘ä¿¡å·é‡</summary>
/// <param name="message">消æ¯ï¼ˆä½“ä¸ºæ•°æ®æŠ¥å†…é›¶æ‹·è´å…±äº«åˆ‡ç‰‡ï¼Œå¤©ç„¶ç‹¬ç«‹ï¼‰</param>
/// <param name="concurrency">æ´¾å‘å‰ç‰å¾…到的信å·é‡å®žä¾‹ï¼›ä¸²è¡Œåˆ†æ”¯ä¸º null。归还必须用åŒä¸€å®žä¾‹ï¼šä¼šè¯é¦–è®¿å¹¶å‘æ—¶å¯èƒ½å„建一个,é‡è¯»å±žæ€§ä¼šæŠŠæ§½ä½è¿˜åˆ°å¦ä¸€ä¸ªæ»¡é¢å®žä¾‹å¹¶æŠ› <see cref="SemaphoreFullException"/></param>
private void ProcessMessage(IMessage message, SemaphoreSlim? concurrency)
{
var delivered = false;
try
{
OnMessage(message);
// 事件链(å¯è§‚测)å¯èƒ½å·²åœ¨å¤„ç†å™¨å†…读空消æ¯ä½“;命ä¸é…对的ç‰å¾…æ–¹è¿˜è¦æ¶ˆè´¹åŒä¸€ä»½ä½“,
// äº¤ä»˜å‰æŠŠå†…å˜æ¨¡å¼ä½“å¤ä½åˆ°èµ·ç‚¹ï¼ˆä¸Žæµå¼ä¼šè¯å£å¾„一致)
if (message.Body is { IsStreaming: false } body) body.Reset();
// 交付阶段é…对:命ä¸åˆ™äº¤ä»˜ç‰å¾…方(由ç‰å¾…方释放);未命ä¸çš„æŒ‰æ™®é€šæ¶ˆæ¯æ”¶å°¾
delivered = _matcher.TryMatch(this, Protocol, message);
}
catch (Exception ex)
{
OnError("OnMessage", ex);
}
finally
{
if (!delivered) message.TryDispose();
if (concurrency != null)
{
// 释放自己ç‰å¾…åˆ°çš„é‚£ä¸ªå¹¶å‘æ§½ä½ï¼ˆèƒŒåŽ‹ï¼‰ï¼›ä¼šè¯é”€æ¯æ—¶è¯¥å®žä¾‹å·²è¢«é‡Šæ”¾ï¼Œå¿½ç•¥è¯¥å¼‚常
try { concurrency.Release(); }
catch (ObjectDisposedException) { }
}
}
}
private SemaphoreSlim? _concurrency;
/// <summary>å¹¶å‘ä¿¡å·é‡ã€‚并行模å¼ï¼ˆ<see cref="MaxConcurrency"/> 大于1)下约æŸåŒä¼šè¯å¹¶å‘å¤„ç†æ•°ï¼Œç‰å¾…æ—¶å½¢æˆèƒŒåŽ‹</summary>
/// <remarks>
/// å¤šæ¡æŽ¥æ”¶æ§½çº¿ç¨‹å¹¶å‘首访时å¯èƒ½å„建一个满é¢å®žä¾‹ï¼Œå—段åªç•™æœ€åŽä¸€ä¸ªã€‚è¯¥ç«žæ€æ— 害:æ¯ä¸ªæ¶ˆæ¯åªå½’还自己ç‰å¾…到的实例,
/// ä¸å˜åœ¨â€œç‰å¾…在Aã€å½’还到Bâ€çš„é”™ä½ï¼Œæœ€å仅首访瞬间并å‘上é™çŸæš‚放大。刻æ„ä¸åŠ CAS/é”,ä¸ä¸ºæžçª„窗å£å¼•å…¥åŽŸåæ“ä½œã€‚
/// </remarks>
private SemaphoreSlim Concurrency => _concurrency ??= new SemaphoreSlim(MaxConcurrency, MaxConcurrency);
/// <summary>收到消æ¯ã€‚æž„é€ æŽ¥æ”¶äº‹ä»¶å‚æ•°å¹¶è¿›å…¥äº‹ä»¶é“¾ï¼ˆå议模å¼ï¼‰</summary>
/// <param name="message">消æ¯ï¼ˆå¤´éƒ¨å—段就ä½ã€ä½“已绑定)</param>
/// <remarks>äº‹ä»¶å‚æ•°çš„ <see cref="ReceivedEventArgs.Packet"/> 为消æ¯è´Ÿè½½è§†å›¾ï¼›ä¸Žæ¶ˆæ¯åŒç”Ÿå‘½å‘¨æœŸï¼ˆå¤„ç†å™¨è¿”回åŽå¤±æ•ˆï¼‰</remarks>
private void OnMessage(IMessage message)
{
var e = ReceivedEventArgs.Rent();
try
{
e.Local = Local.Address;
e.Remote = Remote.EndPoint;
e.Packet = message.Payload;
e.Message = message;
LastTime = DateTime.Now;
Received?.Invoke(this, e);
// 凿 ¼åˆ°æœåŠ¡å™¨å±‚ï¼šä»¥æœåŠ¡å™¨ä¸ºæŽ¥å…¥ç‚¹çš„åœºæ™¯ï¼ˆå¦‚ NetClientï¼‰ç»æ¤æ”¶åˆ°æ¶ˆæ¯
Server?.RaiseReceiveInternal(this, e);
}
finally
{
ReceivedEventArgs.Return(e);
}
}
/// <summary>å¤„ç†æ•°æ®å¸§</summary>
/// <param name="data">æ•°æ®å¸§</param>
void ISocketRemote.Process(IData data) => (Server as ISocketRemote)?.Process(data);
#endregion
#region 异常处ç†
/// <summary>错误å‘生/æ–开连接时</summary>
public event EventHandler<ExceptionEventArgs>? Error;
/// <summary>触å‘异常</summary>
/// <param name="action">动作</param>
/// <param name="ex">异常</param>
protected virtual void OnError(String action, Exception ex)
{
Log?.ErrorPrefixed(LogPrefix, "{0}Error {1} {2}", action, this, ex.Message);
Error?.Invoke(this, new ExceptionEventArgs(action, ex));
}
#endregion
#region 辅助
/// <summary>å·²é‡è½½ã€‚返回会è¯çš„å—符串表示</summary>
/// <returns>本地地å€å’Œè¿œç¨‹åœ°å€çš„组åˆ</returns>
public override String ToString()
{
if (Remote != null && !Remote.EndPoint.IsAny())
return $"{Local}<={Remote.EndPoint}";
else
return Local.ToString();
}
/// <summary>æ‹‰å–æ‹’ç»æç¤ºã€‚UDP 会è¯å…±äº«æœåС噍 Socket,按会è¯è¿‡æ»¤æ‹‰å–会丢弃其他会è¯çš„æ•°æ®æŠ¥</summary>
private String NoPullMessage => L.T("[{0}] A UDP session shares the server socket, so per-session pull is not supported (filtering per session would drop datagrams of other sessions). Subscribe to the Received event, or set AutoReceive=false before opening the server and use UdpServer.Receive/ReceiveAsync; on the client side use NetClient.", Name);
#endregion
#region ITransport接å£
Boolean ITransport.Open() => true;
Boolean ITransport.Close() => true;
#endregion
#region 扩展接å£
private ConcurrentDictionary<String, Object?>? _items;
/// <summary>æ•°æ®é¡¹ã€‚首次访问时创建</summary>
/// <remarks>å¹¶å‘首用时以 CAS ä¿è¯åªä¿ç•™ä¸€ä»½å®žä¾‹ï¼šç›´æŽ¥ ??= 会å„自新建,败者刚写入的数æ®éšä¹‹ä¸å¯è¾¾è€Œä¸¢å¤±</remarks>
public IDictionary<String, Object?> Items
{
get
{
var items = _items;
if (items != null) return items;
var created = new ConcurrentDictionary<String, Object?>();
return Interlocked.CompareExchange(ref _items, created, null) ?? created;
}
}
/// <summary>设置 或 èŽ·å– æ•°æ®é¡¹</summary>
/// <param name="key">é”®</param>
/// <returns>值</returns>
public Object? this[String key] { get => _items != null && _items.TryGetValue(key, out var obj) ? obj : null; set => Items[key] = value; }
#endregion
#region 日志
/// <summary>日志æä¾›è€…</summary>
public ILog Log { get; set; } = Logger.Null;
/// <summary>是å¦è¾“出å‘逿—¥å¿—</summary>
/// <remarks>默认false</remarks>
public Boolean LogSend { get; set; }
/// <summary>是å¦è¾“出接收日志</summary>
/// <remarks>默认false</remarks>
public Boolean LogReceive { get; set; }
private String? _LogPrefix;
/// <summary>日志å‰ç¼€</summary>
public virtual String LogPrefix
{
get
{
if (_LogPrefix == null)
{
var name = Server == null ? "" : Server.Name;
_LogPrefix = $"{name}[{ID}].";
}
return _LogPrefix;
}
set => _LogPrefix = value;
}
/// <summary>输出日志</summary>
/// <param name="format">æ ¼å¼åŒ–å—符串</param>
/// <param name="args">傿•°</param>
public void WriteLog(String format, params Object?[] args) => Log?.InfoPrefixed(LogPrefix, format, args);
#endregion
}
|