【问题标题】:Message Framing Method advantages and disadvantages in .NET.NET 中的消息框架方法的优缺点
【发布时间】:2022-04-25 14:15:34
【问题描述】:

我正在研究我所知道的两种不同 TCP 消息成帧方法的优缺点。

  1. Delimited:使用分隔符字节将 TCP 流分成非固定长度的消息。发送数据时,例程必须检查消息数据的分隔符并将其转义以确保消息帧的安全传输。接收数据时,例程必须通读流,寻找分隔符字节以将帧分解为消息。

EG:用户 [用户名]\n密码 [密码]\n

  1. Length-Prefixed:通过使用 4 个字节的前缀来说明消息的长度,将 TCP 流分成预定大小的消息。接收数据时,例程将首先读取前缀以确定消息帧的长度。发送数据时,例程必须在传输前为消息添加长度前缀。

例如:[MessageLength]User [Username][MessageLength]Password [Password]

这两种方法都允许传输大小不同并包含要解释的字节流的消息帧。更高级别的消息结构或协议不相关。


因此,我将注意力集中在可扩展性和性能效率上。我发现自己需要运行基准测试,看看哪种方法可以在不涉及任何消息处理的情况下获得最大的效率吞吐量。


我目前的想法,无论如何我都不是专家。

分隔消息帧在接收例程期间效率较低,因为需要检查流中的每个字节是否有消息帧分隔符。长度前缀消息帧将始终读取前缀字节,其余消息帧流将直接进入缓冲区而不进行处理,直到接收到整个消息帧。

在发送例程中,作为长度前缀的消息帧在消息本身之前传输的消息前缀效率较低。


我认为可能的其他因素包括:

  • 大量小消息帧会导致为长度前缀结构传输更多数据包。
  • 使用 Length-Prefixed 结构可以更有效地处理大量较大的消息帧,因为不会读取每个字节来检查分隔符。

这个主题的任何亮点都会很棒。我发现很难找到关于 TCP 消息帧结构之间差异的好的资源。

【问题讨论】:

  • 您正在尝试发明 IP,即 TCP/IP 的另一部分。这是行不通的,它已经存在了,你不能直接影响它,也不需要任何帮助。 TCP 是一个流,试图将它分解成碎片会使其效率非常低下。只有选项 2 符合条件。
  • 我说的是你必须实现的消息框架,以通过 TCP 流以数据包的形式传输数据。我不想发明任何东西。我正在尝试讨论您选择实施的框架的优缺点。例如。像“USER [用户名]\n”这样的定界消息帧:其中“\n”是定界符,因此它是一个定界消息帧。

标签: .net sockets


【解决方案1】:

根据我的经验,长度前缀是首选,消息的解析代码往往更容易编写。

此外,如果消息有效负载可能包含分隔符,则使用消息分隔符您需要找出转义方案。

我遇到了与有效负载无关的第三种方案。它定义了具有已知格式的不同消息类型,消息的不同部分可以是固定长度或可变长度。 (固定为简单类型和变量是数组和字符串)。该结构预先在客户端和服务器之间共享。发送消息时,消息以消息类型编号为前缀。接收方可以从消息类型号推断出如何解析消息。
LysKOM 消息系统的protocol 就是一个例子。
这个协议有一个正式的规范,可以用来生成解析器代码。

【讨论】:

  • 我认为大多数协议使用分隔的消息帧是否正确。 IE。 FTP、HTTP、DCP、POP、SMTP、UPNP 等?我知道列出的一些有切换机制,可能会同时使用。
【解决方案2】:

我以前解决过这个问题的方式略有不同。

我获取初始有效负载(消息)并对其进行 Base64 编码。这给了我一个编码的字符串。我选择了两个已知不会在 Base64 中使用的字符。我用一个作为消息“*”的开头,另一个作为结尾“|”。

然后我可以从流中读取传入的字节,直到我得到我的开始或结束字符 - 我知道要提取什么。

这是我编写的代码:

/// <summary>
/// Logic for converting to and from a <see cref="DataPacket"/>
/// </summary>
public sealed class Packager : IPackager
{
    private static string START_DELIMITER => "*";
    private static string END_DELIMITER => "|";
    
    private static IEnumerable<byte> StartDelimiterBytes => Encoding.UTF8.GetBytes(START_DELIMITER);
    private static IEnumerable<byte> EndDelimiterBytes => Encoding.UTF8.GetBytes(END_DELIMITER);
    
    public byte[]? PacketToBytes(DataPacket? message)
    {
        if (message == null)
        {
            return null;
        }
    
        var returnBytes = new List<byte>();

        // START DELIMITER
        returnBytes.AddRange(StartDelimiterBytes);
    
        using (var ms = new MemoryStream())
        {
            using (var writer = new BsonDataWriter(ms))
            {
                new JsonSerializer().Serialize(writer, message);
            }
    
            // BODY BYTES
            var bodyAsString = Convert.ToBase64String(ms.ToArray());
            var bodyAsBytes = Encoding.UTF8.GetBytes(bodyAsString);
    
            returnBytes.AddRange(bodyAsBytes);
        }
   
        // END DELIMITER
        returnBytes.AddRange(EndDelimiterBytes);
    
        return returnBytes.ToArray();
    }
    
    public DataPacket? BytesToPacket(byte[]? bytes)
    {
        if (bytes == null)
        {
            return null;
                }
        var bytesAsString = Encoding.UTF8.GetString(bytes);
        if (string.IsNullOrEmpty(bytesAsString))
        {
            return null;
        }
    
        bytesAsString = bytesAsString.Replace(START_DELIMITER, string.Empty);
        bytesAsString = bytesAsString.Replace(END_DELIMITER, string.Empty);
    
        var decodedString = Convert.FromBase64String(bytesAsString);
    
        using (var ms = new MemoryStream(decodedString))
        {
            using (var reader = new BsonDataReader(ms))
            {
                return new JsonSerializer().Deserialize<DataPacket>(reader);
            }
        }
     }
    
     public int GetFrameStartIndex(string value) => value.IndexOf(START_DELIMITER, StringComparison.InvariantCulture);
    
     public int GetFrameEndIndex(string value) => value.IndexOf(END_DELIMITER, StringComparison.InvariantCulture);
    
     public string ReadBytesAsString(byte[] value, int startIndex, int endIndex) => Encoding.UTF8.GetString(value, startIndex, endIndex);
    }

【讨论】:

    猜你喜欢
    • 2010-11-10
    • 1970-01-01
    • 2011-08-05
    • 2017-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多