最近在写自己的开发库写到Socket时遇到一个很头疼的问题,那就是在发送时发送内容可能会比缓冲区大,而在接收时又不好判断什么时候接收完数据。所以写了一种发送时分割发送和分包接收后拼接的解决方案。而接收时判断数据是否传输结束,我用了select。这里以阻塞式为例子。
首先,我们需要定义一个常量,那就是我们分包时每个包的大小。如下:
#define EACH_PACK_SIZE 1024 // 单个数据包大小
在发送时,我采用了分割字符串进行分包发送的方法发送数据。
bool SendPacket(SOCKET SendSock, const char * pContent)
{
unsigned int unPackSum = strlen(pContent) / EACH_PACK_SIZE; // 计算分包数量
if (strlen(pContent) % EACH_PACK_SIZE != 0)
unPackSum += 1;
char **szSendPack = (char **)calloc(unPackSum, sizeof(char)); // 分配内存用以存放分包内容
for (size_t i = 0; i < unPackSum; i++)
{
szSendPack[i] = (char *)calloc(EACH_PACK_SIZE + 1, sizeof(char)); // 分配内存给每个分包
strncpy_s(szSendPack[i], EACH_PACK_SIZE + 1, pContent + i * EACH_PACK_SIZE, EACH_PACK_SIZE); // 读入分包至数组
}
for (size_t i = 0; i < unPackSum; i++)
{
ErrorCode = send(SendSock, szSendPack[i], strlen(szSendPack[i]), 0); // 发送数据包
if (ErrorCode == SOCKET_ERROR) // 发送失败
{
closesocket(SendSock);
return false;
}
}
for (size_t i = 0; i < unPackSum; i++) // 释放内存
free(szSendPack[i]);
return true;
}
而对于接收数据,由于接收数据长度不定,因此用char二维数组是不方便的,因此我选择了vector来做容器。因此,在使用前应当引用:
#include<vector>
using namespace std;
引用后,我们就可以这样处理接收数据了。
char * RecvPacket(SOCKET RecvSock)
{
vector<char *> vecRecvPack; // 存放接收分包
bool bEmptyPack = false;
fd_set crfd;
timeval td = { 5, 0 };
while (true)
{
FD_ZERO(&crfd);
FD_SET(RecvSock, &crfd);
select(0, &crfd, NULL, NULL, &td);
if (FD_ISSET(RecvSock, &crfd))
{
bEmptyPack = false;
char *szRecvTmp = (char *)calloc(EACH_PACK_SIZE + 1, sizeof(char)); // 分配内存用以缓冲分包
ErrorCode = recv(RecvSock, szRecvTmp, EACH_PACK_SIZE, 0);
vecRecvPack.push_back(szRecvTmp); // 放入分包至数组
FD_ZERO(&crfd);
td = { 0, 0 };
FD_SET(RecvSock, &crfd);
select(0, &crfd, NULL, NULL, &td);
if (FD_ISSET(RecvSock, &crfd))
continue;
else
break;
}
else
{
bEmptyPack = true;
}
}
if (bEmptyPack){
//空数据包丢弃连接
return char();
}
char *szRecvPack = (char *)calloc(EACH_PACK_SIZE * vecRecvPack.size() + 1, sizeof(char)); // 合成分包
for (size_t i = 0; i < vecRecvPack.size(); i++)
{
strncpy_s(szRecvPack + (i * EACH_PACK_SIZE), EACH_PACK_SIZE + 1, vecRecvPack[i], EACH_PACK_SIZE); // 读入分包内容至字符串
}
return szRecvPack;
}
至此,分包发送和接收就完成了。