strtof linux内核,Qt for Android使用grpc探索

利用Qt在Android上使用grpc需要*.a的静态库,Windows上编译的lib库以及linux编译出来的.a,经过尝试,均无法链接成功。本文尝试使用NDK来编译Android版本的grpc静态库。

前言

Qt for Android上要使用grpc,需要用到对应Android架构的静态库文件,本文总结记录下在Ubuntu

准备工作

安装git:sudo apt install git

安装cmake:sudo apt install cmake

安装构建工具:sudo apt-get install build-essential autoconf libtool

下载Android NDK:android-ndk-r18b-linux-x86_64.zip

安装grpc:

$ mkdir git & cd git

$ git clone https://github.com/grpc/grpc

$ cd grpc

$ git submodule update --init

编译

环境:Ubuntu18.04

使用Android NDK交叉工具链编译:

$ cd ../ & mkdir grpc-build

$ vim compile.sh 输入以下脚本内容

#!/bin/sh

cd /home/yakong/git/grpc-build

export NDK_ROOT=/home/yakong/Downloads/android-ndk-r16b

cmake /home/yakong/git/grpc/

-DCMAKE_TOOLCHAIN_FILE=${NDK_ROOT}/build/cmake/android.toolchain.cmake

-DANDROID_ABI=armeabi-v7a

-DANDROID_PLATFORM=android-24

-DANDROID_STL=c++_static

-DRUN_HAVE_STD_REGEX=0

-DRUN_HAVE_POSIX_REGEX=0

-DRUN_HAVE_STEADY_CLOCK=0

-DCMAKE_BUILD_TYPE=Release

cmake --build . --target grpc++

69618e1a2c25318ae8e5a539edefe4a4.png

$ chmod +x ./compile.sh

$ ./compile.sh

成功构建完成后,在grpc-build生成各*.a静态库文件

ef69938bdfdaa02aff857d5ec5b587be.png

Qt for Android使用

环境:Windows10+Qt5.12.3+Android NDK r18b+华为荣耀9手机

新建QtgRPC-Server应用程序,链接好grpc以及第三方的protobuffer等静态库后,构建出现undefined reference to `rand/stderr/strtof`的链接错误,解决请看附录章节。

程序构建打包生成apk后,在Android设备上安装运行直接崩溃退出,提示Protocol Buffer版本不匹配错误,解决请看附录章节。

程序可以在Android真机上运行起来了,但是看日志显示好像?grpc服务没有启动起来,在应用程序输出窗口有以下红色错误(看不懂):

aa497b56b0aa790c07ad735be04663fd.png

ce4c66cf60f42709ba32616d9f85dad3.png

一波刚平,一波又起,真是一波接一波,继续打怪!但是,我直接运行Console服务端(QtgRPC-Server),以及Qt Widget客户端(grpc-client),结果显示可以通信,哈哈,上述红色日志部分不影响,最终运行的结果:

ca484daae4fd62b33785490dc2776b0f.png

31fdc216c511fd1c25915c848fe93f93.png

至此,基于Qt C++跨平台特性,Qt for Android调用grpc成功!

附录

1. /bin/sh: 1: go: not found错误

[ 15%] Generating err_data.c

/bin/sh: 1: go: not found

third_party/boringssl/crypto/err/CMakeFiles/err.dir/build.make:83: recipe for target 'third_party/boringssl/crypto/err/err_data.c' failed

make[3]: *** [third_party/boringssl/crypto/err/err_data.c] Error 127

make[3]: *** Deleting file 'third_party/boringssl/crypto/err/err_data.c'

CMakeFiles/Makefile2:3345: recipe for target 'third_party/boringssl/crypto/err/CMakeFiles/err.dir/all' failed

make[2]: *** [third_party/boringssl/crypto/err/CMakeFiles/err.dir/all] Error 2

CMakeFiles/Makefile2:1016: recipe for target 'CMakeFiles/grpc++.dir/rule' failed

make[1]: *** [CMakeFiles/grpc++.dir/rule] Error 2

Makefile:463: recipe for target 'grpc++' failed

