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

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


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


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


同樣模擬客戶端斷電,需要等待客戶端超過心跳超時(shí)時(shí)間 (70s),更新動作跟設(shè)備斷開一致。
四、TCP客戶端前端連接展示
新建產(chǎn)品,傳輸協(xié)議選擇TCP,通訊協(xié)議可以選擇 JSON、ModbusTcp協(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)識符(Transaction Identifier):用于匹配請求和響應(yīng),通常由客戶端生成。
- 協(xié)議標(biāo)識符(Protocol Identifier):固定為 0,表示 Modbus 協(xié)議。
- 長度(Length):后續(xù)數(shù)據(jù)的字節(jié)長度,包括單元標(biāo)識符和 PDU (Protocol Data Unit)。
- 單元標(biāo)識符(Unit Identifier):用于標(biāo)識從站設(shè)備。
- PDU(Protocol Data Unit):
- 功能碼(Function Code):指示要執(zhí)行的操作,如讀取、寫入等。
- 數(shù)據(jù)(Data):根據(jù)功能碼的不同,包含相應(yīng)的操作數(shù)據(jù)。
使用:產(chǎn)品配置時(shí)選擇Modbus Tcp協(xié)議,具體配置方式參考網(wǎng)關(guān)與子設(shè)備文檔
可以在設(shè)備詳情-數(shù)據(jù)調(diào)試、數(shù)據(jù)采集頁面下發(fā)指令,如果采用云端輪詢方式,可配置輪詢?nèi)蝿?wù)下發(fā)指令:


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

六、TCP+JSON
這里采用動態(tài)傾角監(jiān)測儀(通訊協(xié)議)2023-3-1 103136
設(shè)備通訊協(xié)議進(jìn)行演示。(應(yīng)答機(jī)制)
詳情
A. 設(shè)備監(jiān)測數(shù)據(jù)報(bào)文
- 報(bào)文說明
動態(tài)傾角儀處于低功耗的時(shí)候、按照平臺設(shè)定的采集間隔和發(fā)送間隔通訊、監(jiān)測站每一個(gè)通訊間隔向平臺發(fā)送一次監(jiān)測數(shù)據(jù),數(shù)據(jù)采用HTTP以POST方式發(fā)送、數(shù)據(jù)格式為標(biāo)準(zhǔn)的JSON鍵值對。如果平臺在2倍間隔時(shí)沒有收到監(jiān)測數(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鍵值說明
鍵名 | 數(shù)據(jù)類型 | 說明 | 備注 |
---|---|---|---|
dev_ty | U16 | 設(shè)備類型 | 廠家研發(fā)編碼、用戶無需在意 |
pro_ty | String | 產(chǎn)品類型 | rf_epmt_qjdt_1105=動態(tài)傾角儀 |
use_id | U16 | 用戶ID | 廠家對用戶的編碼 |
dev_id | U32 | 設(shè)備唯一ID | 設(shè)備廠家對設(shè)備的唯一編號 |
pcb_ver | U32 | 電路板版本號 | 設(shè)備電路板版本號 |
bot_ver | U32 | 啟動程序版本 | 遠(yuǎn)程啟動程序版本號 |
app_ver | U32 | 功能程序版本 | 功能程序版本號 |
net_ty | U8 | 網(wǎng)絡(luò)類型 | 1=CAT1、5=NBIOT、6=RJ45、8=北斗短報(bào)文 |
link_ty | U8 | 連接類型 | 0=短連接=設(shè)備發(fā)送完數(shù)據(jù)等待5秒主動斷開連接、1=長連接=設(shè)備發(fā)送完畢數(shù)據(jù)不斷開連接。 |
pak_ty | String | 數(shù)據(jù)包類型 | "up_data"=上報(bào)監(jiān)測數(shù)據(jù) |
mk_id | String | 無線模塊號 | 模塊號IMEI=15-20個(gè)字符串組成 |
ka_id | String | 流量卡號 | 物聯(lián)網(wǎng)卡號IMSI=15-20個(gè)字符串組成 |
rssi | S16 | 信號強(qiáng)度 | 現(xiàn)場網(wǎng)絡(luò)信號的強(qiáng)度 |
snr | S16 | 信號質(zhì)量 | 現(xiàn)場網(wǎng)絡(luò)信號的質(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秒采集并和閾值對比一次傳感器數(shù)據(jù) |
up_s | U32 | 上報(bào)數(shù)據(jù)間隔 | 設(shè)備每到up_s秒向平臺發(fā)送一次數(shù)據(jù)報(bào)文、平臺收到以后盡快向設(shè)備回饋報(bào)文、超過2倍up_s時(shí)間沒有收到平臺回饋,設(shè)備會重啟當(dāng)前連接。 |
xt_s | U32 | 設(shè)備心跳數(shù)據(jù) | 預(yù)留鍵值對、暫時(shí)未使用 |
run_s | U32 | 設(shè)備運(yùn)行時(shí)間 | 設(shè)備從上電到現(xiàn)在的工作時(shí)間、單位秒 |
in_v | float | 太陽能或輸入電壓 | 太陽能或市電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=無錯(cuò)誤、其它的參考后面的錯(cuò)誤代碼定義 |
tm_sm | / | / | 預(yù)留鍵值對、暫時(shí)未使用 |
B、平臺回饋報(bào)文
- 報(bào)文說明
平臺收到傾角儀的監(jiān)測數(shù)據(jù)以后、需盡快通過SET報(bào)文,向傾角儀發(fā)送參數(shù)設(shè)置信息??紤]信號不好的網(wǎng)絡(luò)延遲和平臺服務(wù)響應(yīng)時(shí)間在內(nèi)、如果在10秒內(nèi)沒有收到平臺的ACK回饋,傾角儀在預(yù)定的重傳次數(shù)內(nèi)(固定為5次)、向平臺重傳數(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鍵值說明
鍵名 | 數(shù)據(jù)類型 | 說明 | 備注 |
---|---|---|---|
pack_ty | String | 數(shù)據(jù)包類型 | "set_inf"=設(shè)置報(bào)文 |
cj_s | U32 | 數(shù)據(jù)采集間隔 | 平臺設(shè)置設(shè)備的數(shù)據(jù)采集間隔 |
up_s | U32 | 上報(bào)數(shù)據(jù)間隔 | 平臺設(shè)置設(shè)備的數(shù)據(jù)上報(bào)間隔 |
xt_s | U32 | 設(shè)備心跳間隔 | 平臺設(shè)置設(shè)置的心跳上報(bào)間隔、默認(rèn)不使用 |
x_yz | Float | X軸增量閾值 | 設(shè)置X軸的增量閾值,1.2為±1.2的變動 |
y_yz | Float | Y軸增量閾值 | 設(shè)置Y軸的增量閾值,1.2為±1.2的變動 |
z_yz | Float | X軸增量閾值 | 設(shè)置Z軸的增量閾值,1.2為±1.2的變動 |
nian | U8 | 下發(fā)同步=年 | 平臺下發(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=無錯(cuò)誤、其它的參考后面的錯(cuò)誤代碼定義 |
設(shè)備上報(bào)報(bào)文: (設(shè)備數(shù)據(jù),注冊包,心跳包組合)
{
"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客戶端示例:

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

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

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