C# 什么';这是';最佳';将流解析为结构或类的方法?

C# 什么';这是';最佳';将流解析为结构或类的方法?,c#,C#,实际上,我正在使用.NETFramework3.5,所以我有所有这些很好的小功能,比如lambdas、linq等等 给定的是一个串行连接(或者更抽象地说,是一个流),您在其中接收一些数据,这些数据的格式如下: struct Packet { byte STX UInt16 DataLength string Data byte CRC byte ETX } 使用传入数据的简单映射没有帮助,因为您不知道一个数据包将有多长,因为它写在结构中(数据长度) 所以

实际上,我正在使用.NETFramework3.5,所以我有所有这些很好的小功能,比如lambdas、linq等等

给定的是一个串行连接(或者更抽象地说,是一个流),您在其中接收一些数据,这些数据的格式如下:

struct Packet
{
    byte STX
    UInt16 DataLength
    string Data
    byte CRC
    byte ETX
}
使用传入数据的简单映射没有帮助,因为您不知道一个数据包将有多长,因为它写在结构中(数据长度)

所以我的第一个想法是按字节读取流并将其放入???。是的,这是下一个问题。在哪里存储第一个原始数据?进入一个简单的字节数组,最大可能长度(由于DataLength是一个UInt16加上其他字段的额外字节,因此将为65540字节)。或者我应该打开一个队列并填满所有传入的字节,或者可能存在另一个很好的可能性

让我们假设这些问题已经解决,并且我有某种本地缓冲区,它保存流中的所有原始字节。用给定的结构来解释它最好的方法是什么??只是执行某种for或foreach循环,或者存在一种更智能(性能更好)的方法(例如使用regex或linq)

致以最良好的祝愿,
Oliver

我会将它们存储在字节数组中,然后从那里重新创建它们,这是一种快速而简单的方法


我将读取字节并用BitConverter、Encoding.UTF8.转换它们。我将它们存储在字节数组中,然后从那里重新创建它们,这是一种快速而简单的方法


我将读取字节并使用BitConverter、Encoding.UTF8..

对其进行转换。总之,它归结为使用[Serializable]属性,您的操作完成了

看看这个,不管怎样,它归结为使用了[Serializable]属性,并且您的操作完成了 那么

struct Packet
{
    public byte STX;
    public UInt16 DataLength;
    public string Data;
    public byte CRC;
    public byte ETX;
}

//Warning: Need to add error handling
class PacketReader
{
    private BinaryReader _reader;

    public PacketReader(Stream stream)
    {
        _reader = new BinaryReader(stream);
    }

    Packet ReadPacket()
    {
        var packet = new Packet() 
            {
                STX = _reader.ReadByte(),
                DataLength = _reader.ReadUInt16(),
                Data = Encoding.ASCII.GetString(
                    _reader.ReadBytes(packet.DataLength)),
                CRC = _reader.ReadByte(),
                ETX = _reader.ReadByte()
            };

        return packet;
    }
}
请注意:我并没有故意使用BinaryReader.ReadString(),因为它是为处理BinaryWriter.WriteString()生成的字符串而设计的。即使是长度前缀字符串,编码也有点不同。

那么

struct Packet
{
    public byte STX;
    public UInt16 DataLength;
    public string Data;
    public byte CRC;
    public byte ETX;
}

//Warning: Need to add error handling
class PacketReader
{
    private BinaryReader _reader;

    public PacketReader(Stream stream)
    {
        _reader = new BinaryReader(stream);
    }

    Packet ReadPacket()
    {
        var packet = new Packet() 
            {
                STX = _reader.ReadByte(),
                DataLength = _reader.ReadUInt16(),
                Data = Encoding.ASCII.GetString(
                    _reader.ReadBytes(packet.DataLength)),
                CRC = _reader.ReadByte(),
                ETX = _reader.ReadByte()
            };

        return packet;
    }
}

请注意:我并没有故意使用BinaryReader.ReadString(),因为它是为处理BinaryWriter.WriteString()生成的字符串而设计的。即使是长度前缀字符串,编码也有点不同。

另一种可能的选择,利用C#的yield关键字:

public struct Packet    
{    
    public byte STX;    
    public UInt16 DataLength;    
    public string Data;    
    public byte CRC;    
    public byte ETX;    
}    

