
一、開(kāi)啟TCP服務(wù)
TCP開(kāi)啟在application.yml文件中控制。
提示
tcp.enabled: 開(kāi)啟后, TCP服務(wù)器啟動(dòng)連接設(shè)備
openws: 開(kāi)啟后,配合TCP服務(wù),推送設(shè)備消息至前端實(shí)時(shí)更新

二、TCP設(shè)備連接
1. 注冊(cè)包:
由于TCP設(shè)備連接,需要識(shí)別設(shè)備,因此TCP客戶(hù)端連接時(shí),上報(bào)的第一個(gè)報(bào)文為客戶(hù)端的注冊(cè)包。
例如:7e80D4AD203F3A1C7e
其中 7e 是包頭包尾
80是注冊(cè)包的標(biāo)識(shí)位
D4AD203F3A1C 是設(shè)備編號(hào)
客戶(hù)端上報(bào)注冊(cè)包后,更新設(shè)備狀態(tài)為在線
2. 心跳包:
TCP需要主動(dòng)發(fā)送消息來(lái)維持設(shè)備的心跳保持,上圖中 keep-alive 是心跳超時(shí)的判定時(shí)間 70s,因此設(shè)備的心跳需要維持在70s以下。
心跳包例如: 7e817e
7e 是包頭包尾,81是標(biāo)識(shí)位 (心跳包只是維持TCP長(zhǎng)連接,一般無(wú)其他實(shí)際意義)
3. 特殊的注冊(cè),心跳 數(shù)據(jù)包:
在有些設(shè)備客戶(hù)端,會(huì)把設(shè)備的注冊(cè)包,心跳包,數(shù)據(jù)包,定時(shí)上報(bào),一方面是包含了設(shè)備編號(hào),也可以維持了心跳,數(shù)據(jù)包還包含了設(shè)備上報(bào)的信息,這種情況系統(tǒng)也是可以支持的。
三、TCP模擬客戶(hù)端測(cè)試
模擬客戶(hù)端放在如下文件


1. 模擬客戶(hù)端上線
發(fā)送注冊(cè)包:配置好后,點(diǎn)擊鏈接,發(fā)送注冊(cè)包,如下 7e80D4AD203F3A1C7e
這個(gè)時(shí)候,看下本地啟動(dòng)服務(wù)可以看到
- a. TCP客戶(hù)端上線
- b. 更新TCP客戶(hù)的狀態(tài)
- c. 記錄設(shè)備事件記錄


2. 模擬TCP客戶(hù)端斷電或 斷開(kāi)
斷電需要等待設(shè)備心跳超時(shí)(70S), 斷開(kāi)連接是,是實(shí)時(shí)上報(bào)設(shè)備狀態(tài)
斷開(kāi)連接是,打印信息如下,更新設(shè)備上下線狀態(tài),新增設(shè)備事件記錄,前端ws推送實(shí)時(shí)更新


同樣模擬客戶(hù)端斷電,需要等待客戶(hù)端超過(guò)心跳超時(shí)時(shí)間 (70s),更新動(dòng)作跟設(shè)備斷開(kāi)一致。
四、TCP客戶(hù)端前端連接展示
新建產(chǎn)品,傳輸協(xié)議選擇TCP,通訊協(xié)議可以選擇 JSON、ModbusTcpOverRtu協(xié)議、ModbusRtu協(xié)議

新建產(chǎn)品后,選中產(chǎn)品新建設(shè)備即可。
五、TCP + MODBUS
- 概述:Modbus TCP 報(bào)文由以下幾個(gè)主要部分組成:
- MBAP 報(bào)文頭(Modbus Application Protocol Header):
- 事務(wù)標(biāo)識(shí)符(Transaction Identifier):用于匹配請(qǐng)求和響應(yīng),通常由客戶(hù)端生成。
- 協(xié)議標(biāo)識(shí)符(Protocol Identifier):固定為 0,表示 Modbus 協(xié)議。
- 長(zhǎng)度(Length):后續(xù)數(shù)據(jù)的字節(jié)長(zhǎng)度,包括單元標(biāo)識(shí)符和 PDU (Protocol Data Unit)。
- 單元標(biāo)識(shí)符(Unit Identifier):用于標(biāo)識(shí)從站設(shè)備。
- PDU(Protocol Data Unit):
- 功能碼(Function Code):指示要執(zhí)行的操作,如讀取、寫(xiě)入等。
- 數(shù)據(jù)(Data):根據(jù)功能碼的不同,包含相應(yīng)的操作數(shù)據(jù)。
使用:產(chǎn)品配置時(shí)選擇ModbusTcpOverRtu協(xié)議,具體配置方式參考網(wǎng)關(guān)與子設(shè)備文檔
可以在設(shè)備詳情-數(shù)據(jù)調(diào)試、數(shù)據(jù)采集頁(yè)面下發(fā)指令,如果采用云端輪詢(xún)方式,可配置輪詢(xún)?nèi)蝿?wù)下發(fā)指令:


云端輪詢(xún),用模擬設(shè)備可以看到下發(fā)到設(shè)備的讀指令,如下:

