程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> c#中關於udp實現可靠地傳輸(數據包的分組發送) 升級版

c#中關於udp實現可靠地傳輸(數據包的分組發送) 升級版

編輯:C#入門知識

在c#中關於udp實現可靠地傳輸(數據包的分組發送) 中我們討論了,UDP包的發送,但是上一個程序有一個問題,就是數據比較大,一個Message類序列化後都有2048B,而實際的數據量也就不過 50B罷了,這就說明其中數據有效的很少,這樣當傳送的數據包過多後,效率會極大的降低。因此我們只有想辦法減少冗余數據。

此項目中借用了飛鴿傳書中的一個《FSLib.IPMessager》項目中的思想,並加以改善,感謝此項目作者,讓我對此有了深刻的理解

我們需要自己定義數據的傳輸結構 我們可以定義一個數據頭 其中包含一些基本的必要信息,然後再把要傳送的數據寫入尾部。我把這部分代碼貼出來,要具體完整的項目請到我的資源區下載,由於LZ原創希望給點分哈!

項目下載地址

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;

namespace Netframe.Model
{
    /// 
    /// 消息封包類
    /// 
    public class MessagePacker
    {
        /*
		 * 消息包注意:
		 * 1.第一位始終是2(ASCII碼50)
		 * 2.第二位到第九位是一個long類型的整數,代表消息編號
		 * 3.第十位到第十三位是一個int類型的整數,代表消息內容總長度
		 * 4.第十四位到第十七位是一個int類型的整數,代表分包的總數
		 * 5.第十八位到第二十一位是一個int類型的整數,代表當前的分包編號
		 * 6.第二十二位表示是否需要返回一個確認標識(1/0)
		 * 7.第二十三到第三十一位是保留的(Reserved)
		 * 8.第三十二字節以後是數據包
		 * */

        /// 
        /// 消息版本號
        /// 
        public static byte VersionHeader { get { return 50; } }
        /// 
        /// 返回當前消息封包的頭字節數
        /// 
        public static int PackageHeaderLength { get { return 32; } }

        /// 
        /// 獲得消息包的字節流
        /// 
        /// 要打包的消息對象
        /// 
        public static UdpPacketMsg[] BuildNetworkMessage(Msg message)
        {
            if (message.ExtendMessageBytes != null)
            {
                return BuildNetworkMessage(
                message.RemoteAddr,
                message.PackageNo,
                message.Command,
                message.UserName,
                message.HostName,
                message.Type,
                message.NormalMsgBytes,
                message.ExtendMessageBytes,
                message.IsRequireReceive
                );
            }
            else
            {
                return BuildNetworkMessage(
                message.RemoteAddr,
                message.PackageNo,
                message.Command,
                message.UserName,
                message.HostName,
                message.Type,
                System.Text.Encoding.Unicode.GetBytes(message.NormalMsg),
                System.Text.Encoding.Unicode.GetBytes(message.ExtendMessage),
                message.IsRequireReceive
                );
            }
        }

        /// 
        /// 獲得消息包的字節流
        /// 
        /// 遠程主機地址
        /// 包編號
        /// 命令
        /// 參數
        /// 用戶名
        /// 主機名
        /// 正文消息
        /// 擴展消息
        /// 
        public static UdpPacketMsg[] BuildNetworkMessage(IPEndPoint remoteIp, long packageNo, Commands command, string userName, string hostName,Consts type ,byte[] content, byte[] extendContents, bool RequireReceiveCheck)
        {

            //每次發送所能容下的數據量
            int maxBytesPerPackage = (int)Consts.MAX_UDP_PACKAGE_LENGTH - PackageHeaderLength;
            //壓縮數據流
            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            System.IO.Compression.GZipStream zip = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress);
            System.IO.BinaryWriter bw = new System.IO.BinaryWriter(zip, System.Text.Encoding.Unicode);
            //寫入頭部數據
            bw.Write(packageNo);			//包編號
            bw.Write(userName);				//用戶名
            bw.Write(hostName);				//主機名
            bw.Write((long)command);        //命令
            bw.Write((long)type);           //數據類型
            bw.Write(content == null ? 0 : content.Length);//數據長度

            //寫入消息數據
            if (content != null) 
                bw.Write(content);
            bw.Write(extendContents == null ? 0 : extendContents.Length);//補充數據長度
            if (extendContents != null) 
                bw.Write(extendContents);
            //bw.Close();
            //zip.Close();
            ms.Flush();
            ms.Seek(0, System.IO.SeekOrigin.Begin);

            //打包數據總量
            int dataLength = (int)ms.Length;

            int packageCount = (int)Math.Ceiling(dataLength * 1.0 / maxBytesPerPackage);
            UdpPacketMsg[] pnma = new UdpPacketMsg[packageCount];
            for (int i = 0; i < packageCount; i++)
            {
                int count = i == packageCount - 1 ? dataLength - maxBytesPerPackage * (packageCount - 1) : maxBytesPerPackage;

                byte[] buf = new byte[count + PackageHeaderLength];
                buf[0] = VersionHeader;//版本號 第1位 
                BitConverter.GetBytes(packageNo).CopyTo(buf, 1);//消息編號 第2到9位 long類型的整數
                BitConverter.GetBytes(dataLength).CopyTo(buf, 9);//消息內容長度 第10到13位 int類型的整數
                BitConverter.GetBytes(packageCount).CopyTo(buf, 13);//分包總數 第14位到第17位 int類型的整數
                BitConverter.GetBytes(i).CopyTo(buf, 17);//分包編號 第18位到第21位 int類型的整數
                buf[21] = RequireReceiveCheck ? (byte)1 : (byte)0;//是否回確認包 第22位 
                //第23到第31位是保留的(Reserved)
                ms.Read(buf, 32, buf.Length - 32);//第32字節以後是,具體的數據包

                pnma[i] = new UdpPacketMsg()
                {
                    Data = buf,
                    PackageCount = packageCount,
                    PackageIndex = i,
                    PackageNo = packageNo,
                    RemoteIP = remoteIp,
                    SendTimes = 0,
                    Version = 2,
                    IsRequireReceiveCheck = buf[21] == 1
                };
            }
            bw.Close();
            zip.Close();
            ms.Close();

