I have implemented a web socket server using Play Framework. The server can take connections and respond to clients. If the connections are idle for some time then the server automatically closes the connection . I am not sure if there is any configuration to make the connections always alive. So in order to monitor the connection status (connection is alive or not), the server needs to send PING message to the client at a particular time intervals and it should receive PONG from the client.
Below is my server implementation
@Singleton
class RequestController @Inject()(cc: ControllerComponents)(implicit system: ActorSystem, mat: Materializer) extends AbstractController(cc) {
def ws = WebSocket.accept[String, String] {req =>
ActorFlow.actorRef { out =>
ParentActor.props(out)
}
}
}
object ParentActor {
def props(out: ActorRef) = Props(new ParentActor(out))
}
class ParentActor(out : ActorRef) extends Actor {
override def receive: Receive = {
case msg: String => out ! s"Echo from server $msg"
}
}
So how to send web socket ping message from server to client at particular time intervals?
You can use a scheduler in order to send message to client in certain interval. Following is one of the example that can be use to implement your scenario:
class ParentActor(out : ActorRef) extends Actor {
var isPongReceived = false
override def receive: Receive = {
case "START" =>
// Client started the connection, i.e. so, initially let mark client pong receive status as true.
isPongReceived = true
out ! s"Echo from server - Connection Started"
// This part schedules a scheduler that check in every 10 seconds if client is active/connected or not.
// Sends PING response if connected to client, otherwise terminate the actor.
context.system.scheduler.schedule(new DurationInt(10).seconds,
new DurationInt(10).seconds) {
if(isPongReceived) {
// Below ensures that server is waiting for client's PONG message in order to keep connection.
// That means, next time when this scheduler run after 10 seconds, if PONG is not received from client, the
// connection will be terminated.
isPongReceived = false
out ! "PING_RESPONSE"
}
else {
// Pong is not received from client / client is idle, terminate the connection.
out ! PoisonPill // Or directly terminate - context.stop(out)
}
}
case "PONG" => isPongReceived = true // Client sends PONG request, isPongReceived status is marked as true.
}
}