以太坊学习(8)编写C++程序与以太坊节点进行交互【2】

编写C++程序与节点进行交互


【1】简单版本,需对http request有一定了解,以及QT的基本操作 

【2】进行类的封装,个人水平有限,如有纰漏,请下方留言

 


测试环境:

  • debian 9
  • QT 5.5
  • geth/v1.8.14
  • C++大数bigint library下载: https://pan.baidu.com/s/1bc_d81J2lsqs4bRKa9rNyA  提取码: 5f7m 

在编写C++程序与以太坊节点进行交互【1】中,我们用QT的Network类实现了通过post 到以太坊的JSON-RPC接口与节点进行交互。显然,大量的这些代码在mainwindow.cpp中出现并不利于代码的编写和阅读。

本文将一步步的将这些代码封装起来,便于调用。

一、新建一个类rpc_json_http

(1)在项目中,右键,添加文件,选择C++ Class,基类选择QObject(因为用到了信号和槽)

以太坊学习(8)编写C++程序与以太坊节点进行交互【2】_第1张图片

(2)编辑rpc_json_http.h

(为了方便对比,这里没有用代码编辑器)

#ifndef RPC_JSON_HTTP_H
#define RPC_JSON_HTTP_H

#include
#include
#include
#include

class RPC_JSON_Http : public QObject
{
    Q_OBJECT
public:
    explicit RPC_JSON_Http(QObject *parent = 0);
    ~RPC_JSON_Http();
    void init();//初始化网络管理接口QNetworkAccessManager
    void get(const QString url);//get方法的封装
    void post(const QUrl url, const QByteArray &data);//post方法的封装
private:
    QNetworkAccessManager *manager; //声明网络管理接口QNetworkAccessManager
    QNetworkRequest httpRequest;

protected:
    //纯虚函数,在finishedSlot槽函数中调用
    //具体实现在子类

    virtual void requestFinished(QNetworkReply *reply, const QByteArray data, const int statusCode) = 0;
signals:
    
public slots:
private slots:
    void finishedSlot(QNetworkReply *reply);//槽函数
};

#endif // RPC_JSON_HTTP_H
 

 代码编辑器的:

#ifndef RPC_JSON_HTTP_H
#define RPC_JSON_HTTP_H

#include 
#include
#include
#include
class RPC_JSON_Http : public QObject
{
    Q_OBJECT
public:
    explicit RPC_JSON_Http(QObject *parent = 0);
    ~RPC_JSON_Http();
    void init();//初始化网络管理接口QNetworkAccessManager
    void get(const QString url);//get方法的封装
    void post(const QUrl url, const QByteArray &data);//post方法的封装
private:
    QNetworkAccessManager *manager; //声明网络管理接口QNetworkAccessManager
    QNetworkRequest httpRequest;
protected:
    //纯虚函数,在finishedSlot槽函数中调用
    //具体实现在子类
    virtual void requestFinished(QNetworkReply *reply, const QByteArray data, const int statusCode) = 0;
signals:
    
public slots:
private slots:
    void finishedSlot(QNetworkReply *reply);//槽函数
};

#endif // RPC_JSON_HTTP_H

(3)编辑 rpc_json_http.cpp

#include "rpc_json_http.h"

RPC_JSON_Http::RPC_JSON_Http(QObject *parent) : QObject(parent)
{
    init();//初始化网络管理接口
}
RPC_JSON_Http::~RPC_JSON_Http()
{
    manager->disconnect();//关闭
    manager->deleteLater();
}

void RPC_JSON_Http::init()
{	
	//实例化网络管理器
    manager=new QNetworkAccessManager(this);
    //Header设置
    httpRequest.setRawHeader("Accept", "application/json");
    httpRequest.setRawHeader("Content-Type", "application/json");
    //信号建立,收到返回数据后,调用槽函数
    QObject::connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(finishedSlot(QNetworkReply*)));
}

void RPC_JSON_Http::get(const QString url)
{
    //根据参数url调用get方法
    httpRequest.setUrl(QUrl(url));
    manager->get(httpRequest);
}