public static class StreamExtensions
{
    public IEnumerable<Packet> ToPacketStream(this Stream stream)
    {
        BinaryReader reader = new BinaryReader(stream);
        while(reader.PeekChar() != -1) //Optionally change this to reflect your exit conditions
        {
            var packet = new Packet();

            packet.STX =        _reader.ReadByte();       
            packet.DataLength = _reader.ReadUInt16();       
            packet.Data =       Encoding.ASCII.GetString(_reader.ReadBytes(packet.DataLength));       
            packet.CRC =        _reader.ReadByte();       
            packet.ETX =        _reader.ReadByte();

            yield return packet;
        }
    }   
}

//Usage
foreach(var packet in stream.ToPacketStream())
{
    //Handle packet
}
公共结构数据包
{    
公共字节STX;
公共UInt16数据长度;
公共字符串数据;
公共字节CRC;
公共字节ETX;
}    
公共静态类StreamExtensions
{
公共IEnumerable ToPacketStream(此流)
{
BinaryReader=新的BinaryReader(流);
while(reader.PeekChar()!=-1)//可以选择更改此值以反映退出条件
{
var数据包=新数据包();
packet.STX=_reader.ReadByte();
packet.DataLength=_reader.ReadUInt16();
packet.Data=Encoding.ASCII.GetString(_reader.ReadBytes(packet.DataLength));
packet.CRC=_reader.ReadByte();
packet.ETX=_reader.ReadByte();
产生返回包;
}
}   
}
//用法
foreach(stream.ToPacketStream()中的var数据包)
{
//句柄包
}

另一个可能的选择,利用C#的收益率关键字:

public struct Packet    
{    
    public byte STX;    
    public UInt16 DataLength;    
    public string Data;    
    public byte CRC;    
    public byte ETX;    
}    

public static class StreamExtensions
{
    public IEnumerable<Packet> ToPacketStream(this Stream stream)
    {
        BinaryReader reader = new BinaryReader(stream);
        while(reader.PeekChar() != -1) //Optionally change this to reflect your exit conditions
        {
            var packet = new Packet();

            packet.STX =        _reader.ReadByte();       
            packet.DataLength = _reader.ReadUInt16();       
            packet.Data =       Encoding.ASCII.GetString(_reader.ReadBytes(packet.DataLength));       
            packet.CRC =        _reader.ReadByte();       
            packet.ETX =        _reader.ReadByte();

            yield return packet;
        }
    }   
}

//Usage
foreach(var packet in stream.ToPacketStream())
{
    //Handle packet
}
公共结构数据包
{    
公共字节STX;
公共UInt16数据长度;
公共字符串数据;
公共字节CRC;
公共字节ETX;
}    
公共静态类StreamExtensions
{
公共IEnumerable ToPacketStream(此流)
{
BinaryReader=新的BinaryReader(流);
while(reader.PeekChar()!=-1)//可以选择更改此值以反映退出条件
{
var数据包=新数据包();
packet.STX=_reader.ReadByte();
packet.DataLength=_reader.ReadUInt16();
packet.Data=Encoding.ASCII.GetString(_reader.ReadBytes(packet.DataLength));
packet.CRC=_reader.ReadByte();
packet.ETX=_reader.ReadByte();
产生返回包;
}
}   
}
//用法
foreach(stream.ToPacketStream()中的var数据包)
{
//句柄包
}

抱歉,序列化不起作用,因为我只是数据的接收者,无法更改布局以使序列化工作。抱歉,序列化不起作用,因为我只是数据的接收者,无法更改布局以使序列化工作。我可能会添加IDisposable,也许可以称之为ReadPacket——但这是一个很好的答案,+1但为什么这里需要IDisposable?我没有使用任何非托管资源,为什么不让垃圾收集器替我做呢?“非托管资源”将是一个终结器。IDisposable是因为您正在封装/表示可IDisposable的其他内容—流和二进制读取器。您的Dispose()应该调用_reader.Dispose();看来是个好机会。如前所述,需要进行一些错误处理,但我想我会尝试一下。@Marc Gravell:该流最初不是我创建的,在我的类中关闭它会违反直觉。我可能会添加IDisposable,或者称之为ReadPacket-但这是一个很好的答案,+1但为什么这里需要IDisposable?我没有使用任何非托管资源,为什么不让垃圾收集器替我做呢?“非托管资源”将是一个终结器。IDisposable是因为您正在封装/表示可IDisposable的其他内容—流和二进制读取器。您的Dispose()应该调用_reader.Dispose();看来是个好机会。如前所述,需要进行一些错误处理,但我想我会尝试一下。@Marc Gravell:流最初不是我创建的