最近在做Modbus TCP時,碰到了TCP粘包問題,由於客戶端發送包的字節數較少並且速度也很快(10ms/次),導致了服務器端一下收到了好幾個包!
一般粘包情況存在以下幾種:
很多人在處理TCP粘包時,都會定義一個幀的數據結構,包含標識,長度,數據等信息。
本人認為Modbus TCP的幀結構就很好,能廣泛應用於電力,機房電源監控等領域也不是沒有道理的。
以下就Modbus TCP粘包問題作出處理,直接上代碼:
1 //ADU和RTU合二為一結構體 2 struct adu_rtu 3 { 4 uint16 Tid; //事務標識符. 默認為0x0000.本例用作RS485串口號:0x0001->UART0、0x0002->UART1、0x0003->UART2、0x0004->UART3 5 uint16 Pid; //協議標識符. ModBus=0x0000. 6 uint16 Len; //后續字節數. 高字節在前,低字節在后;包括單元標識符(從機地址)、功能碼、數據. 7 uint8 data[256]; //協議數據幀. 實際為RTU幀。最大256個字節,包括從機地址、功能碼、數據,含CRC校驗碼. 8 }; 9 10 11 /* 12 ****************************************************************************************************** 13 **函數名稱: void tcp_adu_process(struct pbuf *p) ** 14 **函數描述: 處理TCP發來的ADU包,含TCP粘包處理(只針對上述第1種情況) ** 15 **參 數: *p-------傳入的數據指針 ** 16 **返 回: 無 ** 17 ****************************************************************************************************** 18 */ 19 void tcp_adu_process(struct pbuf *p) 20 { 21 uint8 buffer[300]; 22 uint16 CRC_Temp, Tid, tot_len; 23 struct adu_rtu *aru; 24 25 aru = p->payload; 26 tot_len = p->len; 27 28 //ADU數據解析.並發送到指定串口. 29 while( ((HTONS(aru->Len)+6) <= tot_len) && (tot_len > 0) ) //adu_rtu幀頭為6個字節. 30 { 31 Tid = HTONS(aru->Tid); 32 if( (Tid>0)&&(Tid<5) ) 33 { 34 memcpy(buffer, aru->data, HTONS(aru->Len)); //復制數據. 35 36 CRC_Temp = CRC16_bit(buffer, HTONS(aru->Len)); 37 buffer[HTONS(aru->Len)] = (uint8)CRC_Temp; //填充CRC校驗,低字節在前,高字節在后. 38 buffer[HTONS(aru->Len)+1] = (uint8)(CRC_Temp>>8); 39 40 //發送數據到指定串口. 41 UART_IRQ_SendBytes(HTONS(aru->Tid)-1, buffer, HTONS(aru->Len)+2, ENABLE); 42 } 43 44 //求出TCP剩余的總長度、下一個數據包的首地址. 45 tot_len -= HTONS(aru->Len)+6; 46 aru = (struct adu_rtu *)((uint8 *)aru + HTONS(aru->Len) + 6); //轉換成單字節指針后再加偏移地址. 47 } 48 }