很早就听闻过Protocol Buffer是个很神奇的东西,只是没有使用过,由于工作上的需要,决定试试Protocol Buffer,废话不多说,先来看看如何安装Protocol Buffer吧,平台是Linux
安装步骤:
1)下载Protocol Buffer源码包 https://code.google.com/p/protobuf/downloads/list
2) 使用 tar -xvf protobuf-2.5.0.tar.bz2
3) 开始编译protocol buffer
1. ./configure --prefix=/usr/local/protobuf
2. make
3. make check
4. make install
经过了这些步骤之后,就可以开始我们的Protocol Buffer之旅了,下面我们就从最简单的开始吧,一探究竟,在写Protocol Buffer应用之前,我们需要知道使用Protocol Buffer的使用步骤:
1)定义消息结构文件,一般都是以.proto为后缀
2)使用Protocol Buffer 编译器来编译消息结构文件,并且生成对应的***.pb.h 和***.pb.cc文件
3)使用生成的接口文件中的API来实现消息发送与接收
看完了上述步骤之后,我们来写个简单的测试例子,代码如下:
1. 首先定义消息的结构
message Person{
required string name = 1;
required int32 id = 2;
optional string email = 3;
}
message AddressBook{
repeated Person person = 1;
}
在这个结构中,我们定义了两个子结构,其中AddressBook结构包含了一个Person结构,下面我们就使用protoc -I=. --cpp_out=. ./test.proto 来生成test.pb.h和test.pb.cc文件,其中文件test.pb.h文件中生成了很多的接口API,其中我们只需要使用其中的部分API来完成,接下来,我们来看看怎么来使用这些API吧,代码如下:
#include
#include
#include
#include "test.pb.h"
using namespace std;
void People(const Person& person)
{
cout << "Person ID: " << person.id() << endl;
cout << "Name: " << person.name() << endl;
if (person.has_email()) {
cout << "E-mail address: " << person.email() << endl;
}
}
// Iterates though all people in the AddressBook and prints info about them.
void ListPeople(const AddressBook& address_book) {
for (int i = 0; i < address_book.person_size(); i++) {
const Person& person = address_book.person(i);
People(person);
}
}
void write(const char* filename)
{
Person person;
person.set_id(123);
person.set_name("abc");
person.set_email("[email protected]");
fstream out(filename,ios::out |ios::binary | ios::trunc);
person.SerializeToOstream(&out);
out.close();
}
// Main function: Reads the entire address book from a file and prints all
// the information inside.
int main(int argc, char* argv[]) {
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;
if (argc != 2) {
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}
write(argv[1]);
//AddressBook address_book;
Person person;
{
// Read the existing address book.
fstream input(argv[1], ios::in | ios::binary);
if (!person.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
People(person);
// ListPeople(address_book);
// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
测试结果:
Person ID: 123
Name: abc
E-mail address: [email protected]
从这个测试案例中,我们使用了对于文件流的操作,主要是从文件流中获取数据以及将需要序列化的数据写入文件,ParseFromIstrem和SeralizaToOstream,除了这两个接口,在案例中,我们还是用了test.pb.h这个文件中的一些接口,例如一些set函数和get函数,接下来,我们就来看看如何将Protocol Buffer应用到一个实际的案例中,就以一个简单的聊天室为例吧,来看看如何将消息进行序列化发送以及如何将接收到的消息进行反序列化,具体的代码如下:
1)服务器端
#ifndef __CHATSERVER__H
#define __CHATSERVER__H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "Chat.pb.h"
#define MAX_EVENTS 255
class ChatServer
{
public:
ChatServer(const int port):port(port)
{
epollfd = epoll_create(MAX_EVENTS);
}
~ChatServer()
{
}
bool init()
{
sock = ::socket(AF_INET,SOCK_STREAM,0);
setnonblocking(sock);
struct sockaddr_in serverAddr,clientAddr;
bzero(&serverAddr,sizeof(serverAddr));
bzero(&clientAddr,sizeof(clientAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
serverAddr.sin_addr.s_addr = htons(INADDR_ANY);
addEvent(epollfd,EPOLLIN,sock);
if(bind(sock,(struct sockaddr*)&serverAddr,sizeof(serverAddr))<0)
{
printf("bind the address failed\n");
return false;
}
if(listen(sock,10)<0)
{
printf("listen failed\n");
return false;
}
return true;
}
void setnonblocking(int sockfd)
{
int opts;
opts=fcntl(sockfd,F_GETFL);
if(opts<0)
{
perror("fcntl(sock,GETFL)");
exit(1);
}
opts = opts | O_NONBLOCK;
if(fcntl(sockfd,F_SETFL,opts)<0)
{
perror("fcntl(sock,SETFL,opts)");
exit(1);
}
}
bool addEvent(int epollfd,int events,int sock)
{
struct epoll_event ev;
bzero(&ev,sizeof(ev));
ev.data.fd = sock;
ev.events = events;
if(epoll_ctl(epollfd,EPOLL_CTL_ADD,sock,&ev)<0)
{
printf("add epoll failed\n");
return false;
}
return true;
}
bool delEvent(int epollfd,int events,int sock)
{
struct epoll_event ev;
bzero(&ev,sizeof(ev));
ev.data.fd = sock;
ev.events = events;
if(epoll_ctl(epollfd,EPOLL_CTL_DEL,sock,&ev)<0)
{
printf("delete epoll failed\n");
return false;
}
return true;
}
void modEvent(int epollfd,int events,int sock)
{
struct epoll_event ev;
bzero(&ev,sizeof(ev));
ev.data.fd = sock;
ev.events = events;
epoll_ctl(epollfd,EPOLL_CTL_MOD,sock,&ev);
}
void boardcast(int sockfd)
{
char buffer[1024];
bzero(buffer,sizeof(buffer));
int rcvlen = ::recv(sockfd,buffer,sizeof(buffer),0);
if(rcvlen <=0)
return;
printf("buffer:%s\n",buffer);
for(int i=0;i fdSet;
};
#endif
2)客户端
#ifndef __CHATCLIENT__H
#define __CHATCLIENT__H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "Chat.pb.h"
using namespace std;
using namespace boost;
#define MAX_BUFFERSIZE 1024
struct info
{
info()
{
bzero(this,sizeof(this));
}
info(const info& in)
{
fd = in.fd;
clientname = in.clientname;
}
info(const info* in)
{
fd = in->fd;
clientname = in->clientname;
}
info& operator = (const info& in)
{
fd = in.fd;
clientname = in.clientname;
return *this;
}
int fd;
std::string clientname;
};
void sendString(int sock,const string& str)
{
char buffer[MAX_BUFFERSIZE];
bzero(buffer,sizeof(buffer));
memcpy(buffer,str.c_str(),strlen(str.c_str()));
::send(sock,buffer,strlen(str.c_str()),0);
}
static void* send(void* arg)
{
shared_ptr p(new info((info*)arg));
Msg msg;
string context;
string data;
for(;;)
{
getline(cin,context);
msg.set_fromname(p->clientname);
msg.set_msg(context);
msg.SerializeToString(&data);
sendString(p->fd,data);
}
}
static void* recv(void* arg)
{
shared_ptrp (new info((info*)arg));
for(;;)
{
char buffer[MAX_BUFFERSIZE];
bzero(buffer,sizeof(buffer));
int rcdlen = ::recv(p->fd,buffer,MAX_BUFFERSIZE,0);
if(rcdlen <=0)
return NULL;
Msg msg;
msg.ParseFromString(buffer);
cout<<"fromname:"<
测试文件:
客户端
#include "ChatClient.h"
int main()
{
string clientname;
getline(cin,clientname);
ChatClient chatClient(clientname,"127.0.0.1",19999);
if(!chatClient.init())
{
printf("chatClient init failed\n");
return 0;
}
chatClient.run();
sleep(60);
chatClient.stop();
return 0;
}
服务器端
#include "ChatServer.h"
int main()
{
ChatServer charServer(19999);
if(!charServer.init())
{
printf("chatServer init failed\n");
return 0;
}
charServer.loop();
return 0;
}
测试结果:
客户端:
A
hello, i coming my name is A
fromname:A msg:hello, i coming my name is A
how do you do?
fromname:A msg:how do you do?
fromname:B msg:welcome,My name is B
fromname:B msg:nice to meet you!!!!
总结
这篇博文主要是针对protocol buffer安装以及一些简单的应用进行了学习,并且在博文后面自己写了一个简单的聊天室,代码很简单,里面没有过多地考虑更多的安全问题,主要是为了系统地学习下protocol buffer的一些基本的应用,这部分主要是在客户端,在服务器端只是实现了对数据包的转发而已,计划在后面的博文中,会针对Protocol Buffer里面的一些基本接口以及用法进行深入的分析学习,也会涉及到一些高级的代码应用开发,好了,这篇博文到此结束了,谢谢,
如果需要,请注明转载,多谢