之前给出了都是google提供的一些样例,但是protobuf提供的是数据的序列化和反序列化,显然在cs架构中非常适用,适用protobuf可以大大减小编程者对于通信双方格式的定义与解析,只用关系用户的逻辑功能即可。
下面拿一个简单的cs架构中双方心跳包的检测进行举例说明:
1、定义protubuf心跳包的数据结构类型
//Test.proto
package Test.protobuf ;//包名:在生成对应的C++文件时,将被替换为名称空间,在代码中会有体现
option optimize_for = SPEED ;//文件级别的选项,Protobuf优化级别
//心跳信息数据结构
message HeartInfo
{
required int32 curtime = 1;
required string hostip = 2 ;
};
2、编译产生头文件
protoc -I=./ --cpp_out=./ ./Test.proto
结果如下:
3、定义客户端通信代码
#include <iostream>
#include <string>
#include <ctime>
//for protobuf
#include "Test.pb.h"
//for socket
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
using namespace std;
using namespace Test::protobuf ;
const int BUFFSIZE = 128;
int main()
{
//建立socket
int socketfd ;
struct sockaddr_in seraddr ;
string hostip = "127.0.0.1";
//链接,尝试3次
for(int i = 0 ; i < 3;++i)
{
if((socketfd = socket(AF_INET,SOCK_STREAM,0)) > 0)
{
cout<<"create socket success..."<<endl;
break;
}
usleep(2);
}
//地址置空
bzero( &seraddr, sizeof(seraddr) );
//
seraddr.sin_family = AF_INET ;
seraddr.sin_port = htons(9999);
seraddr.sin_addr.s_addr = inet_addr(hostip.c_str());
//尝试连接到服务端地址
if(connect(socketfd,(struct sockaddr *)&seraddr, sizeof(seraddr)) < 0)
{
cout<<"connect to server failed ..."<<endl;
close(socketfd);
return -1;
}
HeartInfo myprotobuf;
while(1)
{
int curtime = time(NULL) ;
//以下方法的实现可以Test.pb.h中找到
myprotobuf.set_curtime(curtime);
myprotobuf.set_hostip("127.0.0.1");
//protobuf的序列化方式之一
char buff[BUFFSIZE];
myprotobuf.SerializeToArray(buff,BUFFSIZE);
if(send(socketfd,buff,strlen(buff),0) < 0)
{
cout<<curtime<<": send failed ..."<<endl;
break;
}
cout<<curtime<<": send success ..."<<endl;
usleep(5); //每隔5s发送一次
}
close(socketfd);
return 0;
}
编译客户端程序,生成可执行程序,命令如下:
g++ Test.pb.cc client.cpp -o client `pkg-config --cflags --libs protobuf`
生成文件如下:
4、定义服务端代码
#include <iostream>
#include <string>
#include <ctime>
//for protobuf
#include "Test.pb.h"
//for socket
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <unistd.h>
using namespace std;
using namespace Test::protobuf ;
const int BUFFSIZE = 128;
const int QLEN = 10 ;
int main()
{
int listenfd ;
int connfd ;
struct sockaddr_in seraddr ;
//建立socket
//AF_INET:IPv4因特网域
//SOCK_STREAM:TCP链接
//0:给定前两个参数,选择默认的协议
listenfd = socket(AF_INET,SOCK_STREAM,0);
if(listenfd < 0 )
{
cout<<"socket failed"<<endl;
}
//地址置空
bzero(&seraddr,sizeof(seraddr));
//
seraddr.sin_family = AF_INET ;
seraddr.sin_port = htons(9999);
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
//关联地址和套接字
if(bind(listenfd,(struct sockaddr *)&seraddr, sizeof(seraddr)) < 0)
{
cout<<"bind address with socket failed..."<<endl;
close(listenfd);
return -1;
}
//调用listen,宣告server愿意接受链接请求
if(listen(listenfd,QLEN) == -1)
{
cout<<"listen on socket failed..."<<endl;
close(listenfd);
return -1;
}
//获得连接请求,并建立连接
if( (connfd = accept(listenfd,(struct sockaddr *)NULL,NULL)) < 0 )
{
cout<<"accept the request failed"<<endl;
close(listenfd);
return -1;
}
HeartInfo myprotobuf;
char buff[BUFFSIZE];
while(1)
{
if(recv(connfd,buff,sizeof(buff),0) < 0)
{
cout<<"recv failed ..."<<endl;
break;
}
//protobuf反序列化
myprotobuf.ParseFromArray(buff,BUFFSIZE);
cout<<"last heart time:"<<myprotobuf.curtime()<<"\t"
<<"host ip:"<<myprotobuf.hostip()<<endl;
}
close(listenfd);
close(connfd);
return 0;
}
和客户端一样,编译代码,生成可执行文件
g++ Test.pb.cc server.cpp -o server `pkg-config --cflags --libs protobuf`
结果如下:
5、运行结果
先运行服务端程序,在运行客户端程序,结果如下:
6、参考
https://blog.csdn.net/cjf_wei/article/details/52894560
https://tech.meituan.com/serialization_vs_deserialization.html?utm_source=tuicool
http://www.cnblogs.com/stephen-liu74/archive/2013/01/04/2842533.html