高性能TcpServer - 3.命令通道(處理:掉包,粘包,垃圾包)

来源:https://www.cnblogs.com/chen1880/archive/2019/07/24/11238699.html
-Advertisement-
Play Games

高性能TcpServer - 1.網路通信協議 高性能TcpServer - 2.創建高性能Socket伺服器SocketAsyncEventArgs的實現(IOCP) 高性能TcpServer - 3.命令通道(處理:掉包,粘包,垃圾包) 高性能TcpServer - 4.文件通道(處理:文件分包 ...


高性能TcpServer(C#) - 1.網路通信協議

高性能TcpServer(C#) - 2.創建高性能Socket伺服器SocketAsyncEventArgs的實現(IOCP)

高性能TcpServer(C#) - 3.命令通道(處理:掉包,粘包,垃圾包)

高性能TcpServer(C#) - 4.文件通道(處理:文件分包,支持斷點續傳)

高性能TcpServer(C#) - 5.客戶端管理

高性能TcpServer(C#) - 6.代碼下載

 

處理原理

每個client創建各自的byte[]數組,通過遍歷每個位元組的數據

1.判斷包長,確定掉包;

2.判斷解析完後byte數組是否還有未解析的數據,確定粘包;

3.判斷包頭,確定垃圾包;

 

緩存數據類

 /// <summary>

    /// 緩存數據類

    /// </summary>

    public class CByteBuffer

    {

        // 預設1k

        int m_iBufferSize = 1024 * 1;

 

        // 數據解析

        byte[] m_abyBuf;

        int m_iPosition = 0;

        int m_iRecvLength = 0;

        bool bWaitRecvRemain;// 數據未接收完等待接收

        object m_lock = new object(); // 內部同步鎖

 

        public int Position

        {

            get { return m_iPosition; }

            set { m_iPosition = value; }

        }

 

        public int RecvLength

        {

            get { return m_iRecvLength; }

            set { m_iRecvLength = value; }

        }

 

        public bool WaitRecvRemain

        {

            get { return bWaitRecvRemain; }

            set { bWaitRecvRemain = value; }

        }

 

        public CByteBuffer(int buffSize)

        {

            m_iBufferSize = buffSize;

            m_abyBuf = new byte[m_iBufferSize];

        }

 

        public int GetPosition()

        {

            return m_iPosition;

        }

 

        public int GetRecvLength()

        {

            return m_iRecvLength;

        }

 

        public void Put(SocketAsyncEventArgs e)

        {

            int iLength = e.BytesTransferred;

            if (m_iRecvLength + iLength >= m_iBufferSize)

            {

                Clear();

                return;

            }

 

            lock (m_lock)

            {

                Array.Copy(e.Buffer, e.Offset, m_abyBuf, m_iRecvLength, iLength);

                m_iRecvLength += iLength;

            }

        }

 

        public byte GetByte()

        {

            bWaitRecvRemain = false;

 

            if (m_iPosition >= m_iRecvLength)

            {

                bWaitRecvRemain = true;

                return 0;

            }

 

            byte byRet;

            lock (m_lock)

            {

                byRet = m_abyBuf[m_iPosition];

            }

            m_iPosition++;

 

            return byRet;

        }

 

        public byte[] GetByteArray(int Length)

        {

            bWaitRecvRemain = false;

 

            if (m_iPosition + Length > m_iRecvLength)

            {

                bWaitRecvRemain = true;

                return null;

            }

 

            byte[] ret = new byte[Length];

 

            lock (m_lock)

            {

                Array.Copy(m_abyBuf, m_iPosition, ret, 0, Length);

 

                m_iPosition += Length;

            }

 

            return ret;

        }

 

        public bool HasRemaining()

        {

            return m_iPosition < m_iRecvLength;

        }

 

        public int Remaining()

        {

            return m_iRecvLength - m_iPosition;

        }

 

        public void Clear()

        {

            m_iPosition = 0;

            m_iRecvLength = 0;

            bWaitRecvRemain = false;

        }

 

        ~CByteBuffer()

        {

            m_abyBuf = null;

            Dispose(false);

        }

 

        protected virtual void Dispose(bool disposing)

        {

            if (disposing)

            {

                GC.SuppressFinalize(this);

            }

        }

 

        public void Dispose()

        {

            Dispose(true);

        }

    }

協議解析類

        public void Process(CByteBuffer bBuffer, CProtocolAnalysis analysis, string sn)

        {

            analysis.BagStatus = CProtocolAnalysis.EBagStatus.BagNone;

            analysis.WhetherToSend = false;

 

            int iPosition = bBuffer.Position;

            byte head1 = 0; byte head2 = 0; byte head3 = 0; byte head4 = 0; byte head5 = 0; byte head6 = 0; bool headok = false;

 

            if (!bBuffer.HasRemaining()) return;

 

            while (bBuffer.HasRemaining())

            {

                head1 = bBuffer.GetByte(); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;

                if (HEAD1 == head1)

                {

                    iPosition = bBuffer.Position - 1;

                    head2 = bBuffer.GetByte(); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;

                    head3 = bBuffer.GetByte(); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;

                    head4 = bBuffer.GetByte(); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;

                    head5 = bBuffer.GetByte(); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;

                    head6 = bBuffer.GetByte(); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;

                    if (HEAD2 == head2 && HEAD3 == head3 && HEAD4 == head4 && HEAD5 == head5 && HEAD6 == head6)

                    {

                        headok = true;

                        break;

                    }

                    else

                    {

                        CLogHelp.AppendLog("Error,Unable to parse the data2:Position=" + iPosition.ToString() + ",Index=" + (bBuffer.GetPosition()).ToString() + ",Head2=" + head2.ToString());

                    }

                }

                else

                {

                    CLogHelp.AppendLog("Error,Unable to parse the data1:Position=" + iPosition.ToString() + ",Index=" + (bBuffer.GetPosition()).ToString() + ",Head1=" + head1.ToString());

                }

            }

 

            if (!bBuffer.HasRemaining())

            {

                if (headok)

                {

                    if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;

                }

                return;

            }

 

            byte[] arrlen = bBuffer.GetByteArray(4); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;

            int len = CCommonFunc.String2Int(CCommonFunc.ByteToString(arrlen)); if (-1 == len) return;

            byte[] source = bBuffer.GetByteArray(len); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;

 

            if (!bBuffer.HasRemaining())

            {

                bBuffer.Clear();

            }

            else

            {

                analysis.BagStatus = CProtocolAnalysis.EBagStatus.BagStick;

            }

 

            // #WaterMeter-001#01##

            string data = CCommonFunc.ByteToString(source);

            if (null == data || 0 == data.Length || data.Length - 1 != data.LastIndexOf(SPLIT1))

            {

                return;

            }

            data = data.Substring(1, data.Length - 2);

            string[] item = data.Split(SPLIT1);

            if (null == item || 4 != item.Length)

            {

                return;

            }

            string uid = item[0];

            string taskid = item[1];

            int cmd = CCommonFunc.String2Int(item[2]);

            string content = item[3];

            Program.AddMessage("R: [" + sn + "] cmd=" + cmd.ToString() + " data=" + data);

 

            analysis.Cmd = cmd;

            analysis.Uid = uid;

            analysis.TaskId = taskid;

 

            if (cmd == 1 || cmd == 2 || cmd == 3 || cmd == 4 || cmd == 5 || cmd == 6 || cmd == 7)

            {

                analysis.WhetherToSend = true;

            }

 

            string softtype = "";  

 

            try

            {

                switch (cmd)

                {

                    case 1:

                        analysis.Msg = "ok";

                        break;

                    case 2:

                        analysis.Msg = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");

                        break;

                    case 3:

                        // HTEMP=0263#WaterMeter-001#1520557004#03#buildid=44@edmid=37@meterid=1228@senddate=2018-02-05 17:36:22@[{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}]#

                        analysis.Msg = "ok";

                        break;

                    case 4:

                        {

                            // 獲取版本信息

                            softtype = content.Split(SPLIT2)[1];

                            StorageFile(softtype, System.Windows.Forms.Application.StartupPath + "\\test.zip");

                            analysis.Msg = "2";// version

                        }

                        break;

                    case 5:

                        // 獲取包數

                        {

                            softtype = content.Split(SPLIT2)[1];

                            if (!dicSoft.ContainsKey(softtype))

                            {

                                StorageFile(softtype, System.Windows.Forms.Application.StartupPath + "\\test.zip");

                            }

                            // 獲取包數

                            int count = 0;

                            FileCut entity = null;

                            dicSoft.TryGetValue(softtype, out entity);

                            if (null != entity) count = entity.Count;

                            analysis.Msg = count.ToString();

                        }

                        break;

                    case 6:

                        // 執行更新動作

                        {

                            string[] items = content.Split(SPLIT2);

                            softtype = items[1];

                            int downindex = CCommonFunc.String2Int(items[2]);

                            if (!dicSoft.ContainsKey(softtype))

                            {

                                analysis.Msg = "error@" + softtype + " 未找到更新文件,請先獲取包數";

                            }

                            else

                            {

                                FileCut entity = null;

                                dicSoft.TryGetValue(softtype, out entity);

                                if (null != entity)

                                {

                                    string filedata = "";

                                    entity.Data.TryGetValue(downindex, out filedata);

                                    if (string.IsNullOrEmpty(filedata))

                                        analysis.Msg = "error@" + softtype + " 第" + downindex + "包的數據為空";

                                    else

                                        analysis.Msg = filedata;

                                }

                            }

                        }

                        break;

                    case 7:

                        // 更新版本信息(update sql)

                        analysis.Msg = "ok";

                        break;

                }

            }

            catch (Exception ex)

            {

                analysis.Msg = "error@" + ex.Message;

            }

            Program.AddMessage("S: [" + sn + "] cmd=" + cmd.ToString() + " data=" + analysis.Msg);

        }

測試效果

正常包

HTEMP=0026#Meter-001#1533022506#01##

 

 

掉包(分兩包發送)

HTEMP=0026#

Meter-001#1533022506#01##

 

 

粘包(兩包一起發送)

HTEMP=0026#Meter-001#1533022506#01##HTEMP=0026#Meter-001#1533022506#01##

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 9.94 守護線程與守護進程的區別 9.95 線程互斥鎖 9.96 死鎖現象與遞歸鎖 mutexA=mutexB=RLock() 一個線程拿到鎖,counter加1,該線程內又碰到加鎖的情況,則counter繼續加1,這期間所有其他線程都只能等待,等待該線程釋放所有鎖,即counter遞減到0為止 ...
  • LeetCode:135. 分發糖果 老師想給孩子們分發糖果,有 N 個孩子站成了一條直線,老師會根據每個孩子的表現,預先給他們評分。 你需要按照以下要求,幫助老師給這些孩子分發糖果: 每個孩子至少分配到 1 個糖果。 相鄰的孩子中,評分高的孩子必須獲得更多的糖果。 那麼這樣下來,老師至少需要準備多 ...
  • .NET Core CSharp 初級篇 1 6 本節內容為類的多態與繼承 簡介 終於講到了面向對象三大特性中的兩大特性——繼承與多態。通過繼承與多態,我們能很好的將類的拓展性發揮到了極致。在下麵的內容講解中,我們將從各個方面對繼承和多態進行刨析。 繼承 繼承事實上是一個非常好理解的語法,在C 中實 ...
  • 1. 前言 上一篇文章介紹了使用Resizer實現Expander簡單的動畫效果,運行效果也還好,不過只有展開/摺疊而缺少了淡入/淡出的動畫(畢竟Resizer模仿Expander只是附帶的功能)。這篇繼續Measure的話題,自定義了一個帶有動畫的ExtendedExpander。 2. Exte ...
  • 使用 MiniProfiler 來分析 ASP.NET Core 應用 MiniProfiler( "https://miniprofiler.com/" )是一個輕量級且簡單易用的分析工具庫,它可以用來分析ASP.NET Core應用。 優點 針對ASP.NET Core MVC應用,使用Mini ...
  • C#進階系列——WebApi 介面測試工具:WebApiTestClient 一、WebApiTestClient介紹 1、WebApiTestClient組件作用主要有以下幾個: (1)、將WebApi的介面放到了瀏覽器裡面,以可視化的方式展現出來,比如我們通過http://localhost:8 ...
  • C# string 字元串的前面可以加 @(稱作"逐字字元串")將轉義字元(\)當作普通字元對待,比如: ...
  • 高性能TcpServer - 1.網路通信協議 高性能TcpServer - 2.創建高性能Socket伺服器SocketAsyncEventArgs的實現(IOCP) 高性能TcpServer - 3.命令通道(處理:掉包,粘包,垃圾包) 高性能TcpServer - 4.文件通道(處理:文件分包 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...