乙個自定義的自報報文格式(用於感測器自報上傳資料)

2021-08-19 23:41:51 字數 4691 閱讀 3286

本報文格式不能處理粘包問題,因為處理粘包問題的成本太高,會極大的降低服務端的處理效率以及增加記憶體消耗,如果傳輸速度很高,建議使用

udp,

udp傳輸速度快,並且應用層做響應,增加重傳機制可以有效的保證資料的可靠性,目前我做的

rtu公升級等功能都是使用

udp完成,伺服器端開銷小,並且不會粘包。

通訊建議:不要做什麼握手機制,直接傳送資料,伺服器收到了就響應,收到響應後就結束通訊,握手增加耗電又增加流量消耗,並且在

gprs

1個來回,特別是之前用過

mqtt

多了幾個來回,太浪費通訊資源了。

使用hex

格式的通訊協議非常高效,不管是傳送打包還是接收拆包,

1個結構體就能搞定,無需乙個位元組乙個位元組解析,如果用字串

json

方式,使用微控制器作為客戶端就很為難了,一般記憶體不會很多,並且解析

json

需要的記憶體會非常多。

報文全部使用小端模式,便於

c語言處理。

注:由於我之前做了乙個支援

10w個裝置的資料解析軟體,其實占用的記憶體非常少,並且對

cpu消耗很低,我們使用了乙個很低端的伺服器就能做到每秒

1k條以上的資料處理,並且每條資料都在

1秒內得到響應,剛開始的時候還好,後面因為裝置越來越多,導致伺服器搜尋裝置變得很漫長(我的裝置資料都是放到記憶體中,使用了乙個指標陣列進行管理),由於裝置會動態的增加,不能對序列號進行排序,因此查詢裝置的索引時間將會不可控,解析一條資料非常快,儲存是非同步的,有

1w條的快取,但是唯獨查詢裝置這個看似簡單的過程變得非常複雜,由於之前的協議我沒有增加索引,我只能對裝置進行分組,我建了

1w個指標陣列,根據裝置尾號後

4位進行分組,這樣會增加幾十兆的記憶體消耗,但是查詢卻變得非常迅速。通過這個事情之後,我決定在協議中直接增加索引,服務端收到索引後直接取出當前裝置的配置與

sn進行對比,如果相同就直接解析儲存資料,如果不相同就進行查詢即可,沒有就進行新建,最後建議將裝置分組與協議中增加索引進行整合,集各自的優點,可以最大限度的提高資料的解析能力,當每個裝置都通訊一次之後,解析任何乙個裝置上傳的資料的時間都是一樣的,並且是最小的。

禁忌:解析資料是千萬不要把配置什麼的都放到資料庫,資料庫相比記憶體實在是慢太多了,而且時間不可控,資料庫本身就不是非常可靠,建議將資料庫操作做非同步處理,中間用

fifo

通訊,這樣資料解析執行緒可以以最高效的方式執行(乙個執行緒輕鬆解析成千上萬條資料,通過增加乙個執行緒可以提高

1倍的處理能力)。

儲存裝置實時資訊方式

我儲存裝置相關資訊的方式是,每個裝置有乙個自己的統一的結構體,比如我最大支援

10000

個裝置,我就會先申請

1個指標陣列,大小為

10000

,占用40000b

,也就不到

40kb

記憶體。然後每新增乙個裝置,就給這個指標申請乙個記憶體,存放裝置的配置資訊,同時會使用託管的執行緒池非同步向資料庫中增加乙個裝置。

但是如果有幾千上萬個裝置的時候,尋找這個裝置的配置就變得很吃力了(相對來說,記憶體中遍歷都會比資料庫快),這個時候我就會對裝置進行分組,比如分

100組,最好的情況下每個組有

100個裝置,但是不排除有一種可能,乙個組中有超過

100個裝置的呀,我就要做乙個二維指標陣列,分

100個陣列,每個陣列中

1000

個裝置指標,這樣記憶體占用就會是

100*1000*4

,也還好

400k

,但是如果乙個組超過了

1000

個就不好辦了,同樣如果是

10w個裝置的時候,這個分組就很吃記憶體了,我的做法是每個分組用個變數進行記錄,每個組給

50個裝置指標,當這個組超過

50個之後,我重新對這個指標進行申請記憶體,大小為上一次的

+50個,然後把之前的資料拷貝過來,釋放掉之前的資料,這樣就可以動態的進行分組控制,實現效率的提公升與記憶體的最小消耗。

使用分組提高查詢的乙個例子

