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[翻译]相关推荐

  1. 被动套接字 主动套接字_了解网络套接字及其可能性

    被动套接字 主动套接字 介绍 (Introduction) As users of web applications, we are accustomed to being able to quick ...

  2. websockets_如何将WebSockets与AWS API Gateway和Lambda一起使用来构建实时应用程序

    websockets by Janitha Tennakoon 通过詹妮莎·特纳库恩 如何将WebSockets与AWS API Gateway和Lambda一起使用来构建实时应用程序 (How to ...

  3. 《Introduction to Tornado》中文翻译计划——第五章:异步Web服务

    http://www.pythoner.com/294.html 本文为<Introduction to Tornado>中文翻译,将在https://github.com/alioth3 ...

  4. gevent-tutorial翻译和解读

    原文在[1],翻译的在[2],这篇博客进一步解读一些代码,以及加了一些注释,并且所有代码都已经改成python3.x: (斜线表示原文引用) ----------------------------- ...

  5. php cdi_集成CDI和WebSockets

    php cdi 考虑尝试一个简单的Java EE 7原型应用程序,该应用程序涉及JAX-RS(REST),WebSockets和CDI. 注意 :不想让它成为破坏者-但本文主要讨论我在尝试使用Web套 ...

  6. websockets_使用用户名/密码和Servlet安全性保护WebSockets

    websockets RFC 6455提供了WebSockets安全注意事项的完整列表. 其中一些是在协议本身中烘焙的,其他一些则需要更多有关如何在特定服务器上实现它们的解释. 让我们来谈谈协议本身内 ...

  7. websockets_使用Java WebSockets,JSR 356和JSON映射到POJO的

    websockets 因此,我一直在研究Tyrus (JSR 356 WebSocket for Java规范的参考实现). 因为我一直在寻找测试工具,所以我对在Java中同时运行客户端和服务器端感兴 ...

  8. 集成CDI和WebSockets

    考虑尝试一个简单的Java EE 7原型应用程序,该应用程序涉及JAX-RS(REST),WebSockets和CDI. 注意 :不想让它成为破坏者-但这篇文章主要讨论了我在尝试使用Web套接字和使用 ...

  9. 使用用户名/密码和Servlet安全性保护WebSockets

    RFC 6455提供了WebSockets安全注意事项的完整列表. 其中一些是在协议本身中烘焙的,其他一些则需要更多有关如何在特定服务器上实现它们的解释. 让我们谈谈协议本身内置的一些安全性: HTT ...

最新文章

  1. C#中try catch中throw ex和throw方式抛出异常有何不同
  2. 苹果在GitHub上正式开源iOS内核源码
  3. (转)8种常见机器学习算法比较
  4. mysql将查到的数据删除_MySQL数据库的基本操作——增、删、改、查
  5. P1334 瑞瑞的木板
  6. Web Broadcast Channel
  7. Qt——P23 登录窗口布局
  8. 软件工程第二次作业中第一个作业
  9. 服务器硬盘和台式机有什么区别,服务器和电脑主机有什么区别?
  10. 雅虎48亿美元卖身Verizon,门户网站路在何方?
  11. 树莓派简易快速安装OpenCV4
  12. 零点起算法44求最小值C语言,【算法链表面试题】面试问题:C语言实现“关… - 看准网...
  13. 微信APP退款功能开发
  14. python语言常用的中文分词第三方库是_基于boost使用Python调用NLPIR(ICTCLAS2013)中文分词组件...
  15. Android开发-视图view讲解
  16. windows测试linux端口,windows、Linux 测试服务器、电脑的某些个端口是否打开
  17. 操作系统管理计算机资源
  18. C#线程实现暂停与继续
  19. maven 国内源配置
  20. 最详细的MOS管讲解

热门文章

  1. UIAlertview改变按钮位置 大小
  2. python 的import m.a.b 和 from m.a import b的区别
  3. C#基础系列问题一break、continue、return、goto语句
  4. ubuntu-基本命令篇-12-磁盘管理
  5. 「golang」panic: commands out of sync. Did you run multiple statements at once
  6. 治堵有智慧 城市轨道交通建设开启奔跑模式
  7. 第4章 管道与FIFO
  8. QApplicationQPushButton
  9. VC6安装错误——Error Launching ......acmboot.exe
  10. tomcat运行模式APR安装