[fix]修正UdpServer在接收广播时连续启动接收的错误,在StarAgent中,此时可能收到广播包,SocketFlags是Broadcast,需要清空,否则报错“参考的对象类型不支持尝试的操作”; 无需设置SocketOptionName.PacketInformation,在ReceiveMessageFromAsync时会自动设置,并且支持ipv6;
石头 authored at 2024-10-10 00:36:00 石头 committed at 2024-10-10 00:45:43
5.16 KiB
X
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
{
    #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 Packet? Payload { get; set; }

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

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

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

        var ms = pk.GetStream();
        var b = ms.ReadByte();

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

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

        var len = ms.ReadByte();

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

        /*
         * 数据长度
         * len < 126    单字节表示长度
         * len = 126    后续2字节表示长度,大端
         * len = 127    后续8字节表示长度
         */
        len &= 0x7F;
        if (len == 126)
            len = (ms.ReadByte() << 8) | ms.ReadByte();
        else if (len == 127)
        {
            var buf = new Byte[8];
            ms.Read(buf, 0, buf.Length);
            // 没有人会传输超大数据
            len = (Int32)BitConverter.ToUInt64(buf, 0);
        }

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

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

        // 特殊处理关闭消息
        if (Type == WebSocketMessageType.Close)
        {
            var data = Payload;
            if (data != null)
            {
                CloseStatus = data.ReadBytes(0, 2).ToUInt16(0, false);
                StatusDescription = data.Slice(2).ToStr();
            }
        }

        return true;
    }

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

        // 特殊处理关闭消息
        if (len == 0 && Type == WebSocketMessageType.Close)
        {
            pk = new Packet(((UInt16)CloseStatus).GetBytes(false))
            {
                Next = StatusDescription.GetBytes()
            };
            len = pk.Total;
        }

        var ms = new MemoryStream();
        ms.WriteByte((Byte)(0x80 | (Byte)Type));

        var masks = MaskKey;

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

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

            ms.Write(masks);

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

        return new Packet(ms.ToArray()) { Next = pk };
    }
    #endregion
}