CPackage

CPackage

Cpackage是Carlsdk中使用Csocket进行收发数据包的类,对数据包进行了定义,保证数据收发的完整性

此对象主要应对大数据包的收发,以及可能出现的TCP粘包问题。

数据包

数据包由包头和包体两部分组成,包头4个字节,包体最大8 * 1024个字节,使用如下两个宏

1
2
#define C_DATA_PACKAGE_HEADER_SIZE                4
#define C_DATA_PACKAGE_MAX_LENGTH (8 * 1024)
C

构造函数

构造函数主要申请数据包的缓存空间,一些标志位的初始化赋值

标志位是用来判断缓存空间的使用情况,包括如下几个:

  • data_package_buffer:缓存空间头指针
  • unconsumed_buffer:还未使用的缓存空间头指针
  • read_end:已经读取的缓存空间尾指针
  • buffer_end:缓存空间的尾指针
1
2
3
4
5
6
7
8
9
10
11
12
13
CPackage::CPackage(std::shared_ptr<CSocket> socket)
{
mSock = socket;
/*Data package buffer is 8 * 1024 + 4*/
dataPackageBuffer = new char[C_DATA_PACKAGE_HEADER_SIZE + C_DATA_PACKAGE_MAX_LENGTH];
dataSendBuffer = new char[C_DATA_PACKAGE_HEADER_SIZE + C_DATA_PACKAGE_MAX_LENGTH];
/*Unconsumed buffer is all data package buffer*/
unconsumedBuffer = dataPackageBuffer;
/*End of buffer already used */
readEnd = dataPackageBuffer;
/*End of buffer*/
bufferEnd = dataPackageBuffer + C_DATA_PACKAGE_HEADER_SIZE + C_DATA_PACKAGE_MAX_LENGTH;
}
C

发送数据包

发送数据包由两部分组成,构造数据和发送数据,发送数据要保证数据全部发送完成,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int CPackage::sendBlockData(const char *data, int data_length)
{
int send_length = 0;
int data_remaining = 0;

if (data == nullptr || data_length <= 0)
{
return -1;
}

if (mSock == nullptr)
{
return -1;
}
/*init remaining data*/
data_remaining = data_length;

/*Ensure all data is sent*/
do
{
send_length = mSock->send(data, data_remaining);
if (send_length < 0)
{
return -1;
}
data_remaining -= send_length;
data += send_length;
} while (data_remaining > 0);

return 0;
}
C

构造数据包,包括包头和包体,包体是传进来的参数,所以只需在发送数据前先发送包头,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int CPackage::sendDataPackage(const char *data_package, int package_length)
{
if (data_package == nullptr || package_length <= 0)
{
return -1;
}

/*package header is 4 byte*/
memset(dataSendBuffer, 0X00, C_DATA_PACKAGE_HEADER_SIZE + C_DATA_PACKAGE_MAX_LENGTH);

dataSendBuffer[0] = (char)((package_length >> 24) & 0xFF);
dataSendBuffer[1] = (char)((package_length >> 16) & 0xFF);
dataSendBuffer[2] = (char)((package_length >> 8) & 0xFF);
dataSendBuffer[3] = (char)(package_length & 0xFF);

memcpy(dataSendBuffer + C_DATA_PACKAGE_HEADER_SIZE,data_package, package_length);
/*send package body*/
if (sendBlockData(dataSendBuffer, package_length + C_DATA_PACKAGE_HEADER_SIZE) != 0)
{
return -1;
}

return 0;
}
C

接收数据包

首先判断缓存空间无数据可读,并初始化缓存空间,接收数据后,再从缓存空间中取出可用数据,这一系列操作主要是解决TCP粘包问题,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
int CPackage::recvDataPackage(char **data_package, int *package_length)
{
int recv_length = 0;

if (data_package == nullptr || package_length == nullptr)
{
return -1;
}

if (mSock == nullptr)
{
return -1;
}

/*At first, there is no data readable in the buffer*/
if (getAvailableDataPackage(data_package, package_length) != -1)
{
return 0;
}

/*Not enough buffer, New operation failed*/
if (unconsumedBuffer == dataPackageBuffer && readEnd == bufferEnd)
{
return -1;
}

/*buffer is not empty*/
if (unconsumedBuffer != dataPackageBuffer)
{
int move_size = readEnd - unconsumedBuffer;
/*TCP packet sticking*/
if (move_size > 0)
{
memmove(dataPackageBuffer, unconsumedBuffer, move_size);
}
else
{
memset(dataPackageBuffer, 0X00, C_DATA_PACKAGE_HEADER_SIZE + C_DATA_PACKAGE_MAX_LENGTH);
}

readEnd = dataPackageBuffer + move_size;
unconsumedBuffer = dataPackageBuffer;
}

recv_length = mSock->recv(readEnd, bufferEnd - readEnd);

if (recv_length < 0)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
*data_package = nullptr;
*package_length = 0;
return 0;
}
else
{
*package_length = 0;
return -1;
}
}
else if (recv_length == 0)
{
*package_length = 0;
return -1;
}

readEnd += recv_length;

if (getAvailableDataPackage(data_package, package_length) != 0)
{
*data_package = nullptr;
*package_length = 0;
}

return 0;
}
C

获取可用数据首先判断缓存空间是否有可读数据,然后根据包头获取指定长度的数据包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int CPackage::getAvailableDataPackage(char **data_package, int *package_length)
{
int data_length = 0;

/*No readable data*/
if (readEnd < unconsumedBuffer + C_DATA_PACKAGE_HEADER_SIZE)
{
return -1;
}

/*Packet header saves data length information*/
data_length = ((unconsumedBuffer[0] & 0xff) << 24)
| ((unconsumedBuffer[1] & 0xff) << 16)
| ((unconsumedBuffer[2] & 0xff) << 8)
| (unconsumedBuffer[3] & 0xff);

/*The data packet is too large and the buffer is not enough*/
if (readEnd < unconsumedBuffer + C_DATA_PACKAGE_HEADER_SIZE + data_length)
{
return -1;
}

*data_package = unconsumedBuffer + C_DATA_PACKAGE_HEADER_SIZE;
*package_length = data_length;
unconsumedBuffer = unconsumedBuffer + C_DATA_PACKAGE_HEADER_SIZE + data_length;

return 0;
}
C

CPackage
https://carl-5535.github.io/2022/10/25/CarlSDK/CPackage/
作者
Carl Chen
发布于
2022年10月25日
许可协议