make: *** [grpc++] Error 2

原因为缺少go编译环境

解决:sudo apt install golang

2. 查看*.a架构信息

yakong@ubuntu:~/git/grpc-build/third_party/protobuf$ ls

CMakeFiles lib Makefile protobuf.pc

cmake_install.cmake libprotobuf.a protobuf-lite.pc

yakong@ubuntu:~/git/grpc-build/third_party/protobuf$ file libprotobuf.a

libprotobuf.a: current ar archive

yakong@ubuntu:~/git/grpc-build/third_party/protobuf$ lipo -info libprotobuf.a

Command 'lipo' not found, did you mean:

command 'lilo' from deb lilo

Try: sudo apt install

yakong@ubuntu:~/git/grpc-build/third_party/protobuf$ readelf -h libprotobuf.a | grep 'Class|File|Machine'

File: libprotobuf.a(any_lite.cc.o)

Class: ELF32

Machine: ARM

File: libprotobuf.a(arena.cc.o)

Class: ELF32

Machine: ARM

3. undefined reference to `rand/stderr/strtof`

Qt for Android中使用grpc出现链接错误:

1f7ee7f2c2adae41c63fcd7bec5c8ec2.png

该文章指出,在NDK15以后,标准IO设备:stderr 等都不被支持了~

仔细发现我使用的是r16b版本编译grpc,而Qt Creator中却使用的是r18b版本,版本确实不一致,有可能会造成影响。

6480b84e1b2614d29369d535f0fb5b61.png

然而,我将Qt Creator使用的ndk版本换成r16b,一样的错误:

d6b5c6073a21d4c6c263e2ec68762004.png

我再次使用r18b版本的ndk在Ubuntu上进行grpc编译,还是出现类似错误:

2c261456913b5743c82fea6a3acf7100.png

对照错误出现的位置,修改grpc源码:

1)将使用rand()的位置改为固定的之值,比如1(任意随机的整数值)

2)将使用stderr的地方注释掉,不影响功能

3)将strtof修改为strtod或使用低版本的ndk

之后重新编译,重新在Qt中加载使用,比较幸运,构建成功了~

4. 运行错误:Protocol Buffer版本不匹配

This program requires version 3.9.0 of the Protocol Buffer runtime library, but the installed version is 3.8.0. Please update your library. If you compiled the program yourself, make sure that your headers are from the same version of Protocol Buffers as your link-time library.

96659bc57121603acc96cfa41679ccde.png

8b0a98f82dafd85331a96ff2b084edd9.png

分析:我在Ubuntu中使用ndk编译grpc使用的protobuf版本为3.8.0,而我在qt for android中使用的protoc命令产生的*.pb.cc是3.9.0版本生成的。所以,希望*.pb.cc也由3.8.0版本的protobuf编译器来生成。

编译生成protobuf编译器:

$ ./grpc/third_party/protobuf

$./autogen.sh

$./configure

$ make    // 耗时较长

$ [sudo] make install

执行protoc –version时出错:

protoc: error while loading shared libraries: libprotoc.so.19: cannot open shared object file: No such file or directory

da7a00e6d0b0ab96d277eb6bb528ac07.png

该文章指出:protobuf的默认安装路径是/usr/local/lib,而/usr/local/lib 不在Ubuntu体系默认的 LD_LIBRARY_PATH 里,所以就找不到该lib

解决方法:

1.在/etc/ld.so.conf.d目录中创建文件libprotobuf.conf 写入内容:/usr/local/lib

2.输入命令重置内容生效:sudo ldconfig

这时,再运行protoc –version 就可以正常看到版本号了

51c2b4aab07b5fd30bcafc385bdf3834.png

在grpc目录下执行:

$ make

报错了:zconf.h找不到

412f0a9abce3e5f3821c40866ab3bf04.png

解决方法:sudo apt-get install zlib1g-dev

再次make, 编译漫长,耐心等待…

$ sudo make install

接着编译自带cpp例子helloworld:

554866036c7e025c9872f9bb6a2f6176.png

