天天看點

c++ TCP協定分包前言資料包格式代碼說明

前言

    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. 代碼邏輯并不十厘清晰和嚴密,歡迎指正。