protobuf3 ubuntu20安装和验证

前序

公司的protobuf相关文件实在windows下生成的,本文是个人想学习Linux下如何生成相关文件,把自己安装步骤和简单实用做一下记录。
我基于ubuntu 20.04.1虚拟机,g++ 9.3.0来讲解。

1. 请确保你的编译器支持c++11及其以上,尽量支持c++14。
2. 下文在涉及编译器版本编写特定代码处会加以说明。

protobuf3简介

简介引用这位博主的文章

步骤

下载及安装

本次我先从github中下载cpp版本的源代码的protobuf-cpp-version.tar.gz/zip (我用的protobuf-cpp-3.15.6.zip) 到windows中。
在这里插入图片描述
然后在虚拟机中,以root登录。将刚刚下好的文件拷到/opt/目录,然后将上述压缩包解压至此。

unzip protobuf-cpp-3.15.6.zip

进入解压后的文件目录

cd protobuf-3.15.6 

./configure 执行
它默认是安装在 /usr/local, 然后make一下,编译时间略长,可以指定核心数量来减少编译时间。

./configure
make && make install

环境变量

vi /etc/profile ,追加以下内容。

#(动态库搜索路径) 程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/protobuf/lib/
#(静态库搜索路径) 程序编译期间查找动态链接库时指定查找共享库的路径
export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/protobuf/lib/
#执行程序搜索路径
export PATH=$PATH:/usr/local/protobuf/bin/
#c程序头文件搜索路径
export C_INCLUDE_PATH=$C_INCLUDE_PATH:/usr/local/protobuf/include/
#c++程序头文件搜索路径
export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/usr/local/protobuf/include/
#pkg-config 路径
export PKG_CONFIG_PATH=/usr/local/protobuf/lib/pkgconfig/

vi /etc/ld.so.conf,追加/usr/local/protobuf/lib
在这里插入图片描述
将文件/etc/ld.so.conf列出的路径下的库文件缓存到/etc/ld.so.cache以供使用,因此当安装完一些库文件,或者修改/etc/ld.so.conf增加了库的新搜索路径,需要运行一下ldconfig,使所有的库文件都被缓存到文件/etc/ld.so.cache中,如果没做,可能会找不到刚安装的库。

/sbin/ldconfig -v

验证protobuf环境配置成功

protoc --version

出现以下输出表示环境配置成功。

root@ubuntu:~# protoc --version
libprotoc 3.15.6

至此pb3安装完成!

C++演示

pb文件生成

我一般在/home/yz/share/下工作,于是我在它里面创建一个protobuf_test目录,然后进去创建一个msg.proto的文件

syntax = "proto3";

package student_msg;

message info
{
    int32 class_room = 1;
    string student_id = 2;
}

message student
{
    int32 index = 1;
    info info = 2;
}

message student_list
{
    repeated student students = 1;
}

执行下面命令,将msg.proto映射为相应头文件和源文件

protoc --cpp_out=. msg.proto 

生成msg.pb.hmsg.pb.cc

C++开发,编译及运行

创建一个cpp文件,码代码

vi main.cpp
#include <iostream>
#include <memory>
#include <random>
#include <string>
#include <fcntl.h>
#include <msg.pb.h>

#ifdef __LINUX__
#include <unistd.h>
#endif

int main()
{
	// 随机数种子
    std::default_random_engine seed{};
    std::uniform_int_distribution<unsigned> uniform(0, 9);

    auto student_list{std::make_unique<student_msg::student_list>()};
    for (size_t i{0}; i < 10; ++i)
    {
    	// 填充每位同学的信息
        auto student = student_list->add_students();
        student->set_index(i+1);
        auto student_info = student->mutable_info();
        student_info->set_class_room(uniform(seed)); // 随机产生班级
        student_info->set_student_id(std::string("No.") + std::to_string(100 + i));
    }

	// 写入当前目录下的文件中
    int fd{::open("./store.txt", O_CREAT | O_RDWR, 0777)};
    student_list->SerializePartialToFileDescriptor(fd);
    ::close(fd);

	// 打开文件,看看之前的录入是否有效
    fd = ::open("./store.txt", O_RDWR, 0777);
    
    student_list.reset(new student_msg::student_list);
    student_list->ParseFromFileDescriptor(fd);
    for (size_t i{0}; i < student_list->students_size(); ++i)
    {
        std::cout << " student.index: " << student_list->students(i).index()
                  << " student.classroom: " << student_list->students(i).info().class_room()
                  << " student.id: " << student_list->students(i).info().student_id()
                  << std::endl;
    }
    ::close(fd);

    return 0;
}

