我正在看https://github.com/playframework/Play20/tree/master/samples/scala/websocket-chat
的例子要创建一个websocket控制器,可以这样写:def chat(username: String) = WebSocket.async[JsValue] { request =>
ChatRoom.join(username)
}
聊天室。join返回一个scala.concurrent.Future[(Iteratee[JsValue,_],Enumerator[JsValue])]。但是在Play!中使用的迭代器和枚举器在哪里?框架?WebSocket类(WebSocket.scala)似乎忽略输入:
case class WebSocket[A](f: RequestHeader => (Enumerator[A], Iteratee[A, Unit]) => Unit) (implicit val frameFormatter: WebSocket.FrameFormatter[A]) extends Handler {
type FRAMES_TYPE = A
/**
* Returns itself, for better support in the routes file.
*
* @return itself
*/
def apply() = this
}
How does Play!当迭代器消耗输入时,管理它不断变化的状态?
值得注意的是,WebSocket
本身只是一个哑容器。在play.core.server.netty
中,魔法发生在不同的类中。
要理解这个魔力是什么,看一下f (WebSocket
包含的函数)的签名是有指导意义的:
RequestHeader => (Enumerator[A], Iteratee[A, Unit]) => Unit
这是一个获取RequestHeader
、Enumerator
和Iteratee
的函数,并对它们进行处理。
因此,在未来的某个时刻,框架将为我们的WebSocket
提供RequestHeader
(应该是不言自明的),Enumerator[A]
(枚举器是源,在这种情况下,这些是从客户端接收的消息)和Iteratee[A, Unit]
(迭代器是接收器,在这种情况下,这是我们发送消息返回客户端的地方)。
在WebSocket.adapter
的情况下,WebSocket将通过Enumeratee
将Enumerator
连接到Iteratee
。在WebSocket.using
的情况下,WebSocket将远程Enumerator
连接到本地Iteratee
,并将移除Iteratee
连接到本地Enumerator
。
WebSocket
对象中的一个方便方法可能更容易。以下代码将回显收到的前一条消息:
def mySocket = WebSocket.adapter {implicit req =>
var lastMessage = "No previous message"
Enumeratee.map[String] {msg =>
val response = lastMessage
lastMessage = msg
response
}
}
请注意,这段代码几乎肯定存在线程安全问题——在Scala中,您应该尽可能避免可变状态,或者使用actor或类似的方法。
或者,尝试WebSocket.using
,并查看pushee Enumerator
,与foreach Iteratee
结合使用,尽管它有点麻烦。也许可以理解,推式枚举器在Play 2.1中被弃用了,因为它被新的通道系统所取代。