我是scala的新手,无法使用websocket向客户端发送私有消息。
这是我的控制器:
object Client extends Controller {
def socket(uuid: String) = WebSocket.acceptWithActor[String, String] { request =>
out => ClientWebSocket.props(uuid)
}
// Test Method to send message to websocket connected client
def sendMessage(guid: String) = Action { implicit request =>
val system = ActorSystem("default")
val out = system.actorOf(Props(classOf[ClientWebSocket], guid))
out ! SendUpdate("Message Recieved")
Ok
}
}
这是我的演员课:
object ClientWebSocket {
def props(uuid: String) = Props(new ClientWebSocket(uuid))
case class SendUpdate(msg:String)
}
class ClientWebSocket(uuid: String) extends Actor {
import ClientWebSocket._
def receive = {
case SendUpdate(msg:String) =>
sender ! "Message is " + msg
}
}
当我用客户端的uuid调用sendMessage时,我收到了akka死信遇到的错误。非常感谢您的帮助。
首先,在WebSocket方法中,您需要使用提供的ActorRef
而不是sender
。sender
可能是层次结构中的其他东西。
class ClientWebSocket(uuid: String, out: ActorRef) extends Actor {
import ClientWebSocket._
def receive = {
case SendUpdate(msg: String) =>
out ! s"Message is $msg"
}
}
其次,你在sendMessage
中收到了死信,因为你正在回复你的控制器,不幸的是,控制器不是Actor。
问题是您无法获得ActorRef
,并且由于您不知道参与者的名称,因此无法使用ActorSelection
。因此,你需要对自己的应用程序进行WebSocket调用,从web浏览器/JavaScript调用它,或者进行一些黑客攻击以找到参与者。
编辑
您的ClientWebSocket
可以向另一个参与者注册(例如,在preStart
中通过ActorSelection
或通过ActorRef
作为道具参数),该参与者保留对所有websocket的Map[String, ActorRef]
引用,然后使用死亡监视来监视它们。然后,该actor会将您的SendUpdate
转发给正确的websocket actor。由于只能在acceptWithActor
中返回Props,因此无法形成真正的层次结构。
out参数是ActorRef,您将使用它向客户端发送消息,因此您需要将其保留在Actor中。
object Client extends Controller {
def socket(uuid: String) = WebSocket.acceptWithActor[String, String] { request =>
// out is an actor to what the outbound messages should go to respond to remote client
out => ClientWebSocket.props(uuid, out)
}
}
您的ClientWebSocket参与者如下:
class ClientWebSocket(uuid: String, out: ActorRef) extends Actor ...
伴随物体如下
object ClientWebSocket {
def props(uuid: String, out: ActorRef) = Props(classOf[ClientWebSocket], uuid, out)
}
在ClientWenSocket中,您可以使用CentralWebSocketControl Actor将Actor注册/注销为:
class ClientWebSocket(uuid: String, out: ActorRef) extends Actor {
@throws[Exception](classOf[Exception])
override def preStart(): Unit = {
super.preStart()
CentralWebSocketControl.instance ! RegisterMe(uuid)
}
@throws[Exception](classOf[Exception])
override def postStop(): Unit = {
CentralWebSocketControl.instance ! UnregisterMe(uuid)
super.postStop()
}
}
CentralWebSocketControl Actor可以是:
class CentralWebSocketControl extends Actor {
val clientActors = scala.collection.mutable.HashMap[String, ActorRef]()
override def receive: Actor.Receive = {
case RegisterMe(uuid) =>
clientActors += uuid -> sender()
case UnregisterMe(uuid) =>
clientActors -= uuid
}
}
object CentralWebSocketControl {
val instance = Akka.system.actorOf(Props(classOf[CentralWebSocketControl]), name = "CentralWebSocketControl")
}
要将消息发送到由uuid标识的给定ClientWebSocket,您可以向CentralWebSocketControl发送消息,后者可以将消息委托给已注册的ClientWebSocket。
class CentralWebSocketControl extends Actor {
val clientActors = scala.collection.mutable.HashMap[String, ActorRef]()
override def receive: Actor.Receive = {
...
case SendUpdateTo(uuid: String, msg: String) =>
clientActors.get(uuid) match {
case Some(actor) =>
actor ! SendUpdate(msg)
case None =>
???
}
}
}
最后是
class ClientWebSocket(uuid: String, out: ActorRef) extends Actor {
override def receive: Receive = {
case SendUpdate(msg) =>
out ! msg
}
...
}