上文中第18行代码,需要C++14支持。如果你的编译器版本不够,请换为普通的unique_ptr的构造。

学会实用cmake,解决因为找不到系统头文件和库所带来的问题,这里推荐我自己学习过程中写的文章cmake

在当前目录创建CMakeLists.txt,写入

cmake_minimum_required(VERSION 3.15)

project(PROTO)

aux_source_directory(${PROJECT_SOURCE_DIR} DIR_MAIN_SRCS)

# -I
include_directories(${PROJECT_SOURCE_DIR})

# parameterss
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAG ${CMAKE_CXX_FLAG} "-O2 -Wall -g ")

# macro
add_definitions(-D __LINUX__)

# -L
set(LIB_DIR /usr/local/lib)  
link_directories(${LIB_DIR})

# -o
add_executable(proto ${DIR_MAIN_SRCS})

# -l
set(LIB_NAME protobuf pthread)
target_link_libraries(proto ${LIB_NAME})

在当前目录创建文件夹build

mkdir build
cd build
cmake ..
make
./proto

点斜杠执行proto后的输出为

 student.index: 1 student.classroom: 0 student.id: No.100
 student.index: 2 student.classroom: 1 student.id: No.101
 student.index: 3 student.classroom: 7 student.id: No.102
 student.index: 4 student.classroom: 4 student.id: No.103
 student.index: 5 student.classroom: 5 student.id: No.104
 student.index: 6 student.classroom: 2 student.id: No.105
 student.index: 7 student.classroom: 0 student.id: No.106
 student.index: 8 student.classroom: 6 student.id: No.107
 student.index: 9 student.classroom: 6 student.id: No.108
 student.index: 10 student.classroom: 9 student.id: No.109

对了,展示一下还没有在build目录下cmake前的我的目录吧,虽然没有把源文件、头文件分离,但是这里只是做测试,不用在意细节~

.
├── build
├── CMakeLists.txt
├── main.cpp
├── msg.pb.cc
├── msg.pb.h
└── msg.proto

更新 增加一个网络传输版本:
服务端代码:

#include <arpa/inet.h>
#include <msg.pb.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include <cstring>
#include <iostream>

const uint16_t PORT = 6666;
const uint16_t BUFFER_LEN = 1000;
const int LISTEN_PAD = 1;
const char* IPADDR = "127.0.0.1";
const std::string bye("get your massage, and good bye.");