六、TCP+JSON
這里采用動(dòng)態(tài)傾角監(jiān)測(cè)儀(通訊協(xié)議)2023-3-1 103136
設(shè)備通訊協(xié)議進(jìn)行演示。(應(yīng)答機(jī)制)
詳情
A. 設(shè)備監(jiān)測(cè)數(shù)據(jù)報(bào)文
- 報(bào)文說(shuō)明
動(dòng)態(tài)傾角儀處于低功耗的時(shí)候、按照平臺(tái)設(shè)定的采集間隔和發(fā)送間隔通訊、監(jiān)測(cè)站每一個(gè)通訊間隔向平臺(tái)發(fā)送一次監(jiān)測(cè)數(shù)據(jù),數(shù)據(jù)采用HTTP以POST方式發(fā)送、數(shù)據(jù)格式為標(biāo)準(zhǔn)的JSON鍵值對(duì)。如果平臺(tái)在2倍間隔時(shí)沒(méi)有收到監(jiān)測(cè)數(shù)據(jù)、可判定為設(shè)備離線。
- 報(bào)文示例
{
"dev_ty":4000,
"pro_ty":"rf_epmt_qjdt_1105",
"use_id":0,
"dev_id":2302180001,
"pcb_ver":221214,
"bot_ver":2023021700,
"app_ver":2023030100,
"net_ty":1,
"link_ty":0,
"pak_ty":"up_data",
"mk_id":"",
"ka_id":"",
"rssi":0,
"snr":0,
"x_jsd":0.0492592602968216,
"y_jsd":0.0070370370522141457,
"z_jsd":4.4122223854064941,
"temp_c_val":14.195767402648926,
"x_jd":0.63720703125,
"y_jd":0.0933837890625,
"z_jd":89.3463134765625,
"cj_s":300,
"up_s":3600,
"xt_s":3600,
"run_s":70,
"in_v":0.00880800001323223,
"bat_v":6.4534401893615723,
"gn_wd":0,
"gn_jd":0,
"gn_hb":521,
"mem_sx":19276,
"tm_sm":"000101001046"
}
- JSON鍵值說(shuō)明
鍵名 | 數(shù)據(jù)類(lèi)型 | 說(shuō)明 | 備注 |
---|---|---|---|
dev_ty | U16 | 設(shè)備類(lèi)型 | 廠家研發(fā)編碼、用戶(hù)無(wú)需在意 |
pro_ty | String | 產(chǎn)品類(lèi)型 | rf_epmt_qjdt_1105=動(dòng)態(tài)傾角儀 |
use_id | U16 | 用戶(hù)ID | 廠家對(duì)用戶(hù)的編碼 |
dev_id | U32 | 設(shè)備唯一ID | 設(shè)備廠家對(duì)設(shè)備的唯一編號(hào) |
pcb_ver | U32 | 電路板版本號(hào) | 設(shè)備電路板版本號(hào) |
bot_ver | U32 | 啟動(dòng)程序版本 | 遠(yuǎn)程啟動(dòng)程序版本號(hào) |
app_ver | U32 | 功能程序版本 | 功能程序版本號(hào) |
net_ty | U8 | 網(wǎng)絡(luò)類(lèi)型 | 1=CAT1、5=NBIOT、6=RJ45、8=北斗短報(bào)文 |
link_ty | U8 | 連接類(lèi)型 | 0=短連接=設(shè)備發(fā)送完數(shù)據(jù)等待5秒主動(dòng)斷開(kāi)連接、1=長(zhǎng)連接=設(shè)備發(fā)送完畢數(shù)據(jù)不斷開(kāi)連接。 |
pak_ty | String | 數(shù)據(jù)包類(lèi)型 | "up_data"=上報(bào)監(jiān)測(cè)數(shù)據(jù) |
mk_id | String | 無(wú)線模塊號(hào) | 模塊號(hào)IMEI=15-20個(gè)字符串組成 |
ka_id | String | 流量卡號(hào) | 物聯(lián)網(wǎng)卡號(hào)IMSI=15-20個(gè)字符串組成 |
rssi | S16 | 信號(hào)強(qiáng)度 | 現(xiàn)場(chǎng)網(wǎng)絡(luò)信號(hào)的強(qiáng)度 |
snr | S16 | 信號(hào)質(zhì)量 | 現(xiàn)場(chǎng)網(wǎng)絡(luò)信號(hào)的質(zhì)量 |
x_jsd | float | X軸加速度 | 重力加速度、單位g |
y_jsd | float | Y軸加速度 | 重力加速度、單位g |
z_jsd | float | Z軸加速度 | 重力加速度、單位g |
temp_c_val | float | 溫度 | 設(shè)備內(nèi)部溫度、單位℃ |
x_jd | float | X軸角度 | 傾角度、單位° |
y_jd | float | Y軸角度 | 傾角度、單位° |
z_jd | float | Z軸角度 | 傾角度、單位° |
cj_s | U32 | 采集數(shù)據(jù)間隔 | 設(shè)備每cj_s秒采集并和閾值對(duì)比一次傳感器數(shù)據(jù) |
up_s | U32 | 上報(bào)數(shù)據(jù)間隔 | 設(shè)備每到up_s秒向平臺(tái)發(fā)送一次數(shù)據(jù)報(bào)文、平臺(tái)收到以后盡快向設(shè)備回饋報(bào)文、超過(guò)2倍up_s時(shí)間沒(méi)有收到平臺(tái)回饋,設(shè)備會(huì)重啟當(dāng)前連接。 |
xt_s | U32 | 設(shè)備心跳數(shù)據(jù) | 預(yù)留鍵值對(duì)、暫時(shí)未使用 |
run_s | U32 | 設(shè)備運(yùn)行時(shí)間 | 設(shè)備從上電到現(xiàn)在的工作時(shí)間、單位秒 |
in_v | float | 太陽(yáng)能或輸入電壓 | 太陽(yáng)能或市電AC-DC適配器輸入的電壓 |
bat_v | float | 蓄電池電壓 | 蓄電池電壓 |
gn_wd | float | 定位緯度 | GNSS當(dāng)前定位-緯度 |
gn_jd | float | 定位經(jīng)度 | GNSS當(dāng)前定位-經(jīng)度 |
gn_hb | float | 定位海拔 | GNSS當(dāng)前定位-海拔高度 |
mem_sx | U32 | 剩余內(nèi)存 | 設(shè)備操作系統(tǒng)的剩余內(nèi)存 |
err_cd | U16 | 錯(cuò)誤代碼 | 當(dāng)前設(shè)備的錯(cuò)誤代碼0=無(wú)錯(cuò)誤、其它的參考后面的錯(cuò)誤代碼定義 |
tm_sm | / | / | 預(yù)留鍵值對(duì)、暫時(shí)未使用 |
B、平臺(tái)回饋報(bào)文
- 報(bào)文說(shuō)明
平臺(tái)收到傾角儀的監(jiān)測(cè)數(shù)據(jù)以后、需盡快通過(guò)SET報(bào)文,向傾角儀發(fā)送參數(shù)設(shè)置信息??紤]信號(hào)不好的網(wǎng)絡(luò)延遲和平臺(tái)服務(wù)響應(yīng)時(shí)間在內(nèi)、如果在10秒內(nèi)沒(méi)有收到平臺(tái)的ACK回饋,傾角儀在預(yù)定的重傳次數(shù)內(nèi)(固定為5次)、向平臺(tái)重傳數(shù)據(jù)保證通訊的可靠性。
- 報(bào)文示例
{
"pak_ty":"set_inf",
"cj_s":null,
"up_s":3600,
"xt_s":3600,
"x_yz":500,
"y_yz":500,
"z_yz":500,
"nian":2022,
"yue":3,
"ri":25,
"shi":12,
"fen":23,"miao":33
}
- JSON鍵值說(shuō)明
鍵名 | 數(shù)據(jù)類(lèi)型 | 說(shuō)明 | 備注 |
---|---|---|---|
pack_ty | String | 數(shù)據(jù)包類(lèi)型 | "set_inf"=設(shè)置報(bào)文 |
cj_s | U32 | 數(shù)據(jù)采集間隔 | 平臺(tái)設(shè)置設(shè)備的數(shù)據(jù)采集間隔 |
up_s | U32 | 上報(bào)數(shù)據(jù)間隔 | 平臺(tái)設(shè)置設(shè)備的數(shù)據(jù)上報(bào)間隔 |
xt_s | U32 | 設(shè)備心跳間隔 | 平臺(tái)設(shè)置設(shè)置的心跳上報(bào)間隔、默認(rèn)不使用 |
x_yz | Float | X軸增量閾值 | 設(shè)置X軸的增量閾值,1.2為±1.2的變動(dòng) |
y_yz | Float | Y軸增量閾值 | 設(shè)置Y軸的增量閾值,1.2為±1.2的變動(dòng) |
z_yz | Float | X軸增量閾值 | 設(shè)置Z軸的增量閾值,1.2為±1.2的變動(dòng) |
nian | U8 | 下發(fā)同步=年 | 平臺(tái)下發(fā)設(shè)備同步時(shí)間=年=2022=22 |
yue | U8 | 下發(fā)同步=月 | |
ri | U8 | 下發(fā)同步=日 | |
shi | U8 | 下發(fā)同步=時(shí) | |
fen | U8 | 下發(fā)同步=分 | |
miao | U8 | 下發(fā)同步=秒 | |
err_cd | U16 | 錯(cuò)誤代碼 | 錯(cuò)誤代碼0=無(wú)錯(cuò)誤、其它的參考后面的錯(cuò)誤代碼定義 |
設(shè)備上報(bào)報(bào)文: (設(shè)備數(shù)據(jù),注冊(cè)包,心跳包組合)
{
"dev_ty":4000,
"pro_ty":"rf_epmt_qjdt_1105",
"use_id":0,
"dev_id":2302180001,
"pcb_ver":221214,
"bot_ver":2023021700,
"app_ver":2023030100,
"net_ty":1,
"link_ty":0,
"pak_ty":"up_data",
"mk_id":"",
"ka_id":"",
"rssi":0,
"snr":0,
"x_jsd":0.0492592602968216,
"y_jsd":0.0070370370522141457,
"z_jsd":4.4122223854064941,
"temp_c_val":14.195767402648926,
"x_jd":0.63720703125,
"y_jd":0.0933837890625,
"z_jd":89.3463134765625,
"cj_s":300,
"up_s":3600,
"xt_s":3600,
"run_s":70,
"in_v":0.00880800001323223,
"bat_v":6.4534401893615723,
"gn_wd":0,
"gn_jd":0,
"gn_hb":521,
"mem_sx":19276,
"tm_sm":"000101001046"
}
服務(wù)端收到設(shè)備上報(bào)消息,應(yīng)答設(shè)備:
{
"pak_ty":"set_inf",
"cj_s":null,
"up_s":3600,
"xt_s":3600,
"x_yz":500,
"y_yz":500,
"z_yz":500,
"nian":2022,
"yue":3,
"ri":25,
"shi":12,
"fen":23,
"miao":33
}
模擬TCP客戶(hù)端示例:

