DavidKeller / kademlia

Dead simple C++11 kademlia distributed hash table library
http://dev.litchis.fr/projects/kademlia
70 stars 17 forks source link

There was an error when I ran the example. #7

Closed Zjek closed 3 years ago

Zjek commented 3 years ago

Hello!

There was an error when I ran two of these programs. Another program modifies the IP and port.

Compiling environment:QT5.12 widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<kademlia/session.hpp>
#include<future>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_searchButton_clicked();

    void on_saveButton_clicked();

private:
    Ui::Widget *ui;
kademlia::session *s;
std::future<std::error_code> *main_loop_result;
};
#endif // WIDGET_H

widget.cpp:

#include "widget.h"
#include "ui_widget.h"
#include<kademlia/session.hpp>
#include<future>
#include<QDebug>
#include<iostream>
#include<kademlia/error.hpp>
#include<QHostInfo>
#include<QFile>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    kademlia::endpoint const initial_peer{"192.168.1.4",34567};
    s = new kademlia::session {initial_peer};
    main_loop_result = new std::future<std::error_code>(std::async(std::launch::async,
                                       &kademlia::session::run,s));

    //qDebug()<<"type: "<<typeid (main_loop_result).name();
    ui->setupUi(this);
}

Widget::~Widget()
{
    s->abort();
    auto failure = main_loop_result->get();
    if(failure!=kademlia::RUN_ABORTED)
        qDebug()<<QString::fromStdString(failure.message());
    delete s;
    s=0;
    delete ui;
}

void Widget::on_searchButton_clicked()
{
    QByteArray bytes("key");
    std::vector<uint8_t> key(bytes.begin(),bytes.end());
    auto on_load = [](std::error_code const& failure
            ,kademlia::session::data_type const& data)
    {
        qDebug()<<"data: "<<data.data();
        if(failure)
            qDebug()<<QString::fromStdString(failure.message());
        else

            std::copy(data.begin(),data.end()
                      ,std::ostream_iterator<std::uint32_t>{std::cout," "});
    };

    s->async_load(key,on_load);
}

void Widget::on_saveButton_clicked()
{
    QFile file("D:/qt_code/222.txt");
    if(!file.open(QFile::ReadOnly)){
        qDebug()<<"open file error!";
        return;
    }
    QByteArray block;
    block = file.readAll();
    file.close();
    std::vector<uint8_t> txt(block.begin(),block.end());

    kademlia::session::data_type const data{txt.begin(),txt.end()};

    auto on_save = [](std::error_code const& failure){
        if(failure)
            qDebug()<<QString::fromStdString(failure.message());
    };
    QByteArray bytes("key");
    std::vector<uint8_t> key(bytes.begin(),bytes.end());
    s->async_save(key,data,on_save);
}

Thanks!

DavidKeller commented 3 years ago

Hi Zjek,

Coding advices

First of all, you should consider RAII when dealing with memory allocation. kademlia::session *s; -> std::unique_ptr<kademlia::session> s;

You don't have to allocate every attribute with a new, for instance, you can change the main_loop_result attribute type from std::future<std::error_code> *main_loop_result; -> std::future<std::error_code> main_loop_result; (dropped the pointer).

And write within your Widget constructor as follow:

main_loop_result = new std::future<std::error_code>(std::async(std::launch::async,
                                       &kademlia::session::run,s));

->

main_loop_result = std::async(std::launch::async, &kademlia::session::run, s.get());

Your error

Could you elaborate on the error. Is it a runtime error or a compilation error ? In both cases, would you paste the error text ?

Zjek commented 3 years ago

Hi @DavidKeller,

That was my mistake. I ran two programs. There was no problem with the first program.But there was an error in the second program.In the second program, I just modified the IP and port.

My code

#include "widget.h"
#include "ui_widget.h"
#include<kademlia/session.hpp>
#include<future>
#include<QDebug>
#include<iostream>
#include<kademlia/error.hpp>
#include<QHostInfo>
#include<QFile>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    kademlia::endpoint const initial_peer{"1.2.3.4", 27980};

/*I use a debugger to debug the code.There was an error when my second program got here.

The inferior stopped because it triggered an exception.
Stopped in thread 0 by:Exception at 0x7ff837823b29,code:0xe06d7363:c++exception,
flags=0x1(execution cannot be continued).*/

 ->  std::unique_ptr<kademlia::session> c (new kademlia::session{initial_peer});  

    s = std::move(c);
    main_loop_result = std::async(std::launch::async, &kademlia::session::run, s.get());
    //qDebug()<<"type: "<<typeid (main_loop_result).name();
    ui->setupUi(this);
}

Widget::~Widget()
{
    s->abort();
//    auto failure = main_loop_result->get();
//    if(failure!=kademlia::RUN_ABORTED)
//        qDebug()<<QString::fromStdString(failure.message());
    delete ui;
}

