Communication
NDN applications fetch data using Interest-Data packet exchanges. The process of such an exchange can be summarized as follows:
- An application that desires a piece of data sends an Interest packet. The Interest packet contains the name of the data being requested.
- The network forwards the Interest packet to the node(s) having a copy of the data.
- On receiving the Interest packet, a node may reply with a matching Data packet.
- The network the forwards this Data packet back to the requesting application.
Types of Applications
Applications that send Interests to fetch data packets are called "consumers". Likewise, applications that serve Data in response to Interests are called "producers". An application may act as both a consumer and a producer simultaneously.
Consumer
Consumers are applications that send Interest packets to fetch data. The Interest packet contains the name of the data being requested along with any optional selectors. The following snippet illustrates how to send an Interest packet and get back the corresponding Data.
For this example to work, you must first run the producer from the next part.
#include <iostream>
#include <ndn-cxx/face.hpp>
int main(int argc, char** argv)
{
// Create an Interest packet
ndn::Interest interest("/edu/ucla/cs/118/notes");
// Connect to the local forwarder over a Unix socket
ndn::Face face;
// Send the Interest packet and get back the Data packet
face.expressInterest(interest,
[](const ndn::Interest&, const ndn::Data& data) {
// Received a Data packet reply
std::cout << data << std::endl;
},
[](const ndn::Interest&, const ndn::lp::Nack& nack) {
// Received a Nack (negative acknowledgement)
std::cout << "Nack received: " << nack.getReason() << std::endl;
},
[](const ndn::Interest&) {
// The Interest has timed out
std::cout << "Timeout" << std::endl;
});
// Start face processing loop
face.processEvents();
}
from ndn.app import NDNApp
from ndn.encoding import Name
# Connect to the local forwarder over a Unix socket
app = NDNApp()
async def main():
try:
data_name, meta_info, content = await app.express_interest("/edu/ucla/cs/118/notes")
# Received a Data packet
print(f'Received Data Name: {Name.to_str(data_name)}')
print(meta_info)
print(bytes(content) if content else None)
except InterestNack as e:
# Received a Nack (negative acknowledgement)
print(f'Nacked with reason={e.reason}')
except InterestTimeout:
# The Interest has timed out
print(f'Timeout')
except InterestCanceled:
# Connection to the local NDN forwarder is broken
print(f'Canceled')
except ValidationFailure:
# Security validation failed for the data
print(f'Data failed to validate')
finally:
# Disconnect from the local forwarder
app.shutdown()
if __name__ == '__main__':
app.run_forever(after_start=main())
import { Interest } from '@ndn/packet';
import { WsTransport } from '@ndn/ws-transport';
import { consume } from '@ndn/endpoint';
// Code running in the browser cannot connect to a local Unix socket.
// In this example, we connect to a remote NFD instance, running as
// a part of the global NDN testbed.
const uplink = await WsTransport.createFace({}, "wss://suns.cs.ucla.edu/ws/");
console.log(`Connected to NFD at ${uplink.remoteAddress}`);
// Create an Interest packet
const interest = new Interest(`/ndn/edu/arizona/ping/NDNts/${Date.now()}`);
// Send the Interest packet and wait for the Data packet
try {
const data = await consume(interest);
console.log(`Received data with name [${data.name}]`);
} catch (err: any) {
console.warn(err);
}
// Disconnect from the remote NFD instance
uplink.close();
Producer
To serve data to other applications, a producer must register a name prefix with the network.
- The producer sends a registration request to the network carrying a name prefix.
- A route to the prefix is registered at the local forwarder, and may be propagated to other forwarders in the network.
- The producer is notified of the successful registration.
- Any Interest packets matching the prefix may now be forwarded to the producer.
The following snippet illustrates how to serve data by registering a name prefix route at the local forwarder. Make sure to start NFD on your development machine before running this example.
#include <iostream>
#include <ndn-cxx/face.hpp>
#include <ndn-cxx/security/key-chain.hpp>
int main(int argc, char** argv)
{
// Connect to the local forwarder over a Unix socket
ndn::Face face;
// Connect to the local KeyChain to sign Data packets
// Note: Security is not optional in NDN
ndn::KeyChain keychain;
// Register a prefix with the local forwarder
face.setInterestFilter("/edu/ucla/cs/118/notes",
[&face, &keychain](const ndn::InterestFilter&, const ndn::Interest& interest) {
std::cout << "Received Interest packet for " << interest.getName() << std::endl;
// Create a Data packet with the same name as the Interest
ndn::Data data(interest.getName());
// Set the Data packet's content to "Hello, World!"
data.setContent(ndn::make_span(reinterpret_cast<const uint8_t*>("Hello, NDN!"), 11));
// Sign the Data packet with default identity
keychain.sign(data);
// Return the Data packet to the network
face.put(data);
},
// Register prefix failure handler -- optional
nullptr,
// Register prefix failure handler
[](const ndn::Name& prefix, const std::string& reason) {
std::cout << "Route registration failed" << std::endl;
});
// Start face processing loop
face.processEvents();
}
from typing import Optional
from ndn.app import NDNApp
from ndn.encoding import Name, InterestParam, BinaryStr, FormalName
# Connect to the local forwarder over a Unix socket
app = NDNApp()
# Register a prefix, and call on_interest when a matching Interest is received
@app.route('/edu/ucla/cs/118/notes')
def on_interest(name: FormalName, param: InterestParam, _app_param: Optional[BinaryStr]):
print(f'Received Interest packet for {Name.to_str(name)}')
# Create the content bytes for the Data packet
content = "Hello, NDN!".encode()
# Sign and send the Data packet back to the network
app.put_data(name, content=content, freshness_period=10000)
if __name__ == '__main__':
app.run_forever()
import { Data, digestSigning } from '@ndn/packet';
import { WsTransport } from '@ndn/ws-transport';
import { produce } from '@ndn/endpoint';
import { toUtf8 } from '@ndn/util';
// Code running in the browser cannot connect to a local Unix socket.
// In this example, we connect to a remote NFD instance, running as
// a part of the global NDN testbed.
const uplink = await WsTransport.createFace({}, "wss://suns.cs.ucla.edu/ws/");
console.log(`Connected to NFD at ${uplink.remoteAddress}`);
// Start one producer
const myProducer = produce('/edu/ucla/cs/118/notes', async (interest) => {
console.log(`Received Interest packet for ${interest.name.toString()}`);
// Create the content bytes for the Data packet
const content = toUtf8("Hello, NDN!");
// Sign and send the Data packet back to the network
const data = new Data(interest.name, Data.FreshnessPeriod(10000), content);
await digestSigning.sign(data);
return data;
});
Faces
Under Construction