1. 程式人生 > >《連載 | 物聯網框架ServerSuperIO教程》- 9. 協議過濾器,解決一包多發、粘包、冗餘資料

《連載 | 物聯網框架ServerSuperIO教程》- 9. 協議過濾器,解決一包多發、粘包、冗餘資料

目       錄

9. 協議過濾器,解決一包多發、粘包、冗餘資料... 2

9.1  概述... 2

9.2 實際問題... 2

9.3 5種過濾器及二次開發... 5

9.4 裝置驅動開發注意事項... 6

9.5 宿主程式服務例項配置注意事項... 6

9. 協議過濾器,解決一包多發、粘包、冗餘資料

9.1    概述

     通訊中涉及到資料包的概念,是通訊協議中的資料組成形式。針對這塊內容,說簡單也簡單,說複雜也複雜。需要我們系統性的把問題考慮全面,並用程式碼實現。

     在工業領域也有極端的情況出現,早些年做通訊的時候,資料包頭、包尾、資料長度、資料校驗位都對,但是就是解析出來的資料不正確,這種情況不會經常出現,但是在某種特殊應用環境可能會頻繁出現,後來經過分析得出結論:可能是由於地質電磁干擾引起的。但是也有技術上的設計缺陷,例如:資料校驗位是累加和,改成CRC是不是就不會出來這個問題了;另外對於增量資料,應該有補發機制等等。

9.2    實際問題

  1. 1.      一包多發及解決

     多包傳送資料是應用環境中的一種情況或一個問題,並不是我們會這樣實際應用,而是說在接收過程中多次接收資料才能完整接收客戶端一次傳送的資料,可能由於網路環境或傳送資料端造成的,示意如下圖:

 

     例如實時資料的完整包為:55 AA 00 61 43 7A 00 00 43 B4 15 0D。那麼接收資料的時候,第一次接收到:55 AA 00 61 43 7A 00 00 43 B4 15,第二次接到:0D。按通訊協議應該能夠把這兩次接收的資料進行自動拼接,形成完整的資料並進行解析。

     ServerSuperIO設定了協議過濾器,可以解決這個問題,如下圖:

  1. 2.      粘包及解決

    我原來並不知道粘包這個概念,後來在網上看文章才明白。在通訊領域中也是經常會遇到的問題。也就是多包資料一次性的接收,那麼就要合理的進行拆包。還有一種情況,就是多包半的資料一次性的接收,那半包的資料結合“1.一包多發及解決”來解決這個問題,示意如下圖:

 

    ServerSuperIO設定了協議過濾器,可以解決這個問題,如下圖:

  1. 3.      冗餘資料的出現及解決

       在工業領域受電纜或環境的電磁干擾,以及接頭虛接等,這種情況極有可能出現。如果幹擾的冗餘資料夾雜在一個協議包中間,那麼校驗出合法的資料很困難。如果幹擾的冗餘資料夾雜在兩個協議包中間,那麼就可以通過協議過濾來實現識別出有用的資料。示意如下圖:

 

      ServerSuperIO設定了協議過濾器,可以解決這個問題,如下圖:

9.3    5種過濾器及二次開發

   FixedEndReceiveFliter:固定結尾的協議過濾器。
   FixedHeadAndEndReceiveFliter:固定開頭和結尾的協議過濾器。
   FixedHeadAndLengthReceiveFliter:固定開頭和長度的協議過濾器。
   FixedHeadReceiveFliter:固定開頭的協議過濾器。
   FixedLengthReceiveFliter:固定長度的協議過濾器。

    這5個過濾器都繼承自IReceiveFilter介面,也可以繼承這個介面進行二次開發,定製自己的協議過濾器。程式碼工程如下圖:

 

9.4    裝置驅動開發注意事項

     對於開發裝置驅動,在初始化過程中可以增加這個驅動的協議過濾器,程式碼如下:

public override void Initialize(string devid)
{
            this.Protocol.InitDriver(this.GetType(),new FixedHeadAndEndReceiveFliter(new byte[] {0x55,0xaa},new byte[] {0x0d} ));
……
}

9.5    宿主程式服務例項配置注意事項

    在配置引數中需要配置:StartReceiveDataFliter = true,協議過濾器才能起到作用。程式碼如下:

static void Main(string[] args)
{
            DeviceSelfDriver dev2 = new DeviceSelfDriver();
            dev2.DeviceParameter.DeviceName = "網路裝置";
            dev2.DeviceParameter.DeviceAddr = 1;
            dev2.DeviceParameter.DeviceID = "1";
            dev2.DeviceDynamic.DeviceID = "1";
            dev2.DeviceParameter.DeviceCode = "1";
            dev2.DeviceParameter.NET.RemoteIP = "127.0.0.1";
            dev2.DeviceParameter.NET.RemotePort = 9600;
            dev2.CommunicateType = CommunicateType.NET;
            dev2.Initialize("1");

            IServer server = new ServerManager().CreateServer(new ServerConfig()
            {
                ServerName = "服務1",
                ComReadTimeout = 1000,
                ComWriteTimeout = 1000,
                NetReceiveTimeout = 1000,
                NetSendTimeout = 1000,
                ControlMode = ControlMode.Self,
                SocketMode = SocketMode.Tcp,
                StartReceiveDataFliter = true,
                ClearSocketSession = false,
                StartCheckPackageLength = false,
                CheckSameSocketSession = false,
                DeliveryMode = DeliveryMode.DeviceIP,
            });

            server.AddDeviceCompleted += server_AddDeviceCompleted;
            server.DeleteDeviceCompleted+=server_DeleteDeviceCompleted;
            server.Start();  

            server.AddDevice(dev2);

            while ("exit" == Console.ReadLine())
            {
                server.Stop();
            }
}

物聯網&整合技術(.NET) QQ群54256083