Protocol Buffer在C++网络编程中使用教程

时间:2021-03-27 04:03:45

1. 下载 Protocol Buffers v2.6.1

https://github.com/google/protobuf/releases/tag/v2.6.1

2.使用 vs2012 编译 protobuf-2.6.1\vsprojects\protobuf.sln

在解决方案中 逐项进行编译,在Debug目录中生成lib和exe,
文件生成清单如下:
libprotobuf.lib
libprotobuf-lite.lib
libprotoc.lib 

lite-test.exe
protoc.exe
test_plugin.exe
tests.exe

3. protobuf测试:

创建两个工程(),每个工程都要添加 protobuf头文件 和 lib文件。

(1). 在属性-> C/C++ ->常规 (附加包含目录)
     添加 C:\xiaofan\tools\protobuf\protobuf-2.6.1\src

(2). 在属性-> 链接器 ->常规 (附加库目录)
     添加 C:\xiaofan\tools\protobuf\protobuf-2.6.1\libs 
     (注意: 此处的 libs 是我自己创建的,把Debug中的 lib 拷贝了进去 )

4.创建people.proto:

package PeopleT.Test;     
message People
{
required string name = 1;
required int32 id = 2;
required string email = 3;
};

编译生成头文件:
找到protoc.exe,运行cmd :
protoc -I=. --cpp_out=. people.proto
生成 people.pb.h 和 people.pb.cc

5.程序调用

使用VS2012分别创建protobuf_client和protobuf_server 程序。
把people.pb.h 和 people.pb.cc加入程序,在主程序中使用命名空间,

#include "people.pb.h"
using namespace PeopleT::Test;

注意:

(1).在windows下写socket程序时,需要加入 #pragma comment( lib, "ws2_32.lib")  ,该库主要提供网络相关API的支持。

(2).编译过程中如果遇到问题,请参见之前的文章:

在VS2012中 集成protobuf 文件时, 编译出现问题

详情参见程序如下:

// 客户端程序

#include "stdafx.h"
#include <stdio.h>
#include <WinSock2.h>
#include <iostream>
#include <string>
#include <ctime>
#include "people.pb.h"
using namespace std;
using namespace PeopleT::Test;

#pragma comment( lib, "ws2_32.lib")

const int BUFFSIZE = 128;

int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsd; //WSADATA变量
SOCKET socketfd; //服务器套接字
struct sockaddr_in seraddr ; //服务器地址
string hostip = "127.0.0.1";


//初始化套结字动态库
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
{
cout << "WSAStartup failed!" << endl;
return -1;
}

IPPROTO_TCP;
//链接,尝试3次
for(int i = 0 ; i < 3;++i)
{
if((socketfd = socket(AF_INET,SOCK_STREAM,0)) > 0)
{
cout<<"create socket success..."<<endl;
break;
}
Sleep(2000);
}

if(INVALID_SOCKET == socketfd)
{
cout<<"socket failed"<<endl;
WSACleanup();//释放套接字资源
return -1;
}


//地址置空
memset( &seraddr, sizeof(seraddr) ,0);
//设置服务器地址
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;
closesocket(socketfd);
WSACleanup(); //释放套接字资源
return -1;
}

People myprotobuf;
char buff[BUFFSIZE];

while( true )
{
int curtime = time(NULL) ;
//以下方法的实现可以Test.pb.h中找到
myprotobuf.set_name("wang");
myprotobuf.set_id(2);
myprotobuf.set_email("abc@123.com");

//protobuf的序列化方式之一
memset(buff,0,BUFFSIZE);
myprotobuf.SerializeToArray(buff,BUFFSIZE);

if(send(socketfd,buff,strlen(buff),0) < 0)
{
cout<<curtime<<": send failed ..."<<endl;
break;
}
cout<<curtime<<": send success ..."<<endl;

Sleep(3000); //每隔3s发送一次
}
closesocket(socketfd);
WSACleanup(); //释放套接字资源

return 0;
}

// 服务端程序
#pragma
#include "stdafx.h"
#include <winsock2.h>
#include <stdio.h>
#include <cstdio>
#include <iostream>
#include <string>
#include <ctime>
#include "people.pb.h"

#pragma comment( lib, "ws2_32.lib")

using namespace std;
using namespace PeopleT::Test;

const int BUFFSIZE = 128;
const int QLEN = 10 ;

int _tmain(int argc, char* argv[])
{

WSADATA wsd; //WSADATA变量
SOCKET listenfd; //服务器套接字
int connfd; //客户端套接字
struct sockaddr_in seraddr ; //服务器地址


//初始化套结字动态库
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
{
cout << "WSAStartup failed!" << endl;
return -1;
}

//建立socket
//AF_INET:IPv4因特网域
//SOCK_STREAM:TCP链接
//0:给定前两个参数,选择默认的协议
listenfd = socket(AF_INET,SOCK_STREAM,0);
if(listenfd < 0 )
{
cout<<"socket failed"<<endl;
WSACleanup();//释放套接字资源;
return -1;
}

//地址置空
memset(&seraddr,sizeof(seraddr),0);
//服务器套接字地址
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;
closesocket(listenfd);
WSACleanup(); //释放套接字资源;
return -1;
}

//调用listen,宣告server愿意接受链接请求 : 开始监听
if(listen(listenfd,QLEN) == INVALID_SOCKET )
{
cout<<"listen on socket failed..."<<endl;
closesocket(listenfd);
WSACleanup(); //释放套接字资源
return -1;
}

//获得连接请求,并建立连接
if( (connfd = accept(listenfd,(struct sockaddr *)NULL,NULL)) < 0 )
{
cout<<"accept the request failed"<<endl;
closesocket(listenfd);
WSACleanup(); //释放套接字资源
return -1;
}

People myprotobuf;
char buff[BUFFSIZE];
while( true )
{
if(recv(connfd,buff,sizeof(buff),0) < 0)
{
cout<<"recv failed ..."<<endl;
break;
}
//protobuf反序列化
myprotobuf.ParseFromArray(buff,BUFFSIZE);
cout<<"name :"<<myprotobuf.name()<<"\n"
<<"id :"<<myprotobuf.id()<<"\n"
<<"email :"<<myprotobuf.email()<<endl;
memset(buff,0,BUFFSIZE);
}
closesocket(listenfd);
closesocket(connfd);
WSACleanup(); //释放套接字资源

return 0;

}