将生成的helloworld.pb.h/cpp以及helloworld.grpc.pb.h/cpp拷贝在qt工程中使用,而且包含头文件也用同一套的,否则会出现“#error This file was generated by an older version of protoc which is ^”的错误:

6702e87d252328bacc4e4a5fe3fc3f08.png

这下构建成功后,运行,不会在运行时报protobuf版本不对导致崩溃了~

5. Qt工程pro文件设置

增加:DEFINES += _WIN32_WINNT=0x0600

增加包含路径和链接静态库路径:

293e1203097033c58df2941b920776d2.png

INCLUDEPATH += $$PWD/../../include

DEPENDPATH += $$PWD/../../include

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/ -lgrpc++

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/libgrpc++.a

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/ -lgrpc

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/libgrpc.a

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/ -lgpr

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/libgpr.a

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/ -laddress_sorting

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/libaddress_sorting.a

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/third_party/protobuf/ -lprotobuf

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/third_party/protobuf/libprotobuf.a

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/third_party/zlib/ -lz

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/third_party/zlib/libz.a

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/third_party/boringssl/ssl/ -lssl

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/third_party/boringssl/ssl/libssl.a

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/third_party/cares/cares/lib/ -lcares

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/third_party/cares/cares/lib/libcares.a

注意:其中include路径包含grpc的头文件以及protobuf的./src/google中的文件

6. Qt测试工程部分代码

Helloworld.proto

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

syntax = "proto3";

option java_multiple_files =true;

option java_package ="io.grpc.examples.helloworld";

option java_outer_classname ="HelloWorldProto";

option objc_class_prefix ="HLW";

package helloworld;// The greeting service definition.service Greeter {// Sends a greetingrpc SayHello (HelloRequest) returns (HelloReply) {}

}// The request message containing the user's name.message HelloRequest {

string name =1;floatscore =2;

}// The response message containing the greetingsmessage HelloReply {

string message =1;floatscore =2;

}

服务端QtgRPC-Server

main.cpp

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

#include#include#include#include#include#include"helloworld.pb.h"#include"helloworld.grpc.pb.h"usinggrpc::Server;usinggrpc::ServerBuilder;usinggrpc::ServerContext;usinggrpc::Status;usinghelloworld::HelloRequest;usinghelloworld::HelloReply;usinghelloworld::Greeter;// Logic and data behind the server's behavior.classGreeterServiceImpl final :publicGreeter::Service {

Status SayHello(ServerContext* context,constHelloRequest* request,

HelloReply* reply) override {

std::string prefix("Hello ");

reply->set_message(prefix + request->name());

reply->set_score(request->score());returnStatus::OK;

}

};voidRunServer() {

std::cout <

std::string server_address("0.0.0.0:50051");

GreeterServiceImpl service;

ServerBuilder builder;// Listen on the given address without any authentication mechanism.builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());// Register "service" as the instance through which we'll communicate with// clients. In this case it corresponds to an *synchronous* service.builder.RegisterService(&service);// Finally assemble the server.std::unique_ptr server(builder.BuildAndStart());

std::cout <Wait();

}intmain(intargc,char*argv[])

{

std::cout <

}

客户端grpc-client

Widget.h

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

#ifndefWIDGET_H#defineWIDGET_H#include#include#include#include#include#include#include"helloworld.grpc.pb.h"usinggrpc::Channel;usinggrpc::ClientContext;usinggrpc::Status;usinghelloworld::HelloRequest;usinghelloworld::HelloReply;usinghelloworld::Greeter;classGreeterClient {public:

GreeterClient(std::shared_ptr channel)

: stub_(Greeter::NewStub(channel)) {}// Assembles the client's payload, sends it and presents the response back// from the server.std::string SayHello(conststd::string& user) {// Data we are sending to the server.HelloRequest request;

