C++解Netty网络数据包

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 
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 <

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 imageBuffer(imageSize);
fin.seekg(0,std::ios::beg).read(&imageBuffer[0],static_cast(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);

你可能感兴趣的:(C++解Netty网络数据包)