使用protobuf和socket实现客户端服务端消息的传递

1,019 阅读2分钟

通过一个简单的示例,记录如何使用protobuf。

本文用的protobuf版本是2.6.1。

众所周知,protobuf是一个序列化/反序列化工具。本例中,由客户端对数据进行序列化,由服务端进行反序列化。客户端和服务端的通信通过socket。

本文的示例,client端会每隔5s向server端发送自己的状态信息,信息中包括client端发送此次心跳的时间和client端的地址。server端解析client端的心跳信息后输出该信息。本文的代码调试均在Linux环境下。

安装protobuf

  1. 下载release 包(protobuf-2.6.1.tar.gz) github.com/protocolbuf…

github.com/protocolbuf…

  1. 解压后,在根目录下,依次执行。
$ ./configure 
$ make 
$ make install (需要root权限)

使用protobuf

  1. 新建示例目录:demo
  2. 在demo目录下,创建Makefile 文件
CC= g++ -g -D__LINUX__ -Wall -fPIC
AR= ar -r

PROJ_LIB= libHeartInfo.a
PROJ_OBJ= HeartInfo.pb.o

all:$(PROJ_LIB) 

prepare:
	protoc -I=./ --cpp_out=./ HeartInfo.proto

$(PROJ_LIB):$(PROJ_OBJ)
	$(AR) $(PROJ_LIB) $(PROJ_OBJ)

.cc.o:
	$(CC) -c $< -o $@

clean:
	rm -f *.o
	rm -f HeartInfo.pb.*

client: client.o
	g++ client.o -o client -L. -lHeartInfo -lprotobuf

client.o: client.cpp HeartInfo.pb.h
	g++ -c client.cpp

server: server.o
	g++ server.o -o server -L. -lHeartInfo -lprotobuf

server.o: server.cpp HeartInfo.pb.h
	g++ -c server.cpp
  1. 在demo目录下,创建 HeartInfo.proto文件
//Test.proto
package Test.protobuf ;//包名:在生成对应的C++文件时,将被替换为名称空间,在代码中会有体现
option optimize_for = SPEED ;//文件级别的选项,Protobuf优化级别
//心跳信息数据结构
message HeartInfo
{
    required int32 curtime = 1;
    required string hostip = 2 ; 
};
  1. 执行 make prepare 生成 HeartInfo.pb.cc HeartInfo.pb.h

  2. 执行 make 生成 libHeartInfo.a

  3. 创建server.cpp 文件

#include <iostream>
#include <string>
#include <ctime>
#include <unistd.h>
//for protobuf
#include "HeartInfo.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>

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(7657);
    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;
}

  1. 创建client.cpp文件
#include <iostream>
#include <string>
#include <ctime>
#include <unistd.h>
//for protobuf
#include "HeartInfo.pb.h" 
//for socket
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.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;
        }
        sleep(2);
    }
    //地址置空
    bzero( &seraddr, sizeof(seraddr) );
    //
    seraddr.sin_family = AF_INET ;
    seraddr.sin_port = htons(7657);
    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;
        sleep(5); //每隔5s发送一次
    }
    close(socketfd);
    return 0;
}

  1. 生成可执行文件 serverclient
$make server
$make client

引用

blog.csdn.net/cjf_wei/art…