Skip to content

Commit

Permalink
doc: add an example to illustrate the usage of the dhtnet API for bot…
Browse files Browse the repository at this point in the history
…h server-side and client-side.

Change-Id: I57c29dcf0a0fc54dcd299edd9db1a6d2b363bcf4
  • Loading branch information
AmnaSnene committed Sep 24, 2024
1 parent c1dc8a7 commit 125f5b3
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 53 deletions.
14 changes: 14 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ option(BUILD_DEPENDENCIES "Build dependencies" ON)
option(DNC_SYSTEMD_UNIT_FILE_LOCATION "Where to install systemd unit file")
option(DNC_SYSTEMD "Enable dnc systemd integration" ON)
option(CODE_COVERAGE "Enable coverage reporting" OFF)
option(BUILD_EXAMPLE "Build example" ON)

# Check if testing is enabled
if(BUILD_TESTING)
Expand Down Expand Up @@ -426,4 +427,17 @@ if (BUILD_TESTING AND NOT MSVC)
#target_link_libraries(tests_stringutils PRIVATE dhtnet fmt::fmt PkgConfig::Cppunit)
#add_test(NAME tests_stringutils COMMAND tests_stringutils)

endif()
if (BUILD_EXAMPLES AND NOT MSVC)
add_executable(server
example/server.cpp)
target_link_libraries(server PRIVATE dhtnet)
target_include_directories(server PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/example)
install(TARGETS server RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

add_executable(client
example/client.cpp)
target_link_libraries(client PRIVATE dhtnet)
target_include_directories(client PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/example)
install(TARGETS client RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
57 changes: 4 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# DHTNet - Lightweight Peer-to-Peer Communication Library

![DHTNet Logo](your-logo.png)
![DHTNet Logo]()

DHTNet is a C++17 library designed to serve as a network overlay that provides an IP network abstraction. Its main objective is to establish secure peer-to-peer connections using public-key authentication.

Expand Down Expand Up @@ -31,58 +31,9 @@ Get started with DHTNet by building and installing the library:
- [Build and Install Instructions](BUILD.md)

## Usage Example

```cpp
#include "connectionmanager.h"
#include <opendht/log.h>
#include <opendht/utils.h>
#include <opendht/thread_pool.h>
#include <fmt/core.h>

int main() {
// Create identities for CA (Certificate Authority), client, and server
auto ca = dht::crypto::generateIdentity("ca");
auto id_client = dht::crypto::generateIdentity("client", ca);
auto id_server = dht::crypto::generateIdentity("server", ca);

// Create client and server ConnectionManager instances
auto client = std::make_shared<ConnectionManager>(id_client);
auto server = std::make_shared<ConnectionManager>(id_server);

// Launch dht nodes
client->onDhtConnected(id_client.first->getPublicKey());
server->onDhtConnected(id_server.first->getPublicKey());

// Connect the client to the server's device via a channel named "channelName"
client->connectDevice(id_server.second->getId(), "channelName", [&](std::shared_ptr<dhtnet::ChannelSocket> socket,
const dht::InfoHash&) {
if (socket) {
// Send a message (example: "Hello") to the server
std::error_code ec;
std::string data = "hello";
socket->write(data.data(), data.size(), ec);
}
});

// Define a callback function for when the server's connection is ready
server->onConnectionReady([&](const DeviceId& device, const std::string& name, std::shared_ptr<ChannelSocket> socket) {
if (socket) {
// Server: Connection succeeded
fmt::print("Server: Connection succeeded\n");

// Set a callback for receiving messages
socket->setOnRecv([&](const uint8_t* data, size_t size) {
fmt::print("Message received: {}\n", std::string_view(data, data + size)); // Print received message
});
} else {
// Server: Connection failed
fmt::print("Server: Connection failed\n");
}
});

return 0;
}
```
In the example repository, there is a client-server application where the client connects to the server and sends a "hello" message.
You can build the example using the project's [Build and Install Instructions](BUILD.md) with `-BUILS_EXAMPLE=ON`.
![Demo](example/client-server_dhtnet.png)

## Dependencies

Expand Down
Binary file added example/client-server_dhtnet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
74 changes: 74 additions & 0 deletions example/client.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include "certstore.h"
#include "connectionmanager.h"
#include "fileutils.h"

#include <opendht/crypto.h>

#include <string>
#include <vector>

namespace dhtnet {
void
client(dht::crypto::Identity id_client, dht::InfoHash id_server)
{
fmt::print("Start client\n");
fmt::print("Client identity: {}\n", id_client.second->getId());

// Create client ConnectionManager instance
auto client = std::make_shared<ConnectionManager>(id_client);

// Launch dht node
client->onDhtConnected(id_client.first->getPublicKey());

// Connect the client to the server's device via a channel named "channelName"
client->connectDevice(id_server,
"channelName",
[&](std::shared_ptr<ChannelSocket> socket, const dht::InfoHash&) {
fmt::print("Client: Sending request\n");
if (socket) {
// Send a message (example: "Hello") to the server
std::error_code ec;
std::string msg = "hello";
fmt::print("Client: Sending message: {}\n", msg);
std::vector<unsigned char> data(msg.begin(), msg.end());

socket->write(data.data(), data.size(), ec);
// For continuous data transmission, refer to the readFromPipe
// function in tools/common.cpp
if (ec) {
fmt::print("Client: Error writing to socket: {}\n",
ec.message());
} else {
fmt::print("Client: Message sent\n");
}
} else {
fmt::print("Client: Connection failed\n");
return;
}
});

// keep the client running
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
} // namespace dhtnet

int
main(int argc, char** argv)
{
// Set the log level to 0 to avoids pj logs
pj_log_set_level(0);

// This is the root certificate that will be used to sign other certificates
auto ca = dht::crypto::generateIdentity("ca_client");

auto id_client = dht::crypto::generateIdentity("client", ca);

auto id_server = dht::InfoHash(argv[1]);

dhtnet::client(id_client, id_server);


return 0;
}
85 changes: 85 additions & 0 deletions example/server.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@

#include "connectionmanager.h"
#include "fileutils.h"

#include <opendht/log.h>
#include <opendht/crypto.h>

#include <fcntl.h>
#include <unistd.h>

#include <string>
#include <string_view>
namespace dhtnet {
void
server(dht::crypto::Identity id_server)
{
fmt::print("Server identity: {}\n", id_server.second->getId());
// Create an instance of ConnectionManager for the server
auto server = std::make_shared<ConnectionManager>(id_server);

fmt::print("Start server\n");
// Start the DHT node for the server
server->onDhtConnected(id_server.first->getPublicKey());

// Handle ICE connection requests from devices
// This callback is triggered when a device requests an ICE connection.
// The callback should decide whether to accept or decline the request.
server->onICERequest([id_server](const DeviceId& device) {
// Optional: Add logic to validate the device's certificate
// Example: Check if the device's certificate is signed by a trusted authority
// In this example, all devices are allowed to connect
fmt::print("Server: ICE request received from {}\n", device.toString());
return true;
});

// Handle requests for establishing a communication channel
// The callback checks if the channel should be opened based on the name or device's certificate.
server->onChannelRequest(
[&](const std::shared_ptr<dht::crypto::Certificate>& cert, const std::string& name) {
// Optional: Add logic to validate the channel name or certificate
// Example: Allow the connection if the channel name is "channelName"
fmt::print("Server: Channel request received from {}\n", cert->getLongId());
return name == "channelName";
});

// Define a callback when the connection is established
server->onConnectionReady([&](const DeviceId& device,
const std::string& name,
std::shared_ptr<ChannelSocket> socket) {
if (socket) {
fmt::print("Server: Connection succeeded\n");
// Set up a callback to handle incoming messages on this connection
socket->setOnRecv([socket](const uint8_t* data, size_t size) {
fmt::print("Server: Received message: {}\n", std::string((const char*) data, size));
return size;
});
} else {
// The connection failed
fmt::print("Server: Connection failed\n");
}
});

// Keep the server running indefinitely
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}

} // namespace dhtnet

int
main()
{
// Set the log level to 0 to avoids pj logs
pj_log_set_level(0);

// This is the root certificate that will be used to sign other certificates
auto ca = dht::crypto::generateIdentity("ca");

auto id_server = dht::crypto::generateIdentity("server", ca);

dhtnet::server(id_server);

return 0;
}

0 comments on commit 125f5b3

Please sign in to comment.