request.set_name(user);// Container for the data we expect from the server.HelloReply reply;// Context for the client. It could be used to convey extra information to// the server and/or tweak certain RPC behaviors.ClientContext context;// The actual RPC.Status status = stub_->SayHello(&context, request, &reply);// Act upon its status.if(status.ok()) {returnreply.message();

}else{

std::cout <

<

}

}floatSayScore(constfloatscore = std::numeric_limits::max()) {// Data we are sending to the server.HelloRequest request;

request.set_score(score);// Container for the data we expect from the server.HelloReply reply;// Context for the client. It could be used to convey extra information to// the server and/or tweak certain RPC behaviors.ClientContext context;// The actual RPC.Status status = stub_->SayHello(&context, request, &reply);// Act upon its status.if(status.ok()) {returnreply.score();

}else{

std::cout <

<

}

}private:

std::unique_ptr<:stub> stub_;

};classQTextBrowser;classWidget :publicQWidget

{

Q_OBJECTpublic:

Widget(QWidget *parent = nullptr);

~Widget();privateslots:voidreq();private:

QTextBrowser *text;

};

#endif // WIDGET_H

Widget.cpp

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

#include"Widget.h"#include#include#include

Widget::Widget(QWidget *parent)

: QWidget(parent)

, text(newQTextBrowser())

{

setWindowTitle(tr("grpc-client"));

resize(320,460);

QVBoxLayout *layout =newQVBoxLayout(this);

QPushButton *button =newQPushButton("req");

connect(button, &QPushButton::clicked,this, &Widget::req);

layout->addWidget(text);

layout->addWidget(button);

setLayout(layout);

}

Widget::~Widget()

{

}voidWidget::req()

{// Instantiate the client. It requires a channel, out of which the actual RPCs// are created. This channel models a connection to an endpoint (in this case,// localhost at port 50051). We indicate that the channel isn't authenticated// (use of InsecureChannelCredentials()).GreeterClient greeter(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));

std::string user("world");

std::string reply = greeter.SayHello(user);floatreplyf= greeter.SayScore();

std::cout <

std::cout <

text->append(QString("Greeter received: %1").arg(reply.c_str()));

text->append(QString("Greeter received: %1").arg(replyf));

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于在Qt6中安装和使用gRPC,你需要执行以下步骤: 1. 首先,确保你已经在系统上安装了gRPC。你可以通过以下命令来安装gRPC: ``` $ sudo apt-get install -y build-essential autoconf libtool pkg-config $ sudo apt-get install -y libgflags-dev libgtest-dev $ sudo apt-get install -y clang libc++-dev $ git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc $ cd grpc $ mkdir -p cmake/build $ cd cmake/build $ cmake -DgRPC_INSTALL=ON -DgRPC_BUILD_TESTS=OFF ../.. $ make $ sudo make install ``` 2. 然后,在你的Qt项目中添加gRPC的依赖。你可以通过将以下内容添加到项目的.pro文件中来完成: ``` CONFIG += c++17 LIBS += -lgrpc++ -lgrpc -lgrpc++_reflection -lprotobuf ``` 这会告诉Qt构建系统在编译和链接时使用gRPC库。 3. 在你的Qt代码中引入所需的头文件并使用gRPC。例如,以下是一个简单的gRPC客户端的示例: ```cpp #include <grpcpp/grpcpp.h> #include "your_service.grpc.pb.h" int main(int argc, char** argv) { // 创建gRPC通道 std::shared_ptr<grpc::Channel> channel = grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()); // 创建服务客户端 YourService::Stub stub(channel); // 构建请求 YourRequest request; // 设置请求参数... // 发起RPC调用 grpc::ClientContext context; YourResponse response; grpc::Status status = stub.YourRPCMethod(&context, request, &response); if (status.ok()) { // 处理响应... } else { // 处理错误... } return 0; } ``` 注意替换`your_service.grpc.pb.h`和相关的gRPC服务和消息文件。 4. 构建和运行你的Qt项目。确保在构建时指定正确的编译器标志和库路径。你可以使用`qmake`和`make`命令来构建项目,或者使用Qt Creator进行集成开发环境。 这样,你就可以在Qt6中安装和使用gRPC了。请注意,这只是一个简单的示例,实际情况可能会有所不同,具体取决于你的项目需求和gRPC服务的定义。详细的gRPC文档提供了更多关于如何使用gRPC的信息,你可以参考它来进一步了解和定制化你的应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值