//查詢指定sn的裝置索引

//返回索引;-1:沒有找到

//2017-12-27 : 會使用分組進行快速查詢索引,只能在單執行緒中使用,不要跨執行緒搜尋

int findsn(char psn[16])

groupindex = temp[0] * 100 + temp[1] * 10 + temp[2];//計算當前裝置的分組索引

if (groupindex >= device_group_cnt) return -1; //分組索引無效

hash = user_lib.bkdrhash(psn); //計算雜湊結果

//在指定的分組內去搜尋當前裝置

for (dword i = 0; i < this->groupdevicecnt[groupindex]; i++)

}return -1;

}

新增裝置的例子,先新增到全域性的配置陣列中,然後再把指標儲存乙份到分組索引中

//新增乙個裝置到配置緩衝區,返回索引,<0:新增失敗

int adddevice(device_config_type *pconfig, char **perror)

if (this->devicecnt >= device_max_cnt) //裝置數量超出範圍

pconfig->sn[15] = 0;

if (strlen(pconfig->sn) != 15)

//檢查最後3位是否為000-999

temp[0] = pconfig->sn[12] - '0'; //字元轉換為數字

temp[1] = pconfig->sn[13] - '0'; //字元轉換為數字

temp[2] = pconfig->sn[14] - '0'; //字元轉換為數字

if (temp[0] > 9 || temp[1] > 9 || temp[2] > 9)

groupindex = temp[0] * 100 + temp[1] * 10 + temp[2]; //計算當前裝置的分組索引

if (groupindex >= device_group_cnt)

if (findsn(pconfig->sn) >= 0) //尋找指定位址的裝置是否存在

p = new one_device_data_type; //申請記憶體

memset(p, 0, sizeof(one_device_data_type)); //清控配置區域

memcpy(&p->config, pconfig, sizeof(device_config_type)); //拷貝資料

p->config.hash = user_lib.bkdrhash(p->config.sn); //生成sn的雜湊值

p->dbstatus.readhistcongifcnt = p->dbstatus.readhiststatuscnt = p->dbstatus.writehistcongifcnt = -1; //將歷史記錄條數置為-1無效值

p->index = this->devicecnt; //記錄當前裝置的索引

//將當前裝置編號進行分組

if (this->adddevicetogroup(groupindex, (dword)p) == false)

u32_devicedatapointerbuff[this->devicecnt] = (dword)p; //存放指標

pdevicedatapointerbuff[this->devicecnt++] = p; //儲存指標

return (this->devicecnt - 1); //返回當前的索引

}

新增裝置到分組中,如果分組滿了將會進行擴充

//新增乙個裝置到指定分組中,如果分組慢,將會申請擴容

//不會進行編號等檢查,只能在adddevice中被呼叫

bool adddevicetogroup(int groupindex, dword devicedatapointer)

sys_log.write("分組" + groupindex + "擴容成功\r\n");

} this->pdevicedatagrouppointerbuff[groupindex][this->groupdevicecnt[groupindex]] = devicedatapointer; //儲存當前裝置指標

this->groupdevicecnt[groupindex] ++; //分組內的裝置數量增加

return true;

}

//為指定的分組進行擴容,會先複製緩衝區,然後重新申請,再釋放之前的緩衝區(注意:使用分組索引只能在乙個執行緒中使用)

//必須分組已經滿了再呼叫

bool devicegroupexpansion(int groupindex)

後面我會擴充套件使用這個協議的下位機與服務端框架,並提供相應的測試例子。

分享乙個可以自定義日期格式的方法

相信大家在開發過程中,經常會需要用到各種的日期格式,如 2020 05 07 2020年5月7日 2020年5月7日 15 52 47 等等的日期格式要求,不知道大家都寫過多少遍的轉日期格式的方法了,反正天某是寫煩了,於是就產生了想要封裝乙個工具出來的想法。export class tools re...

乙個自定義訊息的例子

h define idm myclosepress 45571 lresult onmyclose wparam wparam,lparam lparam cpp begin message map cprogress,cdialog on wm close on message idm myclo...

自定義的乙個日曆Calender

產品要做簽到功能,簽到功能要基於乙個日曆來進行,所以就根據 要求自定義了乙個日曆 自定義控制項相信做android都知道 1 首先建立乙個類,繼承乙個容器類或者是乙個控制項 2 然後就是你需要設定的屬性等的,在attrs資料夾中 3 然後就是在類裡邊進行屬性的設定以及布局等等功能的新增 其實自定義乙...