内容简介:Google的Protobuf是一个高效的二进制编解码机制,应用很广,很多RPC库都使用了它,包括gRPC。为了让大家更好的理解它,今天我来用一个简单的例子和大家介绍下它的使用方式:示例程序由三部分组成:
Google的Protobuf是一个高效的二进制编解码机制,应用很广,很多RPC库都使用了它,包括gRPC。
为了让大家更好的理解它,今天我来用一个简单的例子和大家介绍下它的使用方式:
示例程序介绍
示例程序由三部分组成:
- person.proto:定义通信用的协议数据
- client.cc:客户端实现,生成通信二进制数据发送给server
- server.cc:服务端时间,解析客户端发送来的二进制数据,简单输出
下面分别说明:
person.proto
syntax = "proto2"; package tutorial; // 会生成namespace tutorial message Person { // 会生成class Person required string name = 1; required int32 id = 2; optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phones = 4; }
调用protoc工具,生成类代码:
ligang@vm-xubuntu ~/devspace/hogwarts/cppsimple/src/protobuf $ protoc -I ./ --cpp_out=./ person.proto ligang@vm-xubuntu ~/devspace/hogwarts/cppsimple/src/protobuf $ ll total 80 -rw-rw-r-- 1 ligang ligang 1992 11月 10 13:55 client.cc -rw-rw-r-- 1 ligang ligang 590 11月 10 13:55 CMakeLists.txt -rw-rw-r-- 1 ligang ligang 33288 11月 10 16:55 person.pb.cc // 生成的person类的实现代码 -rw-rw-r-- 1 ligang ligang 25548 11月 10 16:55 person.pb.h // 生成的person类的header文件 -rw-rw-r-- 1 ligang ligang 354 11月 10 13:55 person.proto // 定义协议 -rw-rw-r-- 1 ligang ligang 2749 11月 10 13:55 server.cc
client.cc
#include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <iostream> #include <cstring> #include "person.pb.h" const int kRwBufLen = 1024; int ConnectToServer(uint16_t port) { int connfd = socket(AF_INET, SOCK_STREAM, 0); if (connfd == -1) { perror("connfd fail"); return -1; } struct sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(struct sockaddr_in)); serverAddr.sin_family = AF_INET; if (inet_pton(AF_INET, "127.0.0.1", &(serverAddr.sin_addr)) != 1) { std::cout << "inet_pton fail" << std::endl; return -1; } serverAddr.sin_port = htons(port); if (connect(connfd, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) == -1) { perror("connect failed"); return -1; } return connfd; } std::string ProtobufPersonSerialize() { tutorial::Person person; person.set_name("person1"); person.set_id(1); person.set_email("p1@protobuf.com"); tutorial::Person_PhoneNumber *phone = person.add_phones(); phone->set_number("123"); phone->set_type(tutorial::Person_PhoneType::Person_PhoneType_MOBILE); phone = person.add_phones(); phone->set_number("234"); phone->set_type(tutorial::Person_PhoneType::Person_PhoneType_HOME); phone = person.add_phones(); phone->set_number("345"); phone->set_type(tutorial::Person_PhoneType::Person_PhoneType_WORK); std::string person_serialize = person.SerializeAsString(); std::cout << person_serialize.size() << std::endl; return person_serialize; } int main() { GOOGLE_PROTOBUF_VERIFY_VERSION; int connfd = ConnectToServer(9008); // 获得通信用socket char buf[kRwBufLen]; memset(buf, 0, kRwBufLen); std::string person_serialize = ProtobufPersonSerialize(); // 生成通信用的person的二进制数据 uint32_t dl = htonl(person_serialize.size()); size_t dlsize = sizeof dl; memcpy(buf, &dl, dlsize); // 二进制数据的长度 memcpy(buf + dlsize, (char *) person_serialize.c_str(), person_serialize.size()); // 编码protobuf二进制数据 write(connfd, buf, dlsize + person_serialize.size()); // 组成数据后一起发送 return 0; }
客户端运行后,输出编码发送的二进制数据长度为: 55
server.cc
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <iostream> #include <cstring> #include "person.pb.h" const int kRwBufLen = 1024; int TcpBindAndListen(uint16_t port) { int listenfd = socket(AF_INET, SOCK_STREAM, 0); if (listenfd == -1) { perror("listenfd fail"); return -1; } struct sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(struct sockaddr_in)); serverAddr.sin_family = AF_INET; if (inet_pton(AF_INET, "127.0.0.1", &(serverAddr.sin_addr)) != 1) { std::cout << "inet_pton fail" << std::endl; return -1; } serverAddr.sin_port = htons(port); if (bind(listenfd, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) == -1) { perror("bind fail"); return -1; } if (listen(listenfd, 1024) == -1) { perror("listen failed"); return -1; } return listenfd; } int ReadConn(int connfd, size_t len, char *buf) { size_t readed = 0; size_t remain = len - readed; while (remain > 0) { auto n = read(connfd, buf + readed, remain); if (n < 0) { if (errno == EINTR) { continue; } perror("read error"); return -1; } readed += n; remain = len - readed; } return 0; } void ReadProtobufPerson(int connfd, size_t dl) { char buf[kRwBufLen]; int r = ReadConn(connfd, dl, buf); if (r == -1) { perror("read conn error"); return; } tutorial::Person person; std::cout << "dl = " << dl << std::endl; person.ParseFromArray(buf, dl); // 解码protobuf二进制数据 std::cout << person.id() << std::endl << person.name() << std::endl << person.email() << std::endl; for (auto phone : person.phones()) { std::cout << phone.number() << std::endl; std::cout << phone.type() << std::endl; } } void ProtobufServe(int connfd) { char buf[kRwBufLen]; size_t dl_len = 4; int r = ReadConn(connfd, dl_len, buf); // 先解析二进制数据的长度 if (r == -1) { perror("read conn error"); return; } uint32_t dl; memcpy(&dl, buf, dl_len); std::cout << "before ntoh: " << dl << std::endl; dl = ntohl(dl); std::cout << "after ntoh: " << dl << std::endl; ReadProtobufPerson(connfd, dl); // 解析二进制数据 } int main() { GOOGLE_PROTOBUF_VERIFY_VERSION; int listenfd = TcpBindAndListen(9008); // listen用socket if (listenfd < 0) { return 1; } bool running = true; while (running) { struct sockaddr_in clientAddr; memset(&clientAddr, 0, sizeof(struct sockaddr_in)); socklen_t clientLen; int connfd = accept(listenfd, (struct sockaddr *) &clientAddr, &clientLen); // 客户端连接后获得通信用socket if (connfd == -1) { running = false; perror("accept failed"); } else { ProtobufServe(connfd); // 读取客户端发送来的二进制数据后解析输出 } close(connfd); } return 0; }
运行后,服务端输出客户端发送来的数据为:
dl = 55 // 数据长度 1 // id person1 // name p1@protobuf.com // email 123 // 这里开始是地址薄 0 234 1 345 2
结束语
这里只是为了示例protobuf的基本使用方法,更多的protobuf语法,请查看官方手册,谢谢大家!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- RecyclerView使用指南(一)—— 基本使用
- 如何使用Meteorjs使用URL参数
- 使用 defer 还是不使用 defer?
- 使用 Typescript 加强 Vuex 使用体验
- [译] 何时使用 Rust?何时使用 Go?
- UDP协议的正确使用场合(谨慎使用)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Java Web开发实战经典(基础篇)
李兴华、王月清 / 清华大学出版社 / 2010-8 / 69.80元
本书用通俗易懂的语言和丰富多彩的实例,通过对Ajax、JavaScript、HTML等Web系统开发技术基础知识的讲解,并结合MVC设计模式的理念,详细讲述了使用JSP及Struts框架进行Web系统开发的相关技术。 全书分4部分共17章,内容包括Java Web开发简介,HTML、JavaScript简介,XML简介,Tomcat服务器的安装及配置,JSP基础语法,JSP内置对象,Java......一起来看看 《Java Web开发实战经典(基础篇)》 这本书的介绍吧!