protobuf和socket通信简单实例
protobuf是 Google 公司内部的混合语言数据标准,可以用来定义通信的协议,由于其有序列化和反序列化的操作,减小了存储或通信的数据量,从而达到高效运行的目的。
此实例在ubuntu18.04下正常运行,其它操作系统没有经过测试,无法保证正常运行。
protobuf安装
protobuf的下载地址,如图1所示的安装包,安装可参考官网官网安装说明。
图1
下载完毕解压后,在主文件夹下运行以下命令进行安装。
./configure
make
make check
sudo make install
sudo ldconfig # refresh shared library cache.
编辑程序及编译
本文参考protobuf官网的教程,使用C++编写socket的服务端,python编写socket的客户端,最终实现服务端接收来自客户端的protobuf数据流。总共有4个文件(addressbook.proto、server.cpp、client.py、CMakeLists.txt)需要编写,其它文件通过命令自动生成,步骤如下:
- 创建工作空间文件夹protobuf_socket,并在protobuf_socket下创建必要的文件。
mkdir protobuf_socket
touch addressbook.proto
touch server.cpp
touch client.py
touch CMakeLists.txt
生成的文件目录如图2所示。
图2
- 在addressbook.proto文件中添加如下内容:
syntax = "proto2";
package tutorial;
message Person {
optional string name = 1;
optional int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
optional string number = 1;
optional PhoneType type = 2 [default = MOBILE];
}
repeated PhoneNumber phones = 4;
}
message AddressBook {
repeated Person people = 1;
}
- 在server.cpp文件中添加如下内容:
#include <iostream>
#include <fstream>
#include <string>
#include <boost/asio.hpp>
#include "addressbook.pb.h"
using namespace boost::asio;
using namespace std;
#define MAX_SIZE 128 * 1024
// 打印输出
void ListPeople(const tutorial::AddressBook& address_book) {
for (int i = 0; i < address_book.people_size(); i++) {
const tutorial::Person& person = address_book.people(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.phones_size(); j++) {
const tutorial::Person::PhoneNumber& phone_number = person.phones(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 main(int argc, char* argv[]) {
// 验证库的版本与头文件编译的内容是否一致
GOOGLE_PROTOBUF_VERIFY_VERSION;
// 所有asio类都需要io_service对象
io_service iosev;
ip::tcp::acceptor acceptor(iosev, ip::tcp::endpoint(ip::tcp::v4(), 5000));
while(1) {
// socket对象
ip::tcp::socket socket(iosev);
// 等待直到客户端连接进来
acceptor.accept(socket);
// 显示连接进来的客户端
std::cout << socket.remote_endpoint().address() << std::endl;
boost::system::error_code ec;
// 接收数据
char buf[MAX_SIZE];
size_t len = socket.read_some(buffer(buf), ec);
if(len >= MAX_SIZE) {
std::cout << "data is too big, receive failed!" << std::endl;
continue;
}
// 如果出错,打印出错信息
if(ec) {
std::cout << "receive error: " << boost::system::system_error(ec).what() << std::endl;
continue;
}
tutorial::AddressBook address_book;
if(address_book.ParseFromArray(buf, len) != 1) { //序列化
std::cout << "Failed to parse address book." << std::endl;
}
ListPeople(address_book);
}
// 可选项:删除所有protobuf分配的全局对象
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
- 在client.py文件下添加如下内容:
#! /usr/bin/python
# coding=utf-8
import socket
import addressbook_pb2
import sys
MAX_SIZE = 128 * 1024
#创建socket通信的对象
client = socket.socket()
#连接服务器的IP地址和端口号
client.connect(("localhost", 5000))
# 手动输入消息并打印
def PromptForAddress(person):
person.id = int(input("Enter person ID number: "))
person.name = input("Enter name: ")
email = input("Enter email address (blank for none): ")
if email != "":
person.email = email
while True:
number = input("Enter a phone number (or leave blank to finish): ")
if number == "":
break
phone_number = person.phones.add()
phone_number.number = number
type = input("Is this a mobile, home, or work phone? ")
if type == "mobile":
phone_number.type = addressbook_pb2.Person.PhoneType.MOBILE
elif type == "home":
phone_number.type = addressbook_pb2.Person.PhoneType.HOME
elif type == "work":
phone_number.type = addressbook_pb2.Person.PhoneType.WORK
else:
print ("Unknown phone type; leaving as default value.")
if __name__ == "__main__":
address_book = addressbook_pb2.AddressBook()
PromptForAddress(address_book.people.add())
msg = address_book.SerializeToString()
if len(msg) >= MAX_SIZE:
print('data is too big, server will not receive data!')
client.send(msg)
- 在CMakeLists.txt文件下添加如下内容:
# Minimum CMake required
cmake_minimum_required(VERSION 3.1.3)
if(protobuf_VERBOSE)
message(STATUS "Protocol Buffers Configuring...")
endif()
# CMake policies
cmake_policy(SET CMP0022 NEW)
# On MacOS use @rpath/ for target's install name prefix path
if (POLICY CMP0042)
cmake_policy(SET CMP0042 NEW)
endif ()
# Clear VERSION variables when no VERSION is given to project()
if(POLICY CMP0048)
cmake_policy(SET CMP0048 NEW)
endif()
# Project
project(protobuf_test C CXX)
find_package(Boost COMPONENTS regex system REQUIRED)
# Add c++11 flags
if (CYGWIN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
else()
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
endif()
add_executable(server server.cpp addressbook.pb.cc)
target_link_libraries(server protobuf ${Boost_LIBRARIES})
- 在protobuf_socket文件夹下编译addressbook.proto文件
protoc -I=. --cpp_out=. addressbook.proto
protoc -I=. --python_out=. addressbook.proto
&ems; “-I=.”表示编译的文件(addressbook.proto)位于当前文件夹。“-cpp_out=.”表示将生成的文件放在当前文件夹。最终生成的文件目录如图3所示。
图3
- 在protobuf_socket文件夹下编译客户端server.cpp文件
mkdir build
cd build
cmake ..
make
运行程序
- 在protobuf_socket文件夹下运行服务端程序
./build/server
- 在protobuf_socket文件夹下运行客户端程序,并根据提示输入信息,在服务端将看到最终的信息。
python3 client.py
评论(0)
您还未登录,请登录后发表或查看评论