关于TCP传输过程中的"粘包"问题,目前有几种常见的解决方案:
1.传输定长数据内容,接收端每次解析固定长度的数据
2.使用特殊字符作为数据包的边界,接收端解析到特殊字符则认为是一个数据包
3.使用 固定字节头部 + 不定长消息体;例如固定4字节的头部,存储的是消息体的长度,接收端解析时,先解析4个字节的头部信息,获取到消息体的长度len
,再继续读取len
个字节的数据,即为一个"包"
本文使用第三种方案,演示在网络编程中对发送数据进行封包 以及 接收数据时的解包 操作
QTcpSpcket 收发数据
1.发送
QTcpSocket *client = new QTcpSocket();
connect(ui->btnSend,&QPushButton::clicked,[this]
{
//判断是可操作,isValid表示准备好读写
if(!client->isValid())
return;
// 模拟不同长度的数据包
std::vector<std::string> vec{"1111",
"22222222",
"3333333333333333",
"4444444444444444444444",
"5555555555555555555555555555",
"6666666666666666666666",
"77777777777777777777",
"8888888888888",
"99999999999999999",
"00000000000000000000000"};
for (auto & item : vec)
{
sockets::TcpPacket packet(item); // 使用TcpPacket结构包装, 添加头部长度信息
client->write(packet.getData(), packet.getLenght());
}
});
2.接收数据
sockets::Buffer *m_buffer = new sockets::Buffer();
//收到数据,触发readyRead
connect(client,&QTcpSocket::readyRead,[this]
{
if (client->bytesAvailable() > 0)
{
QByteArray recv = client->readAll();
m_buffer->append(recv.data(), recv.length());
while (m_buffer->readableBytes() >= sockets::TcpPacket::HEADER_LENGTH)
{
const void* data = m_buffer->peek();
int32_t be32 = *static_cast<const int32_t*>(data); // SIGBUS
const int32_t len = sockets::util::networkToHost32(be32);
if (len > 65536 || len < 0)
{
break; // 长度错误
}
else if (m_buffer->readableBytes() >= len + sockets::TcpPacket::HEADER_LENGTH)
{
m_buffer->retrieve(sockets::TcpPacket::HEADER_LENGTH);
std::string msg = m_buffer->retrieveAsString(len);
qDebug() << "receive data:" << msg.c_str();
}
else
{
break; // 不够一个完整包,等待下次接收
}
}
}
});
muduo 收发数据
void onMessage(const TcpConnectionPtr &conn, Buffer *buf, Timestamp time)
{
while (buf->readableBytes() >= sockets::TcpPacket::HEADER_LENGTH) // kHeaderLen == 4
{
// FIXME: use Buffer::peekInt32()
const void* data = buf->peek();
int32_t be32 = *static_cast<const int32_t*>(data); // SIGBUS
const int32_t len = networkToHost32(be32);
if (len > 65536 || len < 0)
{
conn->shutdown(); // FIXME: disable reading
break;
}
else if (buf->readableBytes() >= len + sockets::TcpPacket::HEADER_LENGTH)
{
// 解包
buf->retrieve(sockets::TcpPacket::HEADER_LENGTH);
std::string msg = buf->retrieveAsString(len);
std::cout << "receive data:" << msg << std::endl;
// 使用Tcppacket 封包 + 发送
std::string sendMsg = sockets::TcpPacket(msg).getDataAsString();
conn->send(sendMsg);
}
else
{
break; // 不够一个完整包,等待下次接收
}
}
}