auto main() -> int {
  int fd = ::socket(AF_INET, SOCK_STREAM, 0);

  sockaddr_in sock{};
  sock.sin_family = AF_INET;
  sock.sin_port = htons(PORT);
  sock.sin_addr.s_addr = ::inet_addr(IPADDR);

  if (-1 == ::bind(fd, reinterpret_cast<sockaddr*>(std::addressof(sock)),
                   sizeof(sockaddr_in))) {
    ::close(fd);
    ::exit(-1);
  }

  if (-1 == ::listen(fd, LISTEN_PAD)) {
    ::close(fd);
    ::exit(-1);
  }
  std::cout << "TURN TO LISTEN STATE WAITING FOR ONE PEER CONNECT..."
            << std::endl;

  sockaddr_in peer_sock{};
  uint32_t peer_addr_size = sizeof(sockaddr_in);
  int cfd = ::accept(fd, reinterpret_cast<sockaddr*>(std::addressof(peer_sock)),
                     &peer_addr_size);

  if (-1 == cfd) {
    ::close(fd);
    ::exit(-1);
  }
  std::cout << "CONNECT ESTABLISH TO PEER:" << cfd << std::endl
            << "IP:" << peer_sock.sin_port << " WAITING FOR IT SEND MESSAGE..."
            << std::endl;

  auto buffer = new uint8_t[BUFFER_LEN]{0};
  auto read_len = BUFFER_LEN;

  {
    read_len = ::read(cfd, buffer, BUFFER_LEN);
    if (0 >= read_len) {
      ::close(cfd);
      ::close(fd);
      return 0;
    }

    // 对端没关闭时,read阻塞
    auto student_list{std::make_unique<student_msg::student_list>()};
    if (student_list->ParseFromArray(buffer, read_len)) {
      for (size_t i{0}; i < student_list->students_size(); ++i) {
        std::cout << " student.index: " << student_list->students(i).index()
                  << " student.classroom: "
                  << student_list->students(i).info().class_room()
                  << " student.id: "
                  << student_list->students(i).info().student_id() << std::endl;
      }

      ::write(cfd, bye.data(), bye.size());

      sleep(1);
      ::close(cfd);
      ::close(fd);
      return 0;
    }

    ::close(cfd);
    ::close(fd);
    return 0;
  }
}

客户端代码:

#include <arpa/inet.h>
#include <msg.pb.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include <cstring>
#include <iostream>
#include <memory>
#include <random>
#include <thread>

const uint16_t PORT = 6666;
const uint16_t BUFFER_LEN = 1000;
const char* IPADDR = "127.0.0.1";

using namespace std::literals;

auto main() -> int {
  int fd = ::socket(AF_INET, SOCK_STREAM, 0);

  sockaddr_in server{};
  server.sin_family = AF_INET;
  server.sin_port = htons(PORT);
  server.sin_addr.s_addr = ::inet_addr(IPADDR);

  if (-1 == ::connect(fd, reinterpret_cast<sockaddr*>(std::addressof(server)),
                      sizeof(sockaddr_in))) {
    std::cout << "error:" << errno << std::endl;
    ::exit(-1);
  }

  auto student_list{std::make_unique<student_msg::student_list>()};

  // 组织protobuf
  {
    std::default_random_engine seed{};
    std::uniform_int_distribution<unsigned> uniform(0, 9);
    for (size_t i{0}; i < 10; ++i) {
      auto student = student_list->add_students();
      student->set_index(i + 1);
      auto student_info = student->mutable_info();
      student_info->set_class_room(uniform(seed));
      student_info->set_student_id(std::string("No.") +
                                   std::to_string(100 + i));
    }
  }

  std::string msgInput{};
  student_list->SerializePartialToString(std::addressof(msgInput));

  if (-1 != fd and -1 == ::write(fd, msgInput.data(), msgInput.size())) {
    ::close(fd);
    ::exit(-1);
  }

  // 收服务端回复
  std::unique_ptr<char> bufferGet(new char[50]{0});
  int readLen = -1;
  readLen = ::read(fd, bufferGet.get(), BUFFER_LEN);

  // 对端关闭时且本端fd没关闭,read读到数据为0,对端没关闭则阻塞都实际长度,读到-1失败
  if (0 >= readLen) {
    ::close(fd);
    ::exit(-1);
  }

  std::cout << std::string(bufferGet.get(), readLen) << std::endl;

  ::close(fd);
  return 0;
}

出于演示,期间很多可复用代码,并没封装,太懒了,直接写了一个脚本 里面使用g++进行编译

#!/bin/bash

echo "compile server..."
g++ -g server.cc msg.pb.cc -I./ -L/usr/local/lib -lprotobuf -lpthread -o my_server

echo "compile client..."
g++ -g client.cc msg.pb.cc -I./ -L/usr/local/lib -lprotobuf -lpthread -o my_client

运行结果和之前大差不差

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

歪锅锅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值