            return pnma;
        }


        /// 
        /// 檢測確認是否是這個類型的消息包
        /// 
        /// 
        /// 
        public static bool Test(byte[] buffer)
        {
            return buffer != null && buffer.Length > PackageHeaderLength && buffer[0] == VersionHeader;
        }

        /// 
        /// 緩存接收到的片段
        /// 
        static Dictionary packageCache = new Dictionary();

        /// 
        /// 分析網絡數據包並進行轉換為信息對象
        /// 
        /// 接收到的封包對象
        /// 
        /// 
        /// 對於分包消息,如果收到的只是片段並且尚未接收完全,則不會進行解析
        /// 
        public static Msg ParseToMessage(params UdpPacketMsg[] packs)
        {
            if (packs.Length == 0 || (packs[0].PackageCount > 1 && packs.Length != packs[0].PackageCount))
                return null;

            //嘗試解壓縮,先排序
            Array.Sort(packs);
            //嘗試解壓縮
            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            System.IO.Compression.GZipStream zip = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress);
            //System.IO.BinaryWriter bw = new System.IO.BinaryWriter(zip, System.Text.Encoding.Unicode);
            try
            {
                foreach (var s in packs)
                {
                    if (zip.CanWrite)
                    {
                        zip.Write(s.Data, 0, s.Data.Length);
                    }
                }
                
                //Array.ForEach(packs, s => zip.Write(s.Data, 0, s.Data.Length));
            }
            catch (Exception e)
            {
                //觸發事件
                return null;
            }

            zip.Close();
            ms.Flush();
            ms.Seek(0, System.IO.SeekOrigin.Begin);

            //構造讀取流
            System.IO.BinaryReader br = new System.IO.BinaryReader(ms, System.Text.Encoding.Unicode);

            //開始讀出數據
            Msg m = new Msg(packs[0].RemoteIP);
            m.PackageNo = br.ReadInt64();//包編號

            m.UserName = br.ReadString();//用戶名
            m.HostName = br.ReadString();//主機名
            m.Command = (Commands)br.ReadInt64(); //命令
            m.Type = (Consts)br.ReadInt64();//數據類型
            int length = br.ReadInt32(); //數據長度
            m.NormalMsgBytes = new byte[length];
            br.Read(m.NormalMsgBytes, 0, length);//讀取內容

            length = br.ReadInt32();    //附加數據長度
            m.ExtendMessageBytes = new byte[length];
            br.Read(m.ExtendMessageBytes, 0, length);//讀取附加數據

            if (m.Type == Consts.MESSAGE_TEXT)
            {
                m.NormalMsg = System.Text.Encoding.Unicode.GetString(m.NormalMsgBytes, 0, length);	//正文
                m.ExtendMessage = System.Text.Encoding.Unicode.GetString(m.ExtendMessageBytes, 0, length);	//擴展消息
                m.ExtendMessageBytes = null;
                m.NormalMsgBytes = null;

            }
            return m;
        }

        /// 
        /// 嘗試將收到的網絡包解析為實體
        /// 
        /// 收到的網絡包
        /// 
        /// 如果收到的包是分片包,且其所有子包尚未接受完全,則會返回空值
        public static Msg TryToTranslateMessage(UdpPacketMsg pack)
        {
            if (pack == null || pack.PackageIndex >= pack.PackageCount - 1) return null;
            else if (pack.PackageCount == 1) return ParseToMessage(pack);
            else
            {
                if (packageCache.ContainsKey(pack.PackageNo))
                {
                    UdpPacketMsg[] array = packageCache[pack.PackageNo];
                    array[pack.PackageIndex] = pack;

                    //檢測是否完整
                    if (Array.FindIndex(array, s => s == null) == -1)
                    {
                        packageCache.Remove(pack.PackageNo);
                        return ParseToMessage(array);
                    }
                    else
                    {
                        return null;
                    }
                }
                else
                {
                    UdpPacketMsg[] array = new UdpPacketMsg[pack.PackageCount];
                    array[pack.PackageIndex] = pack;
                    packageCache.Add(pack.PackageNo, array);
                    return null;
                }
            }

        }

        /// 
        /// 將網絡信息解析為封包
        /// 
        /// 
        /// 
        public static UdpPacketMsg Parse(byte[] buffer, IPEndPoint clientAddress)
        {
            if (!Test(buffer)) return null;

            UdpPacketMsg p = new UdpPacketMsg()
            {
                RemoteIP = clientAddress,
                SendTimes = 0
            };
            p.PackageNo = BitConverter.ToInt64(buffer, 1);//包編號
            p.DataLength = (int)BitConverter.ToInt64(buffer, 9); //內容長度
            p.PackageCount = BitConverter.ToInt32(buffer, 17);//分包總數
            p.PackageIndex = BitConverter.ToInt32(buffer, 21);//索引
            p.IsRequireReceiveCheck = buffer[21] == 1;//是否需要回包
            p.Data = new byte[buffer.Length - PackageHeaderLength];
            Array.Copy(buffer, PackageHeaderLength, p.Data, 0, p.Data.Length);

            return p;
        }

    }
}


  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved