If you run Alice now, you'll see the following output:

[DEBUG] jsonrpc-server: Trying to bind listener on tcp://

That indicates that our JSON-RPC server is up and running. However, there's currently no client for us to connect to. That's where dnetview comes in. dnetview implements a JSON-RPC client that calls a single method: get_info().

To use it, let's return to our JsonRpcInterface and add the following method:

    async fn get_info(&self, id: Value, _params: Value) -> JsonResult {
        let resp = self.p2p.get_info().await;
        JsonResponse::new(resp, id).into()

And add it to handle_request():

        match req.method.as_str() {
            Some("ping") => self.pong(, req.params).await,
            Some("get_info") => self.get_info(, req.params).await,
            Some(_) | None => JsonError::new(ErrorCode::MethodNotFound, None,,

This calls the p2p function get_info() and passes the returned data into a JsonResponse.

Under the hood, this function triggers a hierarchy of get_info() calls which deliver info specific to a node, its inbound or outbound Session's, and the Channel's those Session's run.

Here's what happens:

    pub async fn get_info(&self) -> serde_json::Value {
        // Building ext_addr_vec string
        let mut ext_addr_vec = vec![];
        for ext_addr in &self.settings.external_addr {

            "external_addr": format!("{:?}", ext_addr_vec),
            "session_manual": self.session_manual().await.get_info().await,
            "session_inbound": self.session_inbound().await.get_info().await,
            "session_outbound": self.session_outbound().await.get_info().await,
            "state": self.state.lock().await.to_string(),

Here we return two pieces of info that are unique to a node: external_addr and state. We couple that data with SessionInfo by calling get_info() on each Session.

Session::get_info() returns data related to a Session (for example, an Inbound accept_addr in the case of an inbound Session). Session::get_info() then calls the function Channel::get_info() which returns data specific to a Channel. This happens via a child struct called ChannelInfo.

This is ChannelInfo::get_info().

    async fn get_info(&self) -> serde_json::Value {
        let log = match &self.log {
            Some(l) => {
                let mut lock = l.lock().await;
                let ret = lock.clone();
                *lock = Vec::new();
            None => vec![],

            "random_id": self.random_id,
            "remote_node_id": self.remote_node_id,
            "last_msg": self.last_msg,
            "last_status": self.last_status,
            "log": log,

dnetview uses the info returned from Channel and Session and node-specific info like external_addr to display an overview of the p2p network.