c#中用于tcp/ip数据传输的消息结构

本文关键字:数据传输 消息 结构 ip 用于 tcp | 更新日期: 2023-09-27 18:15:30

很久以前,我找到了关于为客户端/服务器目的创建和管理消息结构类的有用指南,但我再也找不到它了。

必须包含4个字段:

_FLAG (integer from 0 to 1000)
_FROM (string up to 16 chars)
_TO (same as above)
_DATA (string up to 48 chars)

我希望能够轻松地将其转换为字节数组,通过套接字发送,然后接收,将其转换为结构体并读取。最好的方法是什么?

编辑

谢谢你的回答(也许我不够清楚),但这不是我想要的。我刚刚发现了一些有用的东西,并把它做成这样:

    [MarshalAs(UnmanagedType.U4)]
    public uint _flag;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
    public String _from;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
    public String _to;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
    public String _data;
    public byte[] Serialize()
    {
        var buffer = new byte[Marshal.SizeOf(typeof(DataPacket))];
        var gch = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        var pBuffer = gch.AddrOfPinnedObject();
        Marshal.StructureToPtr(this, pBuffer, false);
        gch.Free();
        return buffer;
    }
    public void Deserialize(ref byte[] data)
    {
        var gch = GCHandle.Alloc(data, GCHandleType.Pinned);
        this = (DataPacket)Marshal.PtrToStructure(gch.AddrOfPinnedObject(), typeof(DataPacket));
        gch.Free();
    }

c#中用于tcp/ip数据传输的消息结构

如果使用现有的序列化器类之一,则需要支付一些费用。您可以创建自己的序列化器,只满足您的需求。在使用标准的东西和您自己的实现之间总是存在权衡。这是我为我的一个项目创建的例子。用户CreateFromBytesStoreToBytes从字节数组中获取或恢复。如果您使用流(在您的情况下也可能有效),那么流版本是很好的,它有助于避免额外的转换。

// well, there's a bad naming for this class as T can be of any type.
public abstract class ClassSerializer<T> : IClassSerializer<T> 
{
    public const int VersionNull = 0;
    public abstract int StoreToStream(Stream stream, T item);
    public abstract T CreateFromStream(Stream stream);
    public virtual byte[] StoreToBytes(T item)
    {
        byte[] result;
        using (var stream = new MemoryStream(16))
        {
            StoreToStream(stream, item);
            result = stream.ToArray();
        }
        return result;
    }
    public virtual T CreateFromBytes(byte[] bytes)
    {
        using (var stream = new MemoryStream(bytes))
        {
            return CreateFromStream(stream);
        }
    }
    public int WriteVersionNull(Stream stream)
    {
        return stream.WriteIntVariableLength(VersionNull);
    }
}

对于特定的类(结构在你的情况下;我建议你去上课;和结构打交道你不会得到很多好处;无论如何,它被转换为字节数组)你应该创建一个特定的序列化器,像这样。

public class DescriptionSerializer : ClassSerializer<Description>
{
    private const byte Version = 1;
    public override int StoreToStream(Stream stream, KeyDescription item)
    {
        if (item == null)
        {
            return WriteVersionNull(stream);
        }
        var count = stream.WriteIntVariableLength(Version);
        count += stream.WriteString(item.StringProperty);
        count += stream.WriteBytes(item.BytesPropery);
        count += stream.WriteIntVariableLength(item.IntegerProperty);
        return count;
    }
    public override Description CreateFromStream(Stream stream)
    {
        var version = stream.ReadIntVariableLength();
        if (version == VersionNull) return null;
        if (version != Version)
        {
            throw new InvalidDataException(
                string.Format("The stream version '{0}' is incorrect. The data cannot be deserialized. ", version));
        }
        var stringProperty = stream.ReadString();
        var bytesProperty = stream.ReadBytes();
        var integerProperty = stream.ReadIntVariableLength();
        return new Description(
            stringProperty, 
            bytesProperty, 
            integerProperty);
    }
}

你需要一个帮助类,可以为流编写不同的基本类型。

public static class SerializationHelper
{
    public static int WriteArray<T>(
        [NotNull] this Stream stream, 
        [NotNull] IClassSerializer<T> serializer, 
        [CanBeNull] T[] items ) where T: class
    {
        if (items == null)
        {
            return stream.WriteIntVariableLength(0);
        }
        var count = stream.WriteIntVariableLength(items.Length);
        foreach (var item in items)
        {
            count += serializer.StoreToStream(stream, item);
        }
        return count;
    }
    public static T[] ReadArray<T>(
        [NotNull] this Stream stream,
        [NotNull] IClassSerializer<T> serializer) where T : class
    {
        var length = stream.ReadIntVariableLength();
        if (length <= 0)
        {
            return new T[0];
        }
        var result = new T[length];
        for (int i = 0; i < length; i++)
        {
            var item = serializer.CreateFromStream(stream);
            result[i] = item;
        }
        return result;
    }
    public static int WriteString([NotNull] this Stream stream, [CanBeNull] string item)
    {
        if (item == null)
        {
            return stream.WriteIntVariableLength(0);
        }
        var buf = Encoding.UTF8.GetBytes(item);
        var count = stream.WriteIntVariableLength(buf.Length);
        stream.Write(buf, 0, buf.Length);
        return count + buf.Length;
    }
    public static string ReadString([NotNull] this Stream stream)
    {
        var len = stream.ReadIntVariableLength();
        if (len <= 0) return null;
        var buf = new byte[len];
        var count = stream.Read(buf, 0, len);
        if (count != buf.Length)
        {
            throw new InvalidDataException("There should be more bytes to read.");
        }
        var result = Encoding.UTF8.GetString(buf);
        return result;
    }
    public static int WriteIntVariableLength([NotNull] this Stream stream, Int32 value)
    {
        var len = unchecked((UInt32)value);
        if (len < 0x80)
        {
            stream.Write(new [] { (byte) (len & 0x7F) }, 0, 1);
            return 1;
        }
        if (len < 0x4000)
        {
            stream.Write(new[] { (byte)((len & 0x7F) | 0x80) }, 0, 1);
            stream.Write(new[] { (byte)(((len>>7) & 0x7F)) }, 0, 1);
            return 2;
        }
        if (len < 0x200000)
        {
            stream.Write(new[] { (byte)((len & 0x7F) | 0x80) }, 0, 1);
            stream.Write(new[] { (byte)(((len >> 7) & 0x7F) | 0x80) }, 0, 1);
            stream.Write(new[] { (byte)((len >> 14) & 0x7F) }, 0, 1);
            return 3;
        }
        if (len < 0x10000000)
        {
            stream.Write(new[] { (byte)((len & 0x7F) | 0x80) }, 0, 1);
            stream.Write(new[] { (byte)(((len >> 7) & 0x7F) | 0x80) }, 0, 1);
            stream.Write(new[] { (byte)(((len >> 14) & 0x7F) | 0x80)  }, 0, 1);
            stream.Write(new[] { (byte)(((len >> 21) & 0x7F)) }, 0, 1);
            return 4;
        }
        stream.Write(new[] { (byte)((len & 0x7F) | 0x80) }, 0, 1);
        stream.Write(new[] { (byte)(((len >> 7) & 0x7F) | 0x80) }, 0, 1);
        stream.Write(new[] { (byte)(((len >> 14) & 0x7F) | 0x80) }, 0, 1);
        stream.Write(new[] { (byte)(((len >> 21) & 0x7F) | 0x80) }, 0, 1);
        stream.Write(new[] { (byte)(((len >> 28) & 0x0F)) }, 0, 1);
        return 5;
    }
    public static Int32 ReadIntVariableLength([NotNull] this Stream stream)
    {
        //var buf = new byte[sizeof(Int32)];
        var b1 = stream.ReadByte();
        if (b1==-1) throw new InvalidDataException("Unexpected end of the stream");
        if ((b1 & 0x80) == 0)
        {
            return b1;
        }
        var b2 = stream.ReadByte();
        if (b2==-1) throw new InvalidDataException("Unexpected end of the stream");
        if ((b2 & 0x80) == 0)
        {
            return (b1 & 0x7F) +  ((b2 & 0x7F) <<7);
        }
        var b3 = stream.ReadByte();
        if (b3==-1) throw new InvalidDataException("Unexpected end of the stream");
        if ((b3 & 0x80) == 0)
        {
            return (b1 & 0x7F) +  ((b2 & 0x7F) <<7) + ((b3 & 0x7F) <<14);
        }
        var b4 = stream.ReadByte();
        if (b4==-1) throw new InvalidDataException("Unexpected end of the stream");
        if ((b4 & 0x80) == 0)
        {
            return (b1 & 0x7F) +  ((b2 & 0x7F) <<7) + ((b3 & 0x7F) <<14) + ((b4 & 0x7F) <<21);
        }
        var b5 = stream.ReadByte();
        if (b5 == -1) throw new InvalidDataException("Unexpected end of the stream");
        return (b1 & 0x7F) + ((b2 & 0x7F) << 7) + ((b3 & 0x7F) << 14) + ((b4 & 0x7F) << 21) + ((b5 & 0x0F) << 28);
    }
    public static int WriteBytes([NotNull] this Stream stream, [CanBeNull]byte[] bytes)
    {
        if (bytes == null)
        {
            return stream.WriteIntVariableLength(0);
        }
        var length = bytes.Length;
        var count = stream.WriteIntVariableLength(length);
        stream.Write(bytes, 0, length);
        return count + length;
    }
    public static byte[] ReadBytes([NotNull] this Stream stream)
    {
        var length = stream.ReadIntVariableLength();
        if (length == 0) return new byte[0];
        var bytes = new byte[length];
        var count = stream.Read(bytes, 0, length);
        if (count != length)
        {
            throw new InvalidDataException("There should be more bytes");
        }
        return bytes;            
    }
}

您可以使用StoreToStreamCreateFromStream方法中的序列化器类来序列化其他类型,如:

    public override int StoreToStream(Stream stream, SomeClass item)
    {
        if (item == null)
        {
            return WriteVersionNull(stream);
        }
        var count = stream.WriteIntVariableLength(Version);
        count += new SubType1Serializer().StoreToStream(stream, item.SubType1);
        count += new SubType2Serializer().StoreToStream(stream, item.SubType2);

虽然我没有提供详细的描述,但我希望这个想法是清晰的。