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

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


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

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

 


测试环境:


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

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

一、新建一个类rpc_json_http

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

(2)编辑rpc_json_http.h

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

#ifndef RPC_JSON_HTTP_H
#define RPC_JSON_HTTP_H

#include <QObject>
#include<QtNetwork/QNetworkReply>
#include<QtNetwork/QNetworkRequest>
#include<QtNetwork/QNetworkAccessManager>

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 <QObject>
#include<QtNetwork/QNetworkReply>
#include<QtNetwork/QNetworkRequest>
#include<QtNetwork/QNetworkAccessManager>
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:"<<statusCodeV.toInt();
    }
}

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

这个rpc_json_http有4个方法:

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

 

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

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

(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<QList>
#include<QObject>
#include<functional>
#include<QStringList>

#include<bigint-10-2-src/bigInt.h>
class http_for_ethereum : public RPC_JSON_Http
{
    Q_OBJECT
public:
    http_for_ethereum(QString address);
    //~http_for_ethereum();
    //获取节点账户
    void getAccount(int id,std::function<void(bool,const QByteArray data)>callback);
    //获取节点账户回调处理
    QStringList getAccount_process(bool success,QByteArray data);
    
    //获取账户余额
    void getBalance(int id,QString address,std::function<void(bool,const QByteArray data)>callback);
    //获取账户余额回调处理
    std::string getBalance_process(bool success,QByteArray data);
private:
    //节点地址
    QUrl node_address;
    //回调函数声明
    std::function<void(bool,const QByteArray data)>checkCallback;
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<QList>
#include<QObject>
#include<functional>
#include<QStringList>
#include<bigint-10-2-src/bigInt.h>
class http_for_ethereum : public RPC_JSON_Http
{
    Q_OBJECT
public:
    http_for_ethereum(QString address);
    //~http_for_ethereum();
    //获取节点账户
    void getAccount(int id,std::function<void(bool,const QByteArray data)>callback);
    //获取节点账户回调处理
    QStringList getAccount_process(bool success,QByteArray data);
    
    //获取账户余额
    void getBalance(int id,QString address,std::function<void(bool,const QByteArray data)>callback);
    //获取账户余额回调处理
    std::string getBalance_process(bool success,QByteArray data);
private:
    //节点地址
    QUrl node_address;
    //回调函数声明
    std::function<void(bool,const QByteArray data)>checkCallback;
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<QList>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include<iostream>
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::function<void(bool,const QByteArray data)>callback)
{
    //构建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()<<temp;
        //QString转JSONObject
        QJsonDocument jsonDocument = QJsonDocument::fromJson(temp.toLocal8Bit().data());
        //判断转换结果
        if( jsonDocument.isNull() ){
            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<jsonObject.value("result").toArray().size();i++){
                    //从返回的JSON结果中提取result,即节点账户
                    //追加到QStringList
                    result.append(jsonObject.value("result").toArray()[i].toString());
                }
                //返回QStringList
                return result;
            }
        }
    }
}
//获取账户余额具体实现
void http_for_ethereum::getBalance(int id, QString address, std::function<void (bool, const QByteArray)> callback)
{
    //构造json
    QByteArray data_send;
    QJsonObject json_in;
    QJsonDocument docum;
    QJsonArray params;
    //传入要获取余额的账户
    params<<address;
    params<<"latest";
    json_in.insert("jsonrpc","2.0");
    json_in.insert("method","eth_getBalance");
    json_in.insert("params",params);
    json_in.insert("id",id);
    //JSON转qByteArray
    docum.setObject(json_in);
    data_send=docum.toJson(QJsonDocument::Compact);
    qDebug()<<data_send;
    //指定回调
    this->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()<<temp;
        //QString转JSONObject
        QJsonDocument jsonDocument = QJsonDocument::fromJson(temp.toLocal8Bit().data());
        //判断转换结果
        if( jsonDocument.isNull() ){
            qDebug()<< "===> please check the string "<< temp.toLocal8Bit().data();
        }else{
            QJsonObject jsonObject = jsonDocument.object();
            std::string tmp;
            if(jsonObject.value("result").toArray().size()>=0){
                qDebug() <<jsonObject.value("result").toString();
                //提取返回的数据,返回余额为16进制,例:"0x20c7b98d7a7304bf060"
                //这里.mid(2)截取了"0x"后面的部分,并转换为c++的 string类型
                tmp=jsonObject.value("result").toString().mid(2).toStdString();
                //转换hex到bigINT,这里用到了BigINt库,处理大数字(因为返回的余额单位为wei,数字很大)
                BigInt::Vin balance(tmp,16);
                std::cout<<balance.toStrDec()<<std::endl;
                //返回string类型
                return tmp;
            }
        }
    }
}

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

1)主窗体设计如下:

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

2)编辑mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#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<QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include<QStringList>
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()<<accounts;
        //将账户添加到combox列表中
        for(int i=0;i<accounts.size();i++){
            ui->comboBox->addItem(accounts[i]);
        }
    });
}

运行如下:

点击getAccount

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

实现效果:

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值