WebSockets[翻訳]
7091 ワード
原文:WebSockets
WebSockets
WebSocketsは、双方向フルデュプレクス通信プロトコルを許可するWebブラウザに基づいて使用できるソケットである.サービス側とクライアントの間にアクティブなWebSocket接続がある限り、クライアントは情報を送信することができ、サービス側はいつでも情報を受信することができる.
新しいHTML 5対応ブラウザ自体はJavaScript WebSocket APIでWebSocketsをサポートしています.しかし、WebSocketsはWebBrowsersによって使用されるだけでなく、サービス側同士の通信を許可したり、ローカルのモバイルアプリケーションがWebSocketsを使用したりするなど、多くのWebSocketクライアントライブラリが使用できます.これらの環境でWebSocketsを使用すると、Playサービス側で使用されているTCPポートを多重化できるメリットがあります.
ヒント:caniuseを表示します.comは、ブラウザがWebSocketsをサポートする既知の問題と詳細について説明します.
WebSocketsの処理
これまで,Actionインスタンスを用いて標準のHTTP要求を処理し,標準のHTTP応答に返信してきた.WebSocketsは全く違うもので、標準的なActionでは処理できません.
Playは、WebSocketsを処理するための2つの異なる組み込みメカニズムを提供する.1つ目はAkka Streams(通常はActor)、2つ目はIterateesを使用します.どちらのメカニズムも、ビルダーが提供するWebSocketを使用してアクセスできます.
Akka StreamsとActorsを使用してWebSocketsを処理
Actorを使用してWebSocketを処理するには、PlayにActor情報を携帯するakkaが必要です.actor.Propsオブジェクトは、PlayがWebSocket接続を受け入れると、Actorを作成できます.Playはakkaをくれます.actor.ActorRefは、アップリンク情報を送信します.これにより、Propsオブジェクトの作成を支援できます.
気をつけてactorRef(...) 任意のAkka Streams Flow[In,Out,]を使用できます.置き換えますが、一般的には、Actorが最も直接的な方法です.
この場合、私たちはこのようにActorを送信します.
クライアントから受け取った情報はすべてActorに送信され、Playから提供されたActorに送信された情報はすべてクライアントに送信される.上記の簡単なActorは、クライアントから回収された各情報を送信し、
WebSocketがオフになっていることが検出された場合
WebSocketがオフの場合、Playは自動的にActorを停止します.では、ActorのpostStopメソッドを使用してこの状況を処理し、WebSocketが消費するリソースを整理することができます.たとえば、次のようにします.
WebSocketを閉じる
処理されたWebSocketが終了すると、PlayはWebSocketを自動的に閉じる.だから、PlayはPoisonPillを自分のActorに送信して、WebSocketを閉じます.
WebSocketを拒否
WebSocketリクエストを拒否したい場合があります.たとえば、WebSocketに接続するにはユーザーが検証されなければならない場合、またはWebSocketがリソースに関連付けられている場合、そのIDはパスを介して渡されますが、このIDに関連するリソースはありません.Playはこの問題を解決するためにacceptOrResultを提供し、禁止、見つからないなどの結果を返したり、WebSocketのActorを処理したりすることができます.
注意:WebSocketプロトコルは元のプロトコルとは実現していないため、サイト間でのWebSocketハイジャックには備えていません.WebSocketがハイジャックされないように保護するために、要求中のOriginヘッダは、サービス側のソースを防ぐためにチェックされ、CSRFトークンを含む手動の検証が実現され、acceptOrResultはForbiddenに戻ることによって要求を拒否する.
異なるタイプの情報の処理
これまでString構造を扱う情報しか見られなかった.PlayにはArray[Byte]構造の処理とString構造の情報から解析したJsValue情報も内蔵されている.WebSocketの作成方法には、次のようなタイプパラメータとして渡すことができます.
2つのタイプのパラメータがあることに気づいたかもしれませんが、異なるタイプの情報が出力される情報を処理することができます.これは通常、低レベルの構造タイプには役に立たないが、情報を高度なタイプに解析すれば使用できる.
例えば、JSON情報を受信したい場合、入力された情報をInventとして解析し、出力された情報をOutEventとしてフォーマットしたい.私たちが最初にしたいことは、すべてのInEventとOutEventタイプのJSONフォーマットを作成することです.
これらのタイプのMessageFlowTransformerを作成できます.
結局、私たちのWebSocketでこれらを使用しました.
今、私たちのActorでは、Inventタイプの情報を受信し、OutEventタイプの情報を送信することができます.
Iterateesを使用してWebSocketsを処理する
WebSocketリクエストを処理するために、Actionの代わりにWebSocketを使用しました.
WebSocketはリクエストのヘッダ(WebSocket接続を開始したHTTPリクエストから)にアクセスでき、標準のヘッダとSessionデータを取得できますが、リクエストBodyとHTTP応答にアクセスできません.
このようなWebSocketを構築すると、inとoutチャネルに戻らなければなりません. inチャネルはIteratee[A,Unit](Aは情報のタイプであり、ここではStringを用いる)であり、各情報が通知され、ソケットがクライアントで閉じられるとEOFになる. outチャネルは、EOFを送信することによって接続サービス側接続を閉じることができるWebクライアントに送信される情報を生成するEnumerator[A]である.
この例では、コンソールに各情報を印刷する簡単なIterateeを作成します.情報を送信するために、簡単な仮想化を作成しました.
Enumerator、Helloを送信します!情報.
ヒント:locationをws://localhost:9000に設定すれば、https://www.websocket.org/echo.html、WebSocketsをテストします.
入力データを破棄してHelloを送信しましょう.メッセージ後にソケットを閉じる例:
これは別の例である、この例では入力データが標準出力に記録され、Concurrentを用いるブロードキャストされる.broadcastのクライアント:
WebSockets
WebSocketsは、双方向フルデュプレクス通信プロトコルを許可するWebブラウザに基づいて使用できるソケットである.サービス側とクライアントの間にアクティブなWebSocket接続がある限り、クライアントは情報を送信することができ、サービス側はいつでも情報を受信することができる.
新しいHTML 5対応ブラウザ自体はJavaScript WebSocket APIでWebSocketsをサポートしています.しかし、WebSocketsはWebBrowsersによって使用されるだけでなく、サービス側同士の通信を許可したり、ローカルのモバイルアプリケーションがWebSocketsを使用したりするなど、多くのWebSocketクライアントライブラリが使用できます.これらの環境でWebSocketsを使用すると、Playサービス側で使用されているTCPポートを多重化できるメリットがあります.
ヒント:caniuseを表示します.comは、ブラウザがWebSocketsをサポートする既知の問題と詳細について説明します.
WebSocketsの処理
これまで,Actionインスタンスを用いて標準のHTTP要求を処理し,標準のHTTP応答に返信してきた.WebSocketsは全く違うもので、標準的なActionでは処理できません.
Playは、WebSocketsを処理するための2つの異なる組み込みメカニズムを提供する.1つ目はAkka Streams(通常はActor)、2つ目は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))
}
}
気をつけて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.PoisonPill
self ! 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))
}
}
2つのタイプのパラメータがあることに気づいたかもしれませんが、異なるタイプの情報が出力される情報を処理することができます.これは通常、低レベルの構造タイプには役に立たないが、情報を高度なタイプに解析すれば使用できる.
例えば、JSON情報を受信したい場合、入力された情報をInventとして解析し、出力された情報を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.FrameFormatter
implicit 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では、Inventタイプの情報を受信し、OutEventタイプの情報を送信することができます.
Iterateesを使用してWebSocketsを処理する
WebSocketリクエストを処理するために、Actionの代わりにWebSocketを使用しました.
import play.api.mvc._
import play.api.libs.iteratee._
import play.api.libs.concurrent.Execution.Implicits.defaultContext
def 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チャネルに戻らなければなりません.
この例では、コンソールに各情報を印刷する簡単な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.defaultContext
def 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)
}