简介
在网络编程中, 数据高效传输离不开数据的序列化. 直接编码不易于扩展, 而xml的方式太过于笨重, json的方式速度有点慢, protobuf是一个合理的方式, 把数据序列成二进制, 然后反序列化成原来的数据.
编译出protobuf文件
假设有一个msg.proto
文件, 那么需要执行:
protoc msg.proto --cpp_out=./
当前目录会多出2个文件, 分别是msg.pb.h
和msg.pb.cc
, 这是编码解码的文件.
消息格式
使用protobuf3的语法格式, 结合数据嵌套, 最后要发送的是 FinalMsg数据.
syntax = "proto3";
package Data;
message Vector {
int32 vx = 1;
int32 vy = 2;
int32 vz = 3;
}
message Point {
float px = 1;
float py = 2;
float pz = 3;
}
enum MsgType {
Vec = 0;
Pnt = 1;
}
message FinalMsg {
string username = 1;
string password = 2;
MsgType msgtype = 3;
Vector vector = 4;
Point point = 5;
}
客户端
g++ 1.cpp -o 1 data.pb.cc -lprotobuf -pthread -lboost_system
代码:
#include <iostream>
#include <boost/asio.hpp>
#include <string>
#include "data.pb.h"
std::string raw_ip_addr = "127.0.0.1";
unsigned short port_num = 6769;
int main() {
boost::asio::io_context ioc;
boost::asio::ip::tcp::endpoint
ep(boost::asio::ip::address::from_string(raw_ip_addr), port_num);
boost::asio::ip::tcp::socket sock(ioc, ep.protocol());
sock.connect(ep);
Data::FinalMsg finalMsg;
finalMsg.set_username(std::string("Tiger"));
finalMsg.set_password(std::string("123456"));
finalMsg.set_msgtype(Data::MsgType::Vec);
auto vec = finalMsg.mutable_vector();
vec->set_vx(1);
vec->set_vy(2);
vec->set_vz(3);
auto pnt = finalMsg.mutable_point();
pnt->set_px(1.1);
pnt->set_py(2.5);
pnt->set_pz(3.2);
std::string msg;
finalMsg.SerializeToString(&msg);
if (boost::asio::write(sock, boost::asio::buffer(msg)) < 0) {
std::cerr << "boost::asio::write() error\n";
return -1;
}
std::cout << "send successfully\n";
return 0;
}
服务器
g++ 2.cpp -o 2 data.pb.cc -lprotobuf -pthread -lboost_system
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include "data.pb.h"
size_t SIZE = 1024;
unsigned short port_num = 6769;
int main() {
boost::asio::io_context ioc;
boost::asio::ip::tcp::endpoint
ep(boost::asio::ip::address_v4::any(), port_num);
boost::asio::ip::tcp::acceptor acceptor(ioc, ep.protocol());
acceptor.bind(ep);
acceptor.listen(5); // 最多排列5个
boost::asio::ip::tcp::socket sock(ioc);
acceptor.accept(sock);
Data::FinalMsg finalMsg;
std::string msg;
boost::system::error_code error;
try {
char cmsg[SIZE];
SIZE = boost::asio::read(sock, boost::asio::buffer(cmsg, SIZE), error);
msg = std::move(std::string(cmsg, SIZE));
if (error != boost::asio::error::eof) {
throw boost::system::system_error(error);
}
finalMsg.ParseFromString(msg); // 解析数据
} catch (std::exception e) {
std::cout << "recv error: " << e.what() << std::endl;
sock.close();
return -1;
}
sock.close();
std::cout << "username: " << finalMsg.username() << std::endl;
std::cout << "password:" << finalMsg.password() << std::endl;
std::cout << "enum type: " << finalMsg.msgtype() << std::endl;
auto vec = finalMsg.vector();
auto pnt = finalMsg.point();
std::cout << "vector, " << "x = " << vec.vx() << ", y = " << vec.vy() << ", z = " << vec.vz() << std::endl;
std::cout << "point, " << "x = " << pnt.px() << ", y = " << pnt.py() << ", z = " << pnt.pz() << std::endl;
return 0;
}
输出结果:
username: Tiger
password:123456
enum type: 0
vector, x = 1, y = 2, z = 3
point, x = 1.1, y = 2.5, z = 3.2