WebSockets[翻译]
2019独角兽企业重金招聘Python工程师标准>>>
原文:WebSockets
WebSockets
WebSockets是一个可以被基于允许双向全双工通信协议的Web浏览器使用的套接字。只要在服务端和客户端之间有一个活跃的WebSocket 连接, 客户端可以发送信息,服务端可以在任何时候收到信息。
新的兼容HTML5的浏览器本身通过JavaScript WebSocket API 支持WebSockets 。然而WebSockets 不仅仅局限于被WebBrowsers使用,有很多的WebSocket客户端库可以用,例如允许服务端彼此通信,也允许本地的移动应用可以使用WebSockets。在这些环境中使用WebSockets 有可以复用Play服务端已使用的TCP端口的好处。
提示:查看caniuse.com了解更多关于浏览器支持WebSockets的已知问题和更多信息。
处理WebSockets
到目前为止,我们使用Action实例处理标准的HTTP 请求,并发送回标准的HTTP应答。而WebSockets 是完全不同的东西,并不能通过标准的Action处理。
Play提供了两个不同的内置机制来处理WebSockets。第一个是使用Akka Streams(通常用Actor),第二种是使用Iteratees。这两种机制都可以使用构建器提供的WebSocket访问。
使用Akka Streams 和 Actors处理 WebSockets
为了使用Actor处理WebSocket,我们需要给Play一个携带Actor信息的akka.actor.Props 对象,当Play接受到WebSocket 连接时就可以创建Actor。Play将会给我们一个 akka.actor.ActorRef来发送上行信息,如此我们可以使用它来帮助创建Props 对象:
import play.api.mvc._
import play.api.libs.streams._class Controller1 @Inject() (implicit system: ActorSystem, materializer: Materializer) {def socket = WebSocket.accept[String, String] { request =>
ActorFlow.actorRef(out => MyWebSocketActor.props(out))
}
}
注意ActorFlow.actorRef(...) 可以使用任何的Akka Streams Flow[In, Out, _]替换,但是一般情况下,Actor是最直接的方式。
在这种情况下,我们是这样发送的Actor的:
import akka.actor._object MyWebSocketActor {
def props(out: ActorRef) = Props(new MyWebSocketActor(out))
}class MyWebSocketActor(out: ActorRef) extends Actor {
def receive = {
case msg: String =>
out ! ("I received your message: " + msg)
}
}
从客户端收到的任何信息都会被发送给Actor,并且由Play提供的发送给Actor的任何信息都会被发送给客户端。上面简单的Actor发送从客户端收回的每一个信息都会附加一个I received your message:
当检测到一个WebSocket已经关闭
当WebSocket 已经关闭时,Play会自动的停止Actor。那么你可以通过使用Actor的postStop 方法来处理这个状况,清理WebSocket消耗的任何资源,例如:
override def postStop() = {
someResource.close()
}
关闭WebSocket
当处理的WebSocket结束时,Play将自动的关闭WebSocket。 所以,Play发送PoisonPill给你自己的Actor ,来关闭WebSocket:
import akka.actor.PoisonPillself ! PoisonPill
拒绝WebSocket
有时你希望拒绝WebSocket 请求,例如,如果用户必须被验证了才能连接WebSocket,或者如果WebSocket与资源关联,它的ID通过路径传递,但是没有这个ID相关的资源。Play提供了acceptOrResult 来解决这个问题,允许你返回一个结果(如禁止,或没找到),或者处理WebSocket 的Actor:
import scala.concurrent.Future
import play.api.mvc._
import play.api.libs.streams._class Controller3 @Inject() (implicit system: ActorSystem, materializer: Materializer) extends play.api.mvc.Controller {
def socket = WebSocket.acceptOrResult[String, String] { request =>
Future.successful(request.session.get("user") match {
case None => Left(Forbidden)
case Some(_) => Right(ActorFlow.actorRef(MyWebSocketActor.props))
})
}
}
注意:WebSocket 协议没有实现同原协议,所以没有防备跨站点的WebSocket劫持。为了保护WebSocket不被劫持,在请求中的Origin头必须被检查以防服务端的源,并且应该实现手动的验证(包括CSRF令牌),然后acceptOrResult通过返回Forbidden 结果拒绝请求。
处理不同类型的信息
到目前为止,我们只看到处理String 结构的信息。Play也已经内置了 Array[Byte] 结构的处理,和从String结构的信息解析的JsValue 信息。你可以把这些做为类型参数传递给WebSocket 的创建方法,例如:
import play.api.libs.json.JsValue
import play.api.mvc._
import play.api.libs.streams._class Controller4 @Inject() (implicit system: ActorSystem, materializer: Materializer) {
import akka.actor._class MyWebSocketActor(out: ActorRef) extends Actor {
import play.api.libs.json.JsValue
def receive = {
case msg: JsValue =>
out ! msg
}
}object MyWebSocketActor {
def props(out: ActorRef) = Props(new MyWebSocketActor(out))
}def socket = WebSocket.accept[JsValue, JsValue] { request =>
ActorFlow.actorRef(out => MyWebSocketActor.props(out))
}}
你也许已经注意到了,有两个类型参数,这让我们可以处理传入不同类型的信息给输出的信息。这通常对低级的结构类型无用,但是如果你把信息解析成高级类型就可以使用了。
例如,假如我们想接收JSON信息,我们想把传入的信息解析为InEvent 并格式化输出的信息为OutEvent。我们想做的第一件事情是为所有的InEvent 和 OutEvent 类型创建JSON格式:
import play.api.libs.json._implicit val inEventFormat = Json.format[InEvent]
implicit val outEventFormat = Json.format[OutEvent]
现在我们可以为这些类型创建一个MessageFlowTransformer :
import play.api.mvc.WebSocket.FrameFormatterimplicit val messageFlowTransformer = MessageFlowTransformer.jsonMessageFlowTransformer[InEvent, OutEvent]
最终,我在我们的WebSocket中使用这些:
import play.api.libs.json._
import play.api.mvc._
import play.api.libs.streams._// Note: requires implicit ActorSystem and Materializer (inject into your controller)
def socket = WebSocket.accept[InEvent, OutEvent] { request =>
ActorFlow.actorRef(out => MyWebSocketActor.props(out))
}
现在在我们的Actor中,我们将接收到InEvent类型的信息,并且我们可以发送OutEvent类型的信息。
使用Iteratees处理 WebSockets
为了处理WebSocket 请求,我们用WebSocket 替代了Action:
import play.api.mvc._
import play.api.libs.iteratee._
import play.api.libs.concurrent.Execution.Implicits.defaultContextdef socket = WebSocket.using[String] { request =>// Log events to the console
val in = Iteratee.foreach[String](println).map { _ =>
println("Disconnected")
}// Send a single 'Hello!' message
val out = Enumerator("Hello!")(in, out)
}
WebSocket能够访问请求的头(从启动WebSocket 连接的HTTP 请求),允许你可以取到标准的头和Session数据,然而,它不能访问请求Body和HTTP应答。
当构建一个这样的WebSocket ,我必须返回in 和out 通道。
in通道是一个Iteratee[A,Unit](A是信息的类型——这里我们用String),它将通知每一个信息,并且当套接字在客户端关闭时会EOF。
out通道是一个Enumerator[A],它将会生成发送到Web客户端的信息,它可以通过发送EOF关闭连接服务端连接。
在这个例子中我们创建了一个简单的Iteratee, 它在控制台上打印了每一个信息。为了发送信息,我们创建了一个简单的虚拟
Enumerator,它将会发送一个Hello!信息。
提示:只要设置location 为ws://localhost:9000, 你就可以在https://www.websocket.org/echo.html,测试WebSockets 。
让我们再写一个丢弃输入数据并发送Hello!信息后关闭套接字的例子:
import play.api.mvc._
import play.api.libs.iteratee._def socket = WebSocket.using[String] { request =>// Just ignore the input
val in = Iteratee.ignore[String]// Send a single 'Hello!' message and close
val out = Enumerator("Hello!").andThen(Enumerator.eof)(in, out)
}
这是另一个例子,在这个例子中输入数据被记录到标准输出,并广播到使用Concurrent.broadcast的客户端:
import play.api.mvc._
import play.api.libs.iteratee._
import play.api.libs.concurrent.Execution.Implicits.defaultContextdef socket = WebSocket.using[String] { request =>// Concurrent.broadcast returns (Enumerator, Concurrent.Channel)
val (out, channel) = Concurrent.broadcast[String]// log the message to stdout and send response back to client
val in = Iteratee.foreach[String] {
msg => println(msg)
// the Enumerator returned by Concurrent.broadcast subscribes to the channel and will
// receive the pushed messages
channel push("I received your message: " + msg)
}
(in,out)
}
转载于:https://my.oschina.net/u/587323/blog/889858
WebSockets[翻译]相关推荐
- 被动套接字 主动套接字_了解网络套接字及其可能性
被动套接字 主动套接字 介绍 (Introduction) As users of web applications, we are accustomed to being able to quick ...
- websockets_如何将WebSockets与AWS API Gateway和Lambda一起使用来构建实时应用程序
websockets by Janitha Tennakoon 通过詹妮莎·特纳库恩 如何将WebSockets与AWS API Gateway和Lambda一起使用来构建实时应用程序 (How to ...
- 《Introduction to Tornado》中文翻译计划——第五章:异步Web服务
http://www.pythoner.com/294.html 本文为<Introduction to Tornado>中文翻译,将在https://github.com/alioth3 ...
- gevent-tutorial翻译和解读
原文在[1],翻译的在[2],这篇博客进一步解读一些代码,以及加了一些注释,并且所有代码都已经改成python3.x: (斜线表示原文引用) ----------------------------- ...
- php cdi_集成CDI和WebSockets
php cdi 考虑尝试一个简单的Java EE 7原型应用程序,该应用程序涉及JAX-RS(REST),WebSockets和CDI. 注意 :不想让它成为破坏者-但本文主要讨论我在尝试使用Web套 ...
- websockets_使用用户名/密码和Servlet安全性保护WebSockets
websockets RFC 6455提供了WebSockets安全注意事项的完整列表. 其中一些是在协议本身中烘焙的,其他一些则需要更多有关如何在特定服务器上实现它们的解释. 让我们来谈谈协议本身内 ...
- websockets_使用Java WebSockets,JSR 356和JSON映射到POJO的
websockets 因此,我一直在研究Tyrus (JSR 356 WebSocket for Java规范的参考实现). 因为我一直在寻找测试工具,所以我对在Java中同时运行客户端和服务器端感兴 ...
- 集成CDI和WebSockets
考虑尝试一个简单的Java EE 7原型应用程序,该应用程序涉及JAX-RS(REST),WebSockets和CDI. 注意 :不想让它成为破坏者-但这篇文章主要讨论了我在尝试使用Web套接字和使用 ...
- 使用用户名/密码和Servlet安全性保护WebSockets
RFC 6455提供了WebSockets安全注意事项的完整列表. 其中一些是在协议本身中烘焙的,其他一些则需要更多有关如何在特定服务器上实现它们的解释. 让我们谈谈协议本身内置的一些安全性: HTT ...
最新文章
- C#中try catch中throw ex和throw方式抛出异常有何不同
- 苹果在GitHub上正式开源iOS内核源码
- (转)8种常见机器学习算法比较
- mysql将查到的数据删除_MySQL数据库的基本操作——增、删、改、查
- P1334 瑞瑞的木板
- Web Broadcast Channel
- Qt——P23 登录窗口布局
- 软件工程第二次作业中第一个作业
- 服务器硬盘和台式机有什么区别,服务器和电脑主机有什么区别?
- 雅虎48亿美元卖身Verizon,门户网站路在何方?
- 树莓派简易快速安装OpenCV4
- 零点起算法44求最小值C语言,【算法链表面试题】面试问题:C语言实现“关… - 看准网...
- 微信APP退款功能开发
- python语言常用的中文分词第三方库是_基于boost使用Python调用NLPIR(ICTCLAS2013)中文分词组件...
- Android开发-视图view讲解
- windows测试linux端口,windows、Linux 测试服务器、电脑的某些个端口是否打开
- 操作系统管理计算机资源
- C#线程实现暂停与继续
- maven 国内源配置
- 最详细的MOS管讲解
热门文章
- UIAlertview改变按钮位置 大小
- python 的import m.a.b 和 from m.a import b的区别
- C#基础系列问题一break、continue、return、goto语句
- ubuntu-基本命令篇-12-磁盘管理
- 「golang」panic: commands out of sync. Did you run multiple statements at once
- 治堵有智慧 城市轨道交通建设开启奔跑模式
- 第4章 管道与FIFO
- QApplicationQPushButton
- VC6安装错误——Error Launching ......acmboot.exe
- tomcat运行模式APR安装