3

我需要向当前正在处理 Play WebSocket 消息的客户端发送私人响应,而对于其他消息,我需要向所有客户端广播响应。

在 Play 聊天示例中,传入的消息会立即卸载到单个 Actor

case Connected(enumerator) => 
  // Create an Iteratee to consume the feed, assuming all requests have
  // a JSON "text" node, and delegate all to the Akka Actor:
  val iteratee = Iteratee.foreach[JsValue] { event =>
    default ! Talk(username, (event \ "text").as[String])
  }.map { _ =>
    default ! Quit(username)
  }
  (iteratee,enumerator)

上面,一旦连接被批准enumerator,就会传回,这指的chatEnumerator是同一个 Actor 已经创建的单曲:

val (chatEnumerator, chatChannel) = Concurrent.broadcast[JsValue]

我认为这不允许我向单个客户发送消息?我应该删除广播功能并为每个客户端创建和跟踪枚举器,然后自己迭代吗?或者我可以以某种方式获得对特定于客户端的枚举器的引用foreach吗?

(我知道内置的灵活性取决于实现,但是这个用例对我来说似乎很常见。就像使用带有Socket.IO的 WebSockets 时,我可以轻松地将消息发送所有客户端,除了当前的发送者之外的所有客户端请求,或者只是一个客户端。这也是我在 Play 2.1.x 中想要实现的。)

4

2 回答 2

2

一种方法是使用Enumerator.interleave.

因此,您可以创建两对(Enumerator, Channel)使用Concurrent.broadcast两次,一对用于广播,另一对用于私有连接,并交错使用。(或者可能只是Concurrent.unicast用于私人枚举器,但我不知道如何使用它。)

这是一些适用于 play 2.3.0 的示例播放代码。

object Application extends Controller {
  val (publicOut,publicChannel) = Concurrent.broadcast[String]
  def chat = WebSocket.using[String]{ request =>
    val (privateOut,privateChannel) = Concurrent.broadcast[String]
    val in = Iteratee.foreach{
      msg:String => if(msg.startsWith("@")){
        publicChannel.push("Broadcasted: " + msg)
      }else{
        privateChannel.push("Private: " + msg)
      }
    }
    val out = Enumerator.interleave(publicOut,privateOut)
    (in, out)
  }
}

向特定客户端发送消息将是一些复杂的代码,但概念是相同的。创建一个Actor包含(Enumerator, Channel)每个 websocket 的一对并向参与者发送消息。

于 2014-06-23T00:57:52.213 回答
1

这似乎是对 scala 的一种破解。websocket-chat 应用程序的 java 版本使用地图来存储每个用户名和频道。然后遍历它们。更改/阻止消息只是基于循环内的用户名进行分支。我也在寻找一个好的解决方案。

Map<String, WebSocket.Out<JsonNode>> members = new HashMap<String, WebSocket.Out<JsonNode>>();

完整代码在这里

于 2013-10-09T16:46:36.317 回答