七、TCP客戶(hù)端指令下發(fā)
如下圖所示:

查看模擬模擬客戶(hù)端可以看到,寫(xiě)指令下發(fā)成功,如下圖所示:

八、TCP數(shù)據(jù)包,粘包,分包處理
TCP粘包拆包發(fā)生場(chǎng)景
- 因?yàn)門(mén)CP是面向流,沒(méi)有邊界,而操作系統(tǒng)在發(fā)送TCP數(shù)據(jù)時(shí),會(huì)通過(guò)緩沖區(qū)來(lái)進(jìn)行優(yōu)化,例如緩沖區(qū)為1024個(gè)字節(jié)大小。
- 如果一次請(qǐng)求發(fā)送的數(shù)據(jù)量比較小,沒(méi)達(dá)到緩沖區(qū)大小,TCP則會(huì)將多個(gè)請(qǐng)求合并為同一個(gè)請(qǐng)求進(jìn)行發(fā)送,這就形成了粘包問(wèn)題。
- 如果一次請(qǐng)求發(fā)送的數(shù)據(jù)量比較大,超過(guò)了緩沖區(qū)大小,TCP就會(huì)將其拆分為多次發(fā)送,這就是拆包。
對(duì)于粘包和拆包問(wèn)題,常見(jiàn)的解決方案有四種:
- 發(fā)送端將每個(gè)包都封裝成固定的長(zhǎng)度,比如100字節(jié)大小。如果不足100字節(jié)可通過(guò)補(bǔ)0或空等進(jìn)行填充到指定長(zhǎng)度;
- 發(fā)送端在每個(gè)包的末尾使用固定的分隔符,例如\r\n。如果發(fā)生拆包需等待多個(gè)包發(fā)送過(guò)來(lái)之后再找到其中的\r\n進(jìn)行合并;例如,F(xiàn)TP協(xié)議;
- 將消息分為頭部和消息體,頭部中保存整個(gè)消息的長(zhǎng)度,只有讀取到足夠長(zhǎng)度的消息之后才算是讀到了一個(gè)完整的消息;
- 通過(guò)自定義協(xié)議進(jìn)行粘包和拆包的處理
Netty對(duì)粘包和拆包問(wèn)題的處理
Netty對(duì)解決粘包和拆包的方案做了抽象,提供了一些解碼器(Decoder)來(lái)解決粘包和拆包的問(wèn)題。如:
- LineBasedFrameDecoder:以行為單位進(jìn)行數(shù)據(jù)包的解碼;
- DelimiterBasedFrameDecoder:以特殊的符號(hào)作為分隔來(lái)進(jìn)行數(shù)據(jù)包的解碼;
- FixedLengthFrameDecoder:以固定長(zhǎng)度進(jìn)行數(shù)據(jù)包的解碼;
- LenghtFieldBasedFrameDecode:適用于消息頭包含消息長(zhǎng)度的協(xié)議(最常用);
FastBee的TCP服務(wù)端,解決粘包,分包問(wèn)題
- 固定分隔符
DelimiterBasedFrameDecoder 類(lèi)定義了分割符處理粘包,分包問(wèn)題
TCP服務(wù)器啟動(dòng)處理客戶(hù)端消息配置中,第二個(gè)處理類(lèi)配置了粘包,分包處理在TCP啟動(dòng)時(shí)候,添加分隔符號(hào)
- 固定長(zhǎng)度
LengthFieldAndDelimiterFrameDecoder ,固定長(zhǎng)度配置在TCP啟動(dòng)時(shí)候,配置如下做固定長(zhǎng)度處理粘包,分包問(wèn)題