此文作为protobuf 学习笔记!
Window移植
1. 环境准备
windows 7 X64;
VS2015社区版;[VisualStudio](https://www.visualstudio.com) path增加值 C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin
cmake - [CMake](http://www.cmake.org); 设置环境变量 ,安装时勾上。
2. 下载源码
https://github.com/google/protobuf/releases 妈的,墙真是牛逼,下了三天,开了VPN都不行。下载3.0.0版,protobuf-3.0.0.zip,以及 protoc-3.0.0-win32.zip
3. 解压文件
源代码路径D:\cpp\protobuf-3.0.0
生成工具路径D:\cpp\protoc-3.0.0-win32 环境变量path增加值D:\cpp\protoc-3.0.0-win32\bin
4. 编译
D:\cpp\protobuf-3.0.0 下 mkdir cmakebuild & cd cmakebuild (备注 build 目录没法建立,说是已经存在但是打不开);
继续D:\cpp\protobuf-3.0.0\cmakebuild 下 mkdir release & cd release; 建立工程目录
因为是为了生成sln工程,具体要出debug还是Release 库可以在工程中配置 所以不用指定-DCMAKE_BUILD_TYPE=Release ,cmake命令如下
D:\cpp\protobuf-3.0.0\cmakebuild/release> cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_INSTALL_PREFIX=../../../../D:\cpp\protobuf-3.0.0-install../..cmake
可以看到当前目录下出现protobuf.sln,打开编译,注意指定架构 x64 x86,版本debug还是Release;生成后的库复制到protobuf-3.0.0-install(该目录作为工程以后用到库的路径);
编译时提示找不到pthread.h,但是也能编译过。
<!--其他人出的问题:
按照提示打开CMakeError.log,看到提示:无法打开包括文件: “pthread.h”,额,好吧,windows下没有pthread库,下载之~ https://sourceware.org/pthreads-win32/#download,我下载的是 pthreads-w32-2-9-1-release(官网下载可能比较慢,可以选择其中的镜像进行下载)
后面想到当时命令行并没有添加include path的选项,所以即使使用了pthread也不太好配置(后面尝试了多种方式),后面又仔细看了下命令行输出(之前没太注意看命令行输出,只是直接看error文件,额 还是要仔细看看输出的~~),发现cmake终止是由于没有gmock。再看README中写到:
If the gmock directory does not exist, and you do not want tobuild protobuf unit tests,
you need to add cmake command argument -Dprotobuf_BUILD_TESTS=OFF to disabletesting.
好吧,我不用测试,遂在命令行中加入-Dprotobuf_BUILD_TESTS=OFF。OK,居然过了。
-->
5. 测试
5.1. 定义协议格式
新建协议格式文件addressbook.proto
packagetutorial; message 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 phone = 4;
}
message AddressBook {
repeated Person person = 1;
}
该结构与c++或java很像.
.proto文件以包声明开始,防止名字冲突。
简单类型:bool, int32,float, double, string.
其它类型:如上述的Person,PhoneNumber
类型可以嵌套。
“=1”, “=2”标识唯一“tag”.tag数1-15需要至少一个字节。
required: 必须设置它的值
optional: 可以设置,也可以不设置它的值
repeated: 可以认为是动态分配的数组
google工程师认为使用required威害更大, 他们更喜欢使用optional, repeated.
5.2. 编译你的协议
运行protoc 来生成c++文件:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto
生成的文件为:
addressbook.pb.h,
addressbook.pb.cc
5.3. protobuf API
生成的文件中有如下方法:
//name
inlinebool has_name()const;
inlinevoid clear_name();
inlineconst::std::string& name()const;
inlinevoid set_name(const::std::string& value);
inlinevoid set_name(constchar* value);
inline::std::string* mutable_name();
// id
inlinebool has_id()const;
inlinevoid clear_id();
inline int32_t id()const;
inlinevoid set_id(int32_t value);
// email
inlinebool has_email()const;
inlinevoid clear_email();
inlineconst::std::string& email()const;
inlinevoid set_email(const::std::string& value);
inlinevoid set_email(constchar* value);
inline::std::string* mutable_email();
// phone
inlineint phone_size()const;
inlinevoid clear_phone();
inlineconst::google::protobuf::RepeatedPtrField<::tutorial::Person_PhoneNumber>& phone()const;
inline::google::protobuf::RepeatedPtrField<::tutorial::Person_PhoneNumber>* mutable_phone();
inlineconst::tutorial::Person_PhoneNumber& phone(int index)const;
inline::tutorial::Person_PhoneNumber* mutable_phone(int index);
inline::tutorial::Person_PhoneNumber* add_phone();
5.4. Vs2015新建控制台工程
5.4.1. 测试工程1
包含目录增加 D:\cpp\protobuf-3.0.0\src;
库路径增加:D:\cpp\protobuf-3.0.0-install;
复制生成的文件addressbook.pb.h, addressbook.pb.cc到工程目录下,工程增加该文件,对addressbook.pb.cc设置属性,不处理预编译头。
编译测试代码:
// protobuf_002.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> #include <fstream> #include <string> #include "addressbook.pb.h" using namespace std; #ifndef _DEBUG #pragma comment(lib,"libprotobuf.lib") #pragma comment(lib,"libprotoc.lib") #else #pragma comment(lib,"libprotobufd.lib") #pragma comment(lib,"libprotocd.lib") #endif // This function fills in a Person message based on user input. void PromptForAddress(tutorial::Person* person) { cout << "Enter person ID number: "; int id; cin >> id; person->set_id(id); cin.ignore(256, '\n'); cout << "Enter name: "; getline(cin, *person->mutable_name()); cout << "Enter email address (blank for none): "; string email; getline(cin, email); if (!email.empty()) { person->set_email(email); } while (true) { cout << "Enter a phone number (or leave blank to finish): "; string number; getline(cin, number); if (number.empty()) { break; } tutorial::Person::PhoneNumber* phone_number = person->add_phone(); phone_number->set_number(number); cout << "Is this a mobile, home, or work phone? "; string type; getline(cin, type); if (type == "mobile") { phone_number->set_type(tutorial::Person::MOBILE); } else if (type == "home") { phone_number->set_type(tutorial::Person::HOME); } else if (type == "work") { phone_number->set_type(tutorial::Person::WORK); } else { cout << "Unknown phone type. Using default." << endl; } } } //Iterates though all people in the AddressBook and prints info about them. void ListPeople(const tutorial::AddressBook& address_book) { for (int i = 0; i < address_book.person_size(); i++) { const tutorial::Person& person = address_book.person(i); cout << "Person ID: " << person.id() << endl; cout << " Name: " << person.name() << endl; if (person.has_email()) { cout << " E-mail address: " << person.email() << endl; } for (int j = 0; j < person.phone_size(); j++) { const tutorial::Person::PhoneNumber& phone_number = person.phone(j); switch (phone_number.type()) { case tutorial::Person::MOBILE: cout << " Mobile phone #: "; break; case tutorial::Person::HOME: cout << " Home phone #: "; break; case tutorial::Person::WORK: cout << " Work phone #: "; break; } cout << phone_number.number() << endl; } } } int MyWrite(char** argv) { tutorial::AddressBook address_book; { // Read the existing address book. fstream input(argv[1], ios::in | ios::binary); if (!input) { cout << argv[1] << ": File not found. Creating a new file." << endl; } else if (!address_book.ParseFromIstream(&input)) { cerr << "Failed to parse address book." << endl; return -1; } } // Add an address. PromptForAddress(address_book.add_person()); { // Write the new address book back to disk. fstream output(argv[1], ios::out | ios::trunc | ios::binary); if (!address_book.SerializeToOstream(&output)) { cerr << "Failed to write address book." << endl; return -1; } } // Optional: Delete all global objects allocated by libprotobuf. google::protobuf::ShutdownProtobufLibrary(); } int MyRead(char** argv) { tutorial::AddressBook address_book; { // Read the existing address book. fstream input(argv[1], ios::in | ios::binary); if (!address_book.ParseFromIstream(&input)) { cerr << "Failedto parse address book." << endl; return -1; } } ListPeople(address_book); //Optional: Delete all global objects allocated by libprotobuf. google::protobuf::ShutdownProtobufLibrary(); } // Main function: Reads the entire address book from a file, // adds one person based on user input, then writes it back out to the same // file. 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 != 3) { cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << "w|r" << endl; return -1; } if (argv[2][0] == 'w') { MyWrite(argv); } else if (argv[2][0] == 'r') { MyRead(argv); } system("pause"); getchar(); system("pause"); return 0; }
|
Linux移植
1. Linux移植
在网站http://code.google.com/p/protobuf/downloads/list上可以下载 Protobuf 的源代码。然后解压编译安装便可以使用它了。
安装步骤如下所示:
tar -xzf protobuf-2.1.0.tar.gz
cd protobuf-2.1.0
./configure --prefix=/usr/local/protobuf
make
make check
make install
到此步还没有安装完毕,在
/etc/profile 或者用户目录 ~/.bash_profile
添加下面内容
####### add protobuf lib path ########
#(动态库搜索路径) 程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径
exportLD_LIBRARY_PATH=
$LD_LIBRARY_PATH:
/usr/local/protobuf/lib/
#(静态库搜索路径) 程序编译期间查找动态链接库时指定查找共享库的路径
exportLIBRARY_PATH=
$LIBRARY_PATH:
/usr/local/protobuf/lib/
#执行程序搜索路径
exportPATH=
$PATH:
/usr/local/protobuf/bin/
#c程序头文件搜索路径
exportC_INCLUDE_PATH=
$C_INCLUDE_PATH:
/usr/local/protobuf/include/
#c++程序头文件搜索路径
exportCPLUS_INCLUDE_PATH=
$CPLUS_INCLUDE_PATH:
/usr/local/protobuf/include/
#pkg-config 路径
exportPKG_CONFIG_PATH=
/usr/local/protobuf/lib/pkgconfig/
######################################
注意添加后重新ssh登陆才生效
创建文件/etc/ld.so.conf.d/libprotobuf.conf 写入内容:/usr/local/protobuf/lib
输入命令 sudo ldconfig
有可能提示
库不是符号链接,使用
ln
命令修改
ln -sflibprotobuf.so.10.0.0 libprotobuf.so.10
ln -sflibprotobuf.so.10 libprotobuf.so
安装后,输入protoc--version
验证是否安装成功。
2. 测试
1,people.proto
package demo;
message People {
required string name = 1;
required int32 id = 2;
required string email = 3;
}
2, 生成stub类
protoc --cpp_out=. people.proto
rprotoc people.proto
3, C++服务器端server.cc
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <iostream>
#include "people.pb.h"
#define PORT 8888
#define MAXDATASIZE 20
#define BACKLOG 10
using namespace std;
int main()
{
int listenfd, connectfd, numbytes;
char buf[MAXDATASIZE];
struct sockaddr_in server;
struct sockaddr_in client;
int sin_size;
listenfd = socket(AF_INET, SOCK_STREAM,0);
int opt = SO_REUSEADDR;
setsockopt(listenfd, SOL_SOCKET,SO_REUSEADDR, &opt, sizeof(opt));
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr =htonl(INADDR_ANY);
bind(listenfd, (struct sockaddr*)&server, sizeof(struct sockaddr));
listen(listenfd,BACKLOG);
while(1){
sin_size = sizeof(struct sockaddr_in);
connectfd = accept(listenfd, (structsockaddr *)&client, (socklen_t*)&sin_size);
numbytes = recv(connectfd, buf,MAXDATASIZE, 0);
buf[numbytes] = '\0';
string a = buf;
cout << "You got a messagefrom " << inet_ntoa(client.sin_addr) << endl;
cout << "Client Message:" << a << endl;
if(a == "GET PEOPLE") {
string data;
demo::People p;
p.set_name("Hideto");
p.set_id(123);
p.set_email("hideto.bj@gmail.com");
p.SerializeToString(&data);
char bts[data.length()];
strcpy(bts, data.c_str());
send(connectfd, bts, sizeof(bts),0);
}else {
send(connectfd, "Fuckingclient!\n", 16, 0);
}
close(connectfd);
}
close(listenfd);
return 0;
}
4, C++客户端client.cc
#include <stdio.h>
#include <unistd.h>
#include <strings.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string>
#include <iostream>
#include "people.pb.h"
#define HOST "localhost"
#define PORT 8888
#define MAXDATASIZE 100
using namespace std;
int main(int argc, char ** argv)
{
int fd, numbytes;
char buf[MAXDATASIZE];
struct hostent *he;
struct sockaddr_in server;
if (argc != 2) {
printf("Usage: %s\"COMMAND\"\n",argv[0]);
exit(0);
}
he = gethostbyname(HOST);
fd = socket(AF_INET, SOCK_STREAM,0);
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr = *((struct in_addr*)he->h_addr);
connect(fd, (struct sockaddr*)&server, sizeof(struct sockaddr));
send(fd, argv[1], 20, 0);
numbytes = recv(fd, buf, MAXDATASIZE,0);
buf[numbytes] = '\0';
string data = buf;
demo::People p;
p.ParseFromString(data);
cout << "People: "<< endl;
cout << "Name: " <<p.name() << endl;
cout << "ID: " <<p.id() << endl;
cout << "Email: "<< p.email() << endl;
close(fd);
return 0;
}
5, 使用g++编译
$ g++ server.cc people.pb.cc -o s-lprotobuf
$ g++ client.cc people.pb.cc -o c-lprotobuf
6, 运行
#启动server
./s
You got a message from 127.0.0.1
Client Message: GET PEOPLE
You got a message from 127.0.0.1
Client Message: GET PEOPLE
#运行c++的client
./c "GET PEOPLE"
People:
Name: Hideto
ID: 123
Email: hideto.bj@gmail.com