Netty网络框架对protobuf进行封装,在网络传输过程为了解决数据粘包问题,提供了以下编码器和解码器。
ProtobufVarint32FrameDecoder 数据解码器
ProtobufVarint32LengthFieldPrepender 数据编码器
ProtobufVarint32LengthFieldPrepender 将数据编码成以下格式
原始数据 编码数据
* BEFORE ENCODE (300 bytes) AFTER ENCODE (302 bytes)
* +---------------+ +--------+---------------+
* | Protobuf Data |-------------->| Length | Protobuf Data |
* | (300 bytes) | | 0xAC02 | (300 bytes) |
* +---------------+ +--------+---------------+
ProtobufVarint32LengthFieldPrepender将数据解码成以下格式
原始数据 解码数据
* BEFORE DECODE (302 bytes) AFTER DECODE (300 bytes)
* +--------+---------------+ +---------------+
* | Length | Protobuf Data |----->| Protobuf Data |
* | 0xAC02 | (300 bytes) | | (300 bytes) |
* +--------+---------------+ +---------------+
以上数据格式中使用length来记录数据的长度,这里length用的不是传统int类型存储长度,而是使用的Varint32类型存储数据长度。
Varint32类型是可变宽度整数,它允许使用 1 到 10 个字节对无符号 64 位整数进行编码,可实现较小的数值使用更少的字节。
那C++如何使用Varint32类型的数据,可参考以下代码
#include <google/protobuf/io/coded_stream.h>
using namespace google::protobuf::io;
//将Varint32转int类型
inline const uint8_t* ReadVarint32FromArray(const uint8_t* buffer, uint32_t * value) {
static const int kMaxVarintBytes = 10;
static const int kMaxVarint32Bytes = 5;
// 快速路径:缓冲区中还有足够的字节可保证
//这个读数不会超过末尾,所以我们可以跳过检查。
const uint8_t* ptr = buffer;
uint32_t b;
uint32_t result;
b = *(ptr++); result = (b & 0x7F) ; if (!(b & 0x80)) goto done;
b = *(ptr++); result |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done;
b = *(ptr++); result |= (b & 0x7F) << 14; if (!(b & 0x80)) goto done;
b = *(ptr++); result |= (b & 0x7F) << 21; if (!(b & 0x80)) goto done;
b = *(ptr++); result |= b << 28; if (!(b & 0x80)) goto done;
// 如果输入大于32位,我们仍然需要全部读取
// 并丢弃高位。
for (int i = 0; i < kMaxVarintBytes - kMaxVarint32Bytes; i++) {
b = *(ptr++); if (!(b & 0x80)) goto done;
}
// 我们超出了变量的最大大小(10字节)。假定数据已损坏
return NULL;
done:
*value = result;
return ptr;
}
//获取int类型转varint占多少字节
static int computeRawVarint32Size(int value) {
if ((value & (0xffffffff << 7)) == 0) {
return 1;
}
if ((value & (0xffffffff << 14)) == 0) {
return 2;
}
if ((value & (0xffffffff << 21)) == 0) {
return 3;
}
if ((value & (0xffffffff << 28)) == 0) {
return 4;
}
return 5;
}
int main() {
//定义数据长度
uint32_t testNum=3593904516;
//Varint32占用字节长度
int placeSize=computeRawVarint32Size(testNum);
//存储Varint32的字节数组变量
uint8_t *buffer=new uint8_t[placeSize];
//将int变量转Varint32变量
CodedOutputStream::WriteVarint32ToArray(testNum,buffer);
//将Varint32转int变量
uint32_t result;
ReadVarint32FromArray(buffer,&result);
std::cout << result <<std::endl;
return 0;
}
c++使用socket发送数据到netty
WSADATA wsa;
WSAStartup(MAKEWORD(2,2),&wsa);
//初始化socket
int fd=socket(AF_INET,SOCK_STREAM,0);
sockaddr_in sockaddrIn;
sockaddrIn.sin_port=htons(9988);
sockaddrIn.sin_family=AF_INET;
evutil_inet_pton(sockaddrIn.sin_family, "127.0.0.1", (void *)&sockaddrIn.sin_addr);
int sockleng=sizeof(sockaddrIn);
//连接netty服务器
connect(fd,(sockaddr*)&sockaddrIn,sockleng);
//初始化protobuf对象
Person *p1=new Person;
p1->set_age(121212);
p1->set_sex(MAN);
p1->set_image("asdasd");
p1->set_brithday("2020-01-1");
p1->set_name("zhangsan");
p1->set_status(true);
p1->set_picture("1.png");
Address *ad=p1->add_myaddress();
ad->set_citycode("430321");
ad->set_desc("xiangtanshi");
//读取图片
ifstream fin("D:\\1.png",std::ios::binary);
int imageSize=fin.seekg(0,std::ios::end).tellg();
std::vector<char> imageBuffer(imageSize);
fin.seekg(0,std::ios::beg).read(&imageBuffer[0],static_cast<std::streamsize>(imageBuffer.size()));
fin.close();
string image_data;
image_data.assign(imageBuffer.data(),imageSize);
//绑定图片
p1->set_image(image_data);
//获取person对象二进制长度
int size=p1->ByteSizeLong();
//计算int类型转Varint32占多少字节
int len=computeRawVarint32Size(size);
char *data=new char[size+len];
//int类型转Varint32
uint8_t *test=CodedOutputStream::WriteVarint32ToArray(size,(uint8_t*)data);
std::cout << *test << std::endl;
//将对象序列化字节
p1->SerializeToArray(data+len,size);
std::cout << "size=" << size << std::endl;
//发送数据到服务端
send(fd,data,size+ len,0);
//关闭连接
closesocket(fd);
网友评论