前言
TCP是一種流式協定,存在粘包的現象,是以需要分包處理。
資料包格式
不同格式的資料包的分包方法也不相同,本文使用的格式為:
#長度,標頭:資料内容@
其中,長度是從標頭到資料内容總位元組數,4位定長。
代碼
void RecvThread::run()
{
char buffer[2048] = {0}; // 用于存放讀取到的資料
char *buf = buffer; //
int len = 0; // TCP通信讀取到的資料長度
int bufferLen = 0; // 目前資料長度
int needLen = 0; // 還需要讀取的資料長度
while(m_tcpConnectionClient->isOpen())
{
memset(buffer, 0, 2048);
// 查找開始标志
while(buf[0] != '#')
{
needLen = 1 - bufferLen < 0 ? 0 : 1 - bufferLen;
if (needLen == 0)
{
bufferLen -= 1;
memmove(buf, buf + 1, bufferLen);
memset(buf + bufferLen, 0, 2048 - bufferLen);
}
else
{
len = m_tcpConnectionClient->read(buf, needLen);
if (len < 0)
{
break;
}
}
}
if (len < 0)
{
break;
}
// 讀取長度
bufferLen += needLen;
needLen = 6 - bufferLen < 0 ? 0 : 6 - bufferLen;
len = m_tcpConnectionClient->read(buf + 1, needLen);
if (len < 0)
{
break;
}
bufferLen += needLen;
if (buf[5] == ',')
{
char cLen[5] = {0};
cLen[0] = buf[1];
cLen[1] = buf[2];
cLen[2] = buf[3];
cLen[3] = buf[4];
int dataLen = atoi(cLen);
if (dataLen > 0 && dataLen < 2000)// 至此判斷為完整的頭
{
needLen = dataLen + 7 - bufferLen < 0 ? 0 : dataLen + 7 - bufferLen;
len = m_tcpConnectionClient->read(buf + 6, needLen);
if (len < 0)
{
break;
}
bufferLen += needLen;
if (buf[dataLen + 6] == '@')
{
string str = "";
str.assign(buffer, dataLen + 7);
gotMsg(str);
bufferLen -= dataLen + 7;
memmove(buf, buf + dataLen + 7, bufferLen);
memset(buf + bufferLen, 0, 2048 - bufferLen);
continue;
}
}
}
bufferLen -= 1;
memmove(buf, buf + 1, bufferLen);
memset(buf + bufferLen, 0, 2048 - bufferLen);
}
}
說明
1. TCPConnectionClient *m_tcpConnectionClient是自定義的TCP通信的類,其read函數使用while循環讀取資料,直到讀取到指定的資料長度;
2. memmove用于記憶體發生局部重疊的時候,保證拷貝結果是正确的;
3. gotMsg(str)用于處理分出來的單個資料包;
4. 代碼邏輯并不十厘清晰和嚴密,歡迎指正。