在libp2p-rs上开发新协议

前言:

本文以floodsub为例,讨论如何在libp2p-rs上开发新协议,详细代码请查看源码。

实现两个trait

在libp2p-rs中,swarm提供了两个trait:

  • Notifiee用于接收swarm的通知,当有新的连接创建或者连接关闭时,swarm会调用connected()或者disconnected();
  • ProtocolHandler用于读写协议的数据,协议协商成功后,swarm会调用handle()。
/// Notifiee is an trait for an object wishing to receive notifications from swarm
pub trait Notifiee {
    /// called when a connection opened
    fn connected(&mut self, _conn: &mut Connection) {}
    /// called when a connection closed
    fn disconnected(&mut self, _conn: &mut Connection) {}
}

/// Common trait for upgrades that can be applied on inbound substreams, outbound substreams,
/// or both.
/// Possible upgrade on a connection or substream.
#[async_trait]
pub trait ProtocolHandler: UpgradeInfo + Notifiee {
    /// After we have determined that the remote supports one of the protocols we support, this
    /// method is called to start handling the inbound. Swarm will start invoking this method
    /// in a newly spawned task.
    ///
    /// The `info` is the identifier of the protocol, as produced by `protocol_info`.
    async fn handle(&mut self, stream: Substream, info: <Self as UpgradeInfo>::Info) -> Result<(), Box<dyn Error>>;
    /// This is to provide a clone method for the trait object.
    fn box_clone(&self) -> IProtocolHandler;
}

floodsub handler实现Notifiee和ProtocolHandler

#[derive(Clone)]
pub struct Handler {
    incoming_tx: mpsc::UnboundedSender<RPC>,
    new_peer: mpsc::UnboundedSender<PeerEvent>,
}

impl Handler {
    pub(crate) fn new(incoming_tx: mpsc::UnboundedSender<RPC>, new_peer: mpsc::UnboundedSender<PeerEvent>) -> Self {
        Handler { incoming_tx, new_peer }
    }
}

impl UpgradeInfo for Handler {
    type Info = &'static [u8];

    fn protocol_info(&self) -> Vec<Self::Info> {
        vec![FLOOD_SUB_ID]
    }
}

impl Notifiee for Handler {
    fn connected(&mut self, conn: &mut Connection) {
        let peer_id = conn.remote_peer();
        let mut new_peers = self.new_peer.clone();
        task::spawn(async move {
            let _ = new_peers.send(PeerEvent::NewPeer(peer_id)).await;
        });
    }
}

#[async_trait]
impl ProtocolHandler for Handler {
    async fn handle(&mut self, mut stream: Substream, _info: <Self as UpgradeInfo>::Info) -> Result<(), Box<dyn Error>> {
        loop {
            /* recv, decode and send to msg process mainloop */
            self.incoming_tx.send(rpc).await.map_err(|_| FloodsubDecodeError::ProtocolExit)?;
        }
    }

    fn box_clone(&self) -> IProtocolHandler {
        Box::new(self.clone())
    }
}

注册到swarm

let floodsub = FloodSub::new(FloodsubConfig::new(local_peer_id));
let handler = floodsub.handler();

let mut swarm = Swarm::new(local_key.public()).with_protocol(Box::new(handler))

还需要做什么

简单的协议,比如echo,那么所有事情都在ProtocolHandler.handle()中处理即可,到这里就结束了。

稍微复杂的协议,比如floodsub,最好将swarm的通知和收到的数据,发送到消息处理主循环进行处理,实时更新状态;

impl floodsub {
	pub fn start(mut self, control: Swarm_Control) {
	    self.control = Some(control);
	
	    // well, self 'move' explicitly,
	    let mut floodsub = self;
	    task::spawn(async move {
	        let _ = floodsub.process_loop().await;
	    });
	}

	/// Message Process Loop.
	pub async fn process_loop(&mut self) -> Result<()> {
		loop {
	        select! {
	            cmd = self.peer_rx.next() => {
	                self.handle_peer_event(cmd).await;
	            }
	            rpc = self.incoming_rx.next() => {
	                self.handle_incoming_rpc(rpc).await?;
	            }
	            cmd = self.control_rx.next() => {
	                self.on_control_command(cmd).await?;
	            }
	            sub = self.cancel_rx.next() => {
	                self.un_subscribe(sub).await?;
	            }
	        }
	    }
	}
}

从上面可以看到,floodsub消息处理主循环运行在一个task里面,start()时需要将self传递进去,因此后续的发布订阅等操作只能通过channel发消息,这就是control和handler包裹channel的原因。

#[derive(Clone)]
pub struct Control {
    config: FloodsubConfig,
    control_sender: mpsc::UnboundedSender<ControlCommand>,
}

impl Control {
    /// Subscribe to messages on a given topic.
    pub async fn subscribe(&mut self, topic: Topic) -> Option<Subscription> {
        let (tx, rx) = oneshot::channel();
        self.control_sender
            .send(ControlCommand::Subscribe(topic, tx))
            .await
            .expect("control send subscribe");
        rx.await.expect("Subscribe")
    }
}

当新的连接创建时,floodsub会主动创建流,协商通过后向对方发送本节点感兴趣的topic。因此这里需要swarm的control。

self.control.as_mut().unwrap().new_stream(pid, vec![FLOOD_SUB_ID]).await;

总结

在libp2p-rs上面开发简单的协议,只需要两步,对于稍微复杂的协议,需要handler和control这类包裹channel的结构,将消息发送到协议消息处理主循环,以驱动整个协议的运转,完成特定的功能。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值