I run ping pong example in two different console windows like described in that tutorial.
use libp2p::futures::StreamExt;
use libp2p::ping::{Ping, PingConfig};
use libp2p::swarm::{Swarm, SwarmEvent};
use libp2p::{identity, Multiaddr, PeerId};
use std::error::Error;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let local_key = identity::Keypair::generate_ed25519();
let local_peer_id = PeerId::from(local_key.public());
println!("Local peer id: {:?}", local_peer_id);
let transport = libp2p::development_transport(local_key).await?;
let behaviour = Ping::new(PingConfig::new().with_keep_alive(true));
let mut swarm = Swarm::new(transport, behaviour, local_peer_id);
swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
if let Some(addr) = std::env::args().nth(1) {
let remote: Multiaddr = addr.parse()?;
swarm.dial(remote)?;
println!("Dialed {}", addr)
}
loop {
match swarm.select_next_some().await {
SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {:?}", address),
SwarmEvent::Behaviour(event) => println!("{:?}", event),
_ => {}
}
}
}
console 1:
cargo run
Local peer id: PeerId("12D3KooWGi2AZgQL5sXDx4WYcPbtpXQDJJf2JTzEfHMKoYARohUN")
Listening on "/ip4/127.0.0.1/tcp/53912"
Listening on "/ip4/192.168.100.4/tcp/53912"
Event { peer: PeerId("12D3KooWQTRrShA41Kh7qoQYUc3G63uDZcXPSFw68AmzZqmaaHSh"), result: Ok(Pong) }
Event { peer: PeerId("12D3KooWQTRrShA41Kh7qoQYUc3G63uDZcXPSFw68AmzZqmaaHSh"), result: Ok(Ping { rtt: 1.112947ms }) }
Event { peer: PeerId("12D3KooWQTRrShA41Kh7qoQYUc3G63uDZcXPSFw68AmzZqmaaHSh"), result: Ok(Pong) }
Event { peer: PeerId("12D3KooWQTRrShA41Kh7qoQYUc3G63uDZcXPSFw68AmzZqmaaHSh"), result: Ok(Ping { rtt: 1.682508ms }) }
console 2
cargo run -- /ip4/192.168.100.4/tcp/53912
Local peer id: PeerId("12D3KooWQTRrShA41Kh7qoQYUc3G63uDZcXPSFw68AmzZqmaaHSh")
Dialed /ip4/192.168.100.4/tcp/53912
Listening on "/ip4/127.0.0.1/tcp/53913"
Listening on "/ip4/192.168.100.4/tcp/53913"
Event { peer: PeerId("12D3KooWGi2AZgQL5sXDx4WYcPbtpXQDJJf2JTzEfHMKoYARohUN"), result: Ok(Pong) }
Event { peer: PeerId("12D3KooWGi2AZgQL5sXDx4WYcPbtpXQDJJf2JTzEfHMKoYARohUN"), result: Ok(Ping { rtt: 869.493µs }) }
Event { peer: PeerId("12D3KooWGi2AZgQL5sXDx4WYcPbtpXQDJJf2JTzEfHMKoYARohUN"), result: Ok(Pong) }
Event { peer: PeerId("12D3KooWGi2AZgQL5sXDx4WYcPbtpXQDJJf2JTzEfHMKoYARohUN"), result: Ok(Ping { rtt: 2.109459ms }) }
What confuses me is the order of ping and pong messages. This example creates two nodes which send ping pong messages each other. But why do they start with Pong msg every time I run these nodes? I expect first Ping and then Pong event. Do I miss something?
That seems to simply be down to the library's semantics: if you go look at the documentation for PingSuccess (the "success" side of a ping operation), the two variants are:
Pong
Received a ping and sent back a pong.
Ping
Sent a ping and received back a pong. Includes the round-trip time.
So the events are not asymmetric: while "pong" means "I received a ping and sent a pong", "ping" means I received a response to my ping, which is why it can report the RTT. And so the timeline is
ping ->
<- pong
triggers PingSuccess::Pong
triggers PingSuccess::Ping
Thus the order you can see here.