void RPC_JSON_Http::post(const QUrl url, const QByteArray &data)
{
    //根据参数url、data,调用post方法
    httpRequest.setUrl(url);
    manager->post(httpRequest,data);
}
void RPC_JSON_Http::finishedSlot(QNetworkReply *reply)
{
    //获取返回状态
    // Reading attributes of the reply
    QVariant statusCodeV=reply->attribute(
                QNetworkRequest::HttpStatusCodeAttribute);
    // Or the target URL if it was a redirect:
    QVariant redirectionTargetUrl=reply->attribute(
                QNetworkRequest::RedirectionTargetAttribute);
    if(reply->error()==QNetworkReply::NoError){
		//调用纯虚函数,将post\get后得到的返回数据、状态作为参数传入
        requestFinished(reply, reply->readAll(), statusCodeV.toInt());
        //纯虚函数在子类中实现
    }else {
		//错误则输出
        requestFinished(reply, "something worng", statusCodeV.toInt());
        qDebug()<<"something worng:"<

(4)到这里,这个rpc_json_http类就到这里,实际上就是对QNetwork的简单封装,也是作为我们实现C++以太坊接口的基类,

这个rpc_json_http有4个方法:

  • init():初始化
  • get():get方法
  • post():post方法
  • finishedSlot():槽函数,收到返回的数据后会自动调用,然后调用子类实现的纯虚函数

 

二、 新建一个子类http_for_ethereum,这个类也就是我们最终与节点进行交互的类

(1)在项目中,右键,添加文件,选择C++ Class,基类选择自定义custom,勾上include

以太坊学习(8)编写C++程序与以太坊节点进行交互【2】_第2张图片

(2)编辑http_for_ethereum.h,这是rpc_json_http的子类,因此要设置父类rpc_json_http

这里引入了bigInt库,用来处理获取账户余额数字过大的问题

#ifndef HTTP_FOR_ETHEREUM_H
#define HTTP_FOR_ETHEREUM_H

#include"rpc_json_http.h"
#include
#include
#include
#include

#include
class http_for_ethereum : public RPC_JSON_Http
{
    Q_OBJECT
public:
    http_for_ethereum(QString address);
    //~http_for_ethereum();
    //获取节点账户
    void getAccount(int id,std::functioncallback);
    //获取节点账户回调处理
    QStringList getAccount_process(bool success,QByteArray data);
    
    //获取账户余额
    void getBalance(int id,QString address,std::functioncallback);
    //获取账户余额回调处理
    std::string getBalance_process(bool success,QByteArray data);
private:
    //节点地址
    QUrl node_address;
    //回调函数声明
    std::functioncheckCallback;
protected:
    //父类声明的纯虚函数
    void requestFinished(QNetworkReply* reply, const QByteArray data, const int statusCode);
    
};

#endif // HTTP_FOR_ETHEREUM_H
 

#ifndef HTTP_FOR_ETHEREUM_H
#define HTTP_FOR_ETHEREUM_H

#include"rpc_json_http.h"
#include
#include
#include
#include
#include
class http_for_ethereum : public RPC_JSON_Http
{
    Q_OBJECT
public:
    http_for_ethereum(QString address);
    //~http_for_ethereum();
    //获取节点账户
    void getAccount(int id,std::functioncallback);
    //获取节点账户回调处理
    QStringList getAccount_process(bool success,QByteArray data);
    
    //获取账户余额
    void getBalance(int id,QString address,std::functioncallback);
    //获取账户余额回调处理
    std::string getBalance_process(bool success,QByteArray data);
private:
    //节点地址
    QUrl node_address;
    //回调函数声明
    std::functioncheckCallback;
protected:
    //父类声明的纯虚函数
    void requestFinished(QNetworkReply* reply, const QByteArray data, const int statusCode);
    
};

#endif // HTTP_FOR_ETHEREUM_H

(3)编辑http_for_ethereum.cpp

#include "http_for_ethereum.h"

#include "qdebug.h"
#include
#include 
#include 
#include 
#include
http_for_ethereum::http_for_ethereum(QString address)
{
    //构造函数
    //实例化时传入节点地址
    node_address=QUrl(address);
    //初始化网路管理接口
    RPC_JSON_Http::init();
}
//纯虚函数的具体实现
void http_for_ethereum::requestFinished(QNetworkReply *reply, const QByteArray data, const int statusCode)
{
    //判断post\get的状态
    if (statusCode == 200) {
        //调用回调函数,post、get返回的数据作为参数
        //这个回调函数就是对不同接口进行返回数据处理
        this->checkCallback(true, data);
        return;
    }else{
        this->checkCallback(false,data);
    }
}
//获取节点账户具体实现
void http_for_ethereum::getAccount(int id,std::functioncallback)
{
    //构建JSON格式
    QByteArray data_send;
    QJsonObject json_in;
    QJsonDocument docum;
    json_in.insert("jsonrpc","2.0");
    json_in.insert("method","eth_accounts");
    json_in.insert("id",id);
    docum.setObject(json_in);
    //JSON转QByteArray类型
    data_send=docum.toJson(QJsonDocument::Compact);
    //回调函数指定,这里回调函数作为参数传入
    this->checkCallback=callback;
    //post
    RPC_JSON_Http::post(node_address,data_send);
}

//获取节点账户回调处理,这个函数在回调中调用
QStringList http_for_ethereum::getAccount_process(bool success, QByteArray data)
{
    //若post\get成功
    if(success){
        //将post\get返回的数据转换为QString类型
        QString temp;
        temp.prepend(data);
        qDebug()< please check the string "<< temp.toLocal8Bit().data();
        }else{
            //利用QStringList类型储存
            QStringList result;
            QJsonObject jsonObject = jsonDocument.object();
            if(jsonObject.value("result").toArray().size()>0){
                for(int i=0;i callback)
{
    //构造json
    QByteArray data_send;
    QJsonObject json_in;
    QJsonDocument docum;
    QJsonArray params;
    //传入要获取余额的账户
    params<checkCallback=callback;
    //post
    RPC_JSON_Http::post(node_address,data_send);
}
//获取账户余额回调处理,这个函数在回调中调用
std::string http_for_ethereum::getBalance_process(bool success, QByteArray data)
{
    //若post\get成功
    if(success){
        QString temp;
        temp.prepend(data);
        qDebug()< please check the string "<< temp.toLocal8Bit().data();
        }else{
            QJsonObject jsonObject = jsonDocument.object();
            std::string tmp;
            if(jsonObject.value("result").toArray().size()>=0){
                qDebug() <

(4)到这里,获取节点账户、获取账户余额这两个接口就封装好了。接下来看调用

1)主窗体设计如下:

以太坊学习(8)编写C++程序与以太坊节点进行交互【2】_第3张图片

同样的,在按钮中右键,转到槽clicked()

2)编辑mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include
#include"rpc_json_http.h"
#include"http_for_ethereum.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    //声明接口
    http_for_ethereum *eth;
    //定义accounts,储存节点账户
    QStringList accounts;
private slots:
    void on_pushButton_clicked();
private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H
 

 3)编辑mainwindow.cpp

1、实现获取账户

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include
#include 
#include 
#include 
#include
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowTitle("Network request test");
    eth=new http_for_ethereum("http://127.0.0.1:8545");

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    //调用getAount,参数为:(id,回调函数)
    eth->getAccount(1,[&](bool success,QByteArray data){
        //在回调中调用数据处理函数
        accounts=eth->getAccount_process(success,data);
        qDebug()<comboBox->addItem(accounts[i]);
        }
    });
}

运行如下:

以太坊学习(8)编写C++程序与以太坊节点进行交互【2】_第4张图片

点击getAccount

以太坊学习(8)编写C++程序与以太坊节点进行交互【2】_第5张图片

2、实现选择对应账户,则账户下面的lineEdit显示账户余额

1)在combox右键,转到槽,选择currentIndexChanged(QString)

2)在槽函数实现如下:

void MainWindow::on_comboBox_currentIndexChanged(const QString &arg1)
{
    //获取余额
    eth->getBalance(1,ui->comboBox->currentText(),[&](bool success,QByteArray data){
        QString balance=QString::fromStdString(eth->getBalance_process(success,data));
        //更新ui
        ui->lineEdit2->setText(balance);
    });
}

实现效果:

以太坊学习(8)编写C++程序与以太坊节点进行交互【2】_第6张图片以太坊学习(8)编写C++程序与以太坊节点进行交互【2】_第7张图片

以太坊学习(8)编写C++程序与以太坊节点进行交互【2】_第8张图片

 

你可能感兴趣的:(区块链学习)