protocol_ping.rs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. use std::{sync::Arc, time::Instant};
  2. use async_trait::async_trait;
  3. use log::{debug, error};
  4. use rand::Rng;
  5. use smol::Executor;
  6. use crate::{util::sleep, Error, Result};
  7. use super::{
  8. super::{message, message_subscriber::MessageSubscription, ChannelPtr, P2pPtr, SettingsPtr},
  9. ProtocolBase, ProtocolBasePtr, ProtocolJobsManager, ProtocolJobsManagerPtr,
  10. };
  11. /// Defines ping and pong messages.
  12. pub struct ProtocolPing {
  13. channel: ChannelPtr,
  14. ping_sub: MessageSubscription<message::PingMessage>,
  15. pong_sub: MessageSubscription<message::PongMessage>,
  16. settings: SettingsPtr,
  17. jobsman: ProtocolJobsManagerPtr,
  18. }
  19. impl ProtocolPing {
  20. /// Create a new ping-pong protocol.
  21. pub async fn init(channel: ChannelPtr, p2p: P2pPtr) -> ProtocolBasePtr {
  22. let settings = p2p.settings();
  23. // Creates a subscription to ping message.
  24. let ping_sub = channel
  25. .clone()
  26. .subscribe_msg::<message::PingMessage>()
  27. .await
  28. .expect("Missing ping dispatcher!");
  29. // Creates a subscription to pong message.
  30. let pong_sub = channel
  31. .clone()
  32. .subscribe_msg::<message::PongMessage>()
  33. .await
  34. .expect("Missing pong dispatcher!");
  35. Arc::new(Self {
  36. channel: channel.clone(),
  37. ping_sub,
  38. pong_sub,
  39. settings,
  40. jobsman: ProtocolJobsManager::new("ProtocolPing", channel),
  41. })
  42. }
  43. /// Runs ping-pong protocol. Creates a subscription to pong, then starts a
  44. /// loop. Loop sleeps for the duration of the channel heartbeat, then
  45. /// sends a ping message with a random nonce. Loop starts a timer, waits
  46. /// for the pong reply and insures the nonce is the same.
  47. async fn run_ping_pong(self: Arc<Self>) -> Result<()> {
  48. debug!(target: "net", "ProtocolPing::run_ping_pong() [START]");
  49. loop {
  50. // Wait channel_heartbeat amount of time.
  51. sleep(self.settings.channel_heartbeat_seconds).await;
  52. // Create a random nonce.
  53. let nonce = Self::random_nonce();
  54. // Send ping message.
  55. let ping = message::PingMessage { nonce };
  56. self.channel.clone().send(ping).await?;
  57. debug!(target: "net", "ProtocolPing::run_ping_pong() send Ping message");
  58. // Start the timer for ping timer.
  59. let start = Instant::now();
  60. // Wait for pong, check nonce matches.
  61. let pong_msg = self.pong_sub.receive().await?;
  62. if pong_msg.nonce != nonce {
  63. // TODO: this is too extreme
  64. error!("Wrong nonce for ping reply. Disconnecting from channel.");
  65. self.channel.stop().await;
  66. return Err(Error::ChannelStopped)
  67. }
  68. let duration = start.elapsed().as_millis();
  69. debug!(target: "net", "Received Pong message {}ms from [{:?}]",
  70. duration, self.channel.address());
  71. }
  72. }
  73. /// Waits for ping, then replies with pong. Copies ping's nonce into the
  74. /// pong reply.
  75. async fn reply_to_ping(self: Arc<Self>) -> Result<()> {
  76. debug!(target: "net", "ProtocolPing::reply_to_ping() [START]");
  77. loop {
  78. // Wait for ping, reply with pong that has a matching nonce.
  79. let ping = self.ping_sub.receive().await?;
  80. debug!(target: "net", "ProtocolPing::reply_to_ping() received Ping message");
  81. // Send pong message.
  82. let pong = message::PongMessage { nonce: ping.nonce };
  83. self.channel.clone().send(pong).await?;
  84. debug!(target: "net", "ProtocolPing::reply_to_ping() sent Pong reply");
  85. }
  86. }
  87. fn random_nonce() -> u32 {
  88. let mut rng = rand::thread_rng();
  89. rng.gen()
  90. }
  91. }
  92. #[async_trait]
  93. impl ProtocolBase for ProtocolPing {
  94. /// Starts ping-pong keep-alive messages exchange. Runs ping-pong in the
  95. /// protocol task manager, then queues the reply. Sends out a ping and
  96. /// waits for pong reply. Waits for ping and replies with a pong.
  97. async fn start(self: Arc<Self>, executor: Arc<Executor<'_>>) -> Result<()> {
  98. debug!(target: "net", "ProtocolPing::start() [START]");
  99. self.jobsman.clone().start(executor.clone());
  100. self.jobsman.clone().spawn(self.clone().run_ping_pong(), executor.clone()).await;
  101. self.jobsman.clone().spawn(self.reply_to_ping(), executor).await;
  102. debug!(target: "net", "ProtocolPing::start() [END]");
  103. Ok(())
  104. }
  105. fn name(&self) -> &'static str {
  106. "ProtocolPing"
  107. }
  108. }