负数转为Boolean时,作为true
石头 编写于 2024-09-29 00:34:11
X
using System.Buffers.Binary;
using System.Text;
using NewLife.Buffers;
using NewLife.Data;

namespace NewLife.Http;

/// <summary>WebSocket消息类型</summary>
public enum WebSocketMessageType
{
    /// <summary>附加数据</summary>
    Data = 0,

    /// <summary>文本数据</summary>
    Text = 1,

    /// <summary>二进制数据</summary>
    Binary = 2,

    /// <summary>连接关闭</summary>
    Close = 8,

    /// <summary>心跳</summary>
    Ping = 9,

    /// <summary>心跳响应</summary>
    Pong = 10,
}

/// <summary>WebSocket消息</summary>
public class WebSocketMessage : IDisposable
{
    #region 属性
    /// <summary>消息是否结束</summary>
    public Boolean Fin { get; set; }

    /// <summary>消息类型</summary>
    public WebSocketMessageType Type { get; set; }

    /// <summary>加密数据的掩码</summary>
    public Byte[]? MaskKey { get; set; }

    /// <summary>负载数据</summary>
    public IPacket? Payload { get; set; }

    /// <summary>关闭状态。仅用于Close消息</summary>
    public Int32 CloseStatus { get; set; }

    /// <summary>关闭状态描述。仅用于Close消息</summary>
    public String? StatusDescription { get; set; }
    #endregion

    #region 构造
    /// <summary>销毁。回收数据包到内存池</summary>
    public void Dispose() => Payload.TryDispose();
    #endregion

    #region 方法
    /// <summary>读取消息</summary>
    /// <param name="pk"></param>
    /// <returns></returns>
    public Boolean Read(IPacket pk)
    {
        if (pk.Length < 2) return false;

        var reader = new SpanReader(pk.GetSpan())
        {
            IsLittleEndian = false
        };
        //var reader = pk.GetStream();
        var b = reader.ReadByte();

        Type = (WebSocketMessageType)(b & 0x7F);

        // 仅处理一个包
        Fin = (b & 0x80) == 0x80;
        if (!Fin) return false;

        var b2 = reader.ReadByte();

        var mask = (b2 & 0x80) == 0x80;

        /*
         * 数据长度
         * len < 126    单字节表示长度
         * len = 126    后续2字节表示长度,大端
         * len = 127    后续8字节表示长度
         */
        var len = (Int64)(b2 & 0x7F);
        if (len == 126)
            len = reader.ReadUInt16();
        else if (len == 127)
            len = reader.ReadInt64();

        // 如果mask,剩下的就是数据,避免拷贝,提升性能
        if (!mask)
        {
            Payload = pk.Slice(reader.Position, (Int32)len);
        }
        else
        {
            var masks = new Byte[4];
            if (mask) reader.ReadBytes(4).CopyTo(masks);
            MaskKey = masks;

            if (mask)
            {
                // 直接在数据缓冲区修改,避免拷贝
                Payload = pk.Slice(reader.Position, (Int32)len);
                var data = Payload.GetSpan();
                for (var i = 0; i < len; i++)
                {
                    data[i] = (Byte)(data[i] ^ masks[i % 4]);
                }
            }
        }

        // 特殊处理关闭消息
        if (Type == WebSocketMessageType.Close && Payload != null)
        {
            var data = Payload.GetSpan();
            CloseStatus = BinaryPrimitives.ReadUInt16BigEndian(data[..2]);
            StatusDescription = data[2..].ToStr();
        }

        return true;
    }

    /// <summary>把消息转为封包</summary>
    /// <returns></returns>
    public virtual IOwnerPacket ToPacket()
    {
        var pk = Payload;
        var len = pk == null ? 0 : pk.Total;

        // 特殊处理关闭消息
        if (len == 0 && Type == WebSocketMessageType.Close)
        {
            len = 2;
            if (!StatusDescription.IsNullOrEmpty()) len += Encoding.UTF8.GetByteCount(StatusDescription);
        }

        var rs = new OwnerPacket(1 + 1 + 8 + 4 + len);
        var writer = new SpanWriter(rs.GetSpan())
        {
            IsLittleEndian = false
        };

        writer.WriteByte((Byte)(0x80 | (Byte)Type));

        var masks = MaskKey;

        /*
         * 数据长度
         * len < 126    单字节表示长度
         * len = 126    后续2字节表示长度,大端
         * len = 127    后续8字节表示长度
         */

        if (masks == null)
        {
            if (len < 126)
            {
                writer.WriteByte((Byte)len);
            }
            else if (len < 0xFFFF)
            {
                writer.WriteByte(126);
                writer.Write((Int16)len);
            }
            else
            {
                writer.WriteByte(127);
                writer.Write((Int64)len);
            }
        }
        else
        {
            if (len < 126)
            {
                writer.WriteByte((Byte)(len | 0x80));
            }
            else if (len < 0xFFFF)
            {
                writer.WriteByte(126 | 0x80);
                writer.Write((Int16)len);
            }
            else
            {
                writer.WriteByte(127 | 0x80);
                writer.Write((Int64)len);
            }

            writer.Write(masks);

            // 掩码混淆数据。直接在数据缓冲区修改,避免拷贝
            if (Payload != null)
            {
                var data = Payload.GetSpan();
                for (var i = 0; i < len; i++)
                {
                    data[i] = (Byte)(data[i] ^ masks[i % 4]);
                }
            }
        }

        if (pk != null && pk.Length > 0)
            writer.Write(pk.GetSpan());
        else if (Type == WebSocketMessageType.Close)
        {
            writer.Write((Int16)CloseStatus);
            if (!StatusDescription.IsNullOrEmpty()) writer.Write(StatusDescription, -1);
        }

        return rs.Slice(0, writer.Position);
    }
    #endregion
}