void Widget::on_searchButton_clicked()
{
    QByteArray bytes("key");
    std::vector<uint8_t> key(bytes.begin(),bytes.end());
    auto on_load = [](std::error_code const& failure
            ,kademlia::session::data_type const& data)
    {
        qDebug()<<"data: "<<data.data();
        if(failure)
            qDebug()<<QString::fromStdString(failure.message());
        else

            std::copy(data.begin(),data.end()
                      ,std::ostream_iterator<std::uint32_t>{std::cout," "});
    };

    s->async_load(key,on_load);
}

void Widget::on_saveButton_clicked()
{
    QFile file("D:/qt_code/222.txt");
    if(!file.open(QFile::ReadOnly)){
        qDebug()<<"open file error!";
        return;
    }
    QByteArray block;
    block = file.readAll();
    file.close();
    std::vector<uint8_t> txt(block.begin(),block.end());

    kademlia::session::data_type const data{txt.begin(),txt.end()};

    auto on_save = [](std::error_code const& failure){
        if(failure)
            qDebug()<<QString::fromStdString(failure.message());
    };
    QByteArray bytes("key");
    std::vector<uint8_t> key(bytes.begin(),bytes.end());
    s->async_save(key,data,on_save);
}

My error

abort() has been called (Press Retry to debug the application)

Question How can I print information about another discovered node.

DavidKeller commented 3 years ago

Hum this is where the project is lacking some documentation. I'll will fix that.

Your error

I believe the std::abort() is called because an exception is thrown from the initial peer discovery code as it can't be reached at 1.2.3.4:27980.

When a session is created, an initial_peer is required as first argument. This is the first neighbor node the current node will contact in order to discover other nodes.

To create the initial_peer node, a session object can't be used because it requires... an initial_peer. But the first_session (include/kademlia/first_session.hpp) class doesn't requires an initial_peer and should be used to create the first node. This first_session class is required to bootstrap the network. Once there is more than one initial_peer node in the network, the initial_peer node can be shut down.

So in your case, you should create a another application which instantiates a first_session. It can be a small console application or another full fledged Qt Application, or even a parameter of your current Qt Application to select between two behaviors.

Another point is that by default session & first_session listen for 0.0.0.0:27980 & [::]:27980. So if you run the bootstrap and the main application on the same host, you have to ensure ports or IPs are unique.

Answer to your question

You can't get information about discovered nodes from the API. In order to keep it dead simple, all the node discovery work is hidden. If that's a piece of information important to you, you may have a look at other C++ kademlia implementations on github. I've seen some very interesting projects.

But you only want to see the discovery for debugging purpose, can enable debug logging by compiling with KADEMLIA_ENABLE_DEBUG defined.

Zjek commented 3 years ago

Hello,@DavidKeller!

My mistake has not been solved. Can you give me a complete example?

My code

first_session

 kademlia::first_session first;
    auto result = std::async(std::launch::async,
                             &kademlia::first_session::run,
                             &first);
    first.abort();
    auto failure = result.get();
    if(failure.value() == kademlia::RUN_ABORTED)
    {
        qDebug()<<QString::fromStdString(failure.message());
    }

the first session

 namespace k = kademlia;
    k::endpoint const initial_peer{"192.168.1.33",6666};
    std::unique_ptr<k::session>c(new k::session(initial_peer));
    s=std::move(c);
    auto q = std::async(std::launch::async,&k::session::run,s.get());
    s->abort();
    auto e = q.get();
    if(e == kademlia::RUN_ABORTED)
    {
        qDebug()<<QString::fromStdString(e.message());
    }

the second session

 namespace k = kademlia;
    k::endpoint const initial_peer{"192.168.1.77",55556};
    std::unique_ptr<k::session>c(new k::session(initial_peer));
    s=std::move(c);
    auto q = std::async(std::launch::async,&k::session::run,s.get());
    s->abort();
    auto e = q.get();
    if(e == kademlia::RUN_ABORTED)
    {
        qDebug()<<QString::fromStdString(e.message());
    }

My error

I created a frist_session and two session .When I created the second session, the abort () was called;

DavidKeller commented 3 years ago

Your code starts the session processing loops and aborts them right away:

    auto q = std::async(std::launch::async,&k::session::run,s.get());
    // TODO: Wait on something here (like a signal, a key pressed, a timeout, whatever)
    s->abort();
    auto result = std::async(std::launch::async,
                             &kademlia::first_session::run,
                             &first);
    // Wait here as well
    first.abort();

You should wait for something between the start and the abort.

Zjek commented 3 years ago

@DavidKeller The bug was fixed. My question How does this library transfer files?Is it transmitted by multiple clients or end-to-end?How can I debug and print peers' ip or port.I added "#define KADEMLIA_ENABLE_DEBUG " at the top of the cpp file.But there is nothing to display.

.

Thanks!

DavidKeller commented 3 years ago

Hello Zjek, I've written a documentation for you here: https://kademlia-cpp.readthedocs.io/en/latest/?badge=master

This library won't be efficient to transfer files.

It allows to associate a value with a key. If you want to transfer files with the maxim throughput, you should store them on a host with a high bandwidth (like a server living in a datacenter), and use this library to save the stored file URL.

Other client will find the file URL using kademlia, but will perform the fetching using the protocol you selected (e.g. http, ftp, bittorrent) when you uploaded the file on the high bandwidth host.