C#中tcp / ip数据传输的消息结构

时间:2016-10-30 15:38:03

标签: c# sockets struct tcp client-server

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

必须包含4个字段:

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

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

修改

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

    [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();
    }

1 个答案:

答案 0 :(得分:0)

如果您使用现有的序列化程序类之一,它们会带来一些费用。您可以创建自己的序列化程序,仅满足您的需求。在使用标准和自己的实现之间总是存在权衡。这是我为我的一个项目创建的示例。用户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);
    }
}

对于特定的类(在你的情况下结构;我建议你使用类;你没有从结构中获得很多好处;无论如何它转换为字节数组)你应该创建一个像这样的特定序列化器。 / p>

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);

我希望这个想法很清楚,但我没有提供详细的说明。