using NewLife.Data;
using NewLife.Messaging;
namespace NewLife.Http;
/// <summary>WebSocket 分片é‡ç»„器(RFC 6455 §5.4)。累积 FIN=0 æ•°æ®å¸§ï¼Œæœ«ç‰‡ï¼ˆFIN=1)åˆå¹¶ä¸ºå®Œæ•´æ¶ˆæ¯äº¤ä»˜</summary>
/// <remarks>
/// æ¯ä¸ªè¿žæŽ¥æŒæœ‰ä¸€ä¸ªå®žä¾‹ï¼›æŽ§åˆ¶å¸§ï¼ˆClose/Ping/Pong)ä¸å¯åˆ†ç‰‡ï¼Œä¸ç»è¿‡æœ¬é‡ç»„器。
/// 首片为文本或二进制帧(opcode=1/2,FIN=0),åŽç»ç»ç‰‡ä¸ºé™„åŠ æ•°æ®å¸§ï¼ˆopcode=0)。
/// </remarks>
internal sealed class WebSocketFragment
{
private readonly List<Byte[]> _fragments = [];
private WebSocketMessageType _type;
private Int32 _length;
/// <summary>分片é‡ç»„上é™ï¼ˆå—节)。超过丢弃当å‰åˆ†ç‰‡åºåˆ—,é¿å…å†…å˜æ— 界增长。默认 16M</summary>
public Int32 MaxFragments { get; set; } = 16 * 1024 * 1024;
/// <summary>æ˜¯å¦æ£åœ¨ç´¯ç§¯åˆ†ç‰‡</summary>
public Boolean Active => _fragments.Count > 0;
/// <summary>开始累积新的分片åºåˆ—ã€‚æ–°é¦–ç‰‡åˆ°æ¥æ—¶ä¸¢å¼ƒæœªå®Œæˆçš„æ—§åºåˆ—(åè®®é”™è¯¯æˆ–å¯¹ç«¯ä¸æ–)</summary>
/// <param name="type">首片类型(文本或二进制)</param>
/// <param name="payload">首片负载(内部拷è´ï¼‰</param>
public void Begin(WebSocketMessageType type, IPacket? payload)
{
_fragments.Clear();
_fragments.Add(payload?.ToArray() ?? []);
_type = type;
_length = _fragments[0].Length;
}
/// <summary>è¿½åŠ ç»ç‰‡ï¼›æœ«ç‰‡ï¼ˆFIN=1)时åˆå¹¶ä¸ºå®Œæ•´æ¶ˆæ¯</summary>
/// <param name="fin">æ˜¯å¦æœ«ç‰‡</param>
/// <param name="payload">ç»ç‰‡è´Ÿè½½ï¼ˆå†…部拷è´ï¼‰</param>
/// <returns>åˆå¹¶åŽçš„完整消æ¯ï¼›æœªåˆ°æœ«ç‰‡ã€è¶…陿ˆ–æ— é¦–ç‰‡çš„å¤ç«‹ç»ç‰‡è¿”回 null</returns>
public WsMessage? Append(Boolean fin, IPacket? payload)
{
if (_fragments.Count == 0) return null;
var data = payload?.ToArray() ?? [];
_length += data.Length;
if (_length > MaxFragments)
{
// è¶…é™ï¼šä¸¢å¼ƒæ•´æ®µåˆ†ç‰‡åºåˆ—
_fragments.Clear();
return null;
}
_fragments.Add(data);
if (!fin) return null;
// 末片:åˆå¹¶ä¸ºå®Œæ•´æ¶ˆæ¯
var buf = new Byte[_length];
var offset = 0;
foreach (var item in _fragments)
{
item.CopyTo(buf, offset);
offset += item.Length;
}
_fragments.Clear();
var msg = new WsMessage { Fin = true, Type = _type };
msg.SetBody(new ArrayPacket(buf));
return msg;
}
}
|