c#局域网屏幕广播二三事(2)-拆包组包

拆包组包提炼于一位大拿的文章,这里提供链接:

c#中关于udp实现可靠地传输(数据包的分组发送)


仅仅使用了大拿所设计的包的拆分、组装与序列化部分。

在此工程中,将拆包组包模块单独作为了一个library工程,包含了PacketHelper文件。

其中包序列化和反序列化类如下:

public class SerializationUnit
    {
        public static byte[] SerializeObject(object obj)
        {
            if (obj == null)
                return null;
            MemoryStream ms = new MemoryStream();
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(ms, obj);
            ms.Position = 0;
            byte[] bytes = ms.GetBuffer();
            ms.Read(bytes, 0, bytes.Length);
            ms.Close();
            return bytes;
        }

        public static object DeserializeObject(byte[] bytes)
        {
            object obj = null;
            if (bytes == null)
                return obj;
            MemoryStream ms = new MemoryStream(bytes);
            ms.Position = 0;
            BinaryFormatter formatter = new BinaryFormatter();
            obj = formatter.Deserialize(ms);
            ms.Close();
            return obj;
        }
    }

屏幕数据包设计如下,在项目中屏幕广播的接收端并不是C#所写,在此进行了提炼,省去了其他的包体信息等:

[Serializable]
    public class CastPacket
    {
        public double TimeStamp { get; set; }
        public Int32 Index { get; set; }
        public Int32 Total { get; set; }
        public Int32 TotalLength { get; set; }
        public Int32 DataOffset { get; set; }
        public Int32 DataLength { get; set; }
        public byte[] Data { get; set; }


        public CastPacket(double timeStamp, Int32 total, Int32 index, byte[] data, Int32 dataOffset, Int32 totalLength)
        {
            this.TimeStamp = timeStamp;
            this.Total = total;
            this.Index = index;
            this.Data = data;
            this.DataOffset = dataOffset;
            this.DataLength = data.Length;
            this.TotalLength = totalLength;
        }

        public byte[] ToArray()
        {
            return SerializationUnit.SerializeObject(this);
        }
    }

拆包类为:
public class PacketSplitter
    {
        public static ICollection<CastPacket> Split(double TimeStamp, byte[] data, int chunkLength = 8174)
        {
            List<CastPacket> packets = new List<CastPacket>();

            int chunks = data.Length / chunkLength;
            int remainder = data.Length % chunkLength;
            int total = chunks;

            if (remainder > 0) total++;

            for (int i = 1; i <= chunks; i++)
            {
                byte[] chunk = new byte[chunkLength];
                Buffer.BlockCopy(data, (i - 1) * chunkLength, chunk, 0, chunkLength);
                packets.Add(new CastPacket(TimeStamp, total, i, chunk, chunkLength * (i - 1), data.Length));
            }

            if (remainder > 0)
            {
                int length = data.Length - (chunkLength * chunks);
                byte[] chunk = new byte[length];
                Buffer.BlockCopy(data, chunkLength * chunks, chunk, 0, length);
                packets.Add(new CastPacket(TimeStamp, total, total, chunk, chunkLength * chunks, data.Length));
            }

            return packets;
        }

    }

组包事件和组包类为:
public class PacketCollectorEventArgs : EventArgs
    {
        public byte[] data;
    }

    public class PacketCollector
    {
        private Object ObjLock = new Object();
        public EventHandler<PacketCollectorEventArgs> OnCollectorEventHandler;
        private Dictionary<double, List<CastPacket>> DicCollectedPacket = new Dictionary<double, List<CastPacket>>();
        private double CurrentTimeStamp = -1;

        private Thread CollectThreadHandler;
        private Boolean bRunning;

        public PacketCollector()
        {
            bRunning = true;
            CollectThreadHandler = new Thread(new ThreadStart(CollectThread)) { IsBackground = true };
            CollectThreadHandler.Start();
        }

        public void Dispose()
        {
            bRunning = false;
            //WinHelper.WaitForThreadExit(CollectThreadHandler);
            DicCollectedPacket = null;
        }

        private void CollectThread()
        {
            while (bRunning == true)
            {
                lock (ObjLock)
                {
                    if (DicCollectedPacket.Count == 0) continue;

                    var SortedDic = DicCollectedPacket.OrderBy(p => p.Key).ToDictionary(p => p.Key, p => p.Value);

                    foreach (var ListPack in SortedDic)
                    {
                        if (CurrentTimeStamp - ListPack.Key >= 2000)
                        {
                            //Console.WriteLine("Abbanded package.");
                            DicCollectedPacket.Remove(ListPack.Key);
                            continue;
                        }

                        if (ListPack.Value.Count == ListPack.Value[0].Total)
                        {
                            RePackMsg(ListPack.Value);
                            DicCollectedPacket.Remove(ListPack.Key);
                        }
                    }

                }
                Thread.Sleep(5);
            }
        }

        public void Collect(Byte[] Msg)
        {

            CastPacket Pac = (CastPacket)SerializationUnit.DeserializeObject(Msg);
            if (Pac == null)
                return;

            if (Pac.Data == null)
                return;

            lock (ObjLock)
            {
                List<CastPacket> ListPacket = null;
                Boolean bContain = DicCollectedPacket.TryGetValue(Pac.TimeStamp, out ListPacket);
                if (bContain == false)
                {
                    ListPacket = new List<CastPacket>();
                    ListPacket.Add(Pac);
                    DicCollectedPacket.Add(Pac.TimeStamp, ListPacket);
                }
                else
                {
                    ListPacket.Add(Pac);
                }
                CurrentTimeStamp = Math.Max(Pac.TimeStamp, CurrentTimeStamp);
            }

        }

        private void RePackMsg(List<CastPacket> listPack)
        {
            Byte[] Data = new Byte[listPack[0].TotalLength];
            foreach (var Pac in listPack)
            {
                try
                {
                    Buffer.BlockCopy(Pac.Data, 0, Data, Pac.DataOffset, Pac.Data.Length);
                }
                catch
                {
                    Console.WriteLine("Wrong package.");
                    return;
                }
            }

            if (this.OnCollectorEventHandler != null)
                this.OnCollectorEventHandler(this, new PacketCollectorEventArgs() { data = Data });
        }

    }

以上即为拆包组包所用到的共用模块,很奇怪的一点是,我把这个类分别放在客户端和服务端中会报出转换异常,还没找到原因。


源码传送门:

C#屏幕广播源码V1.0



评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值