客户端 WebSocket 支持

Client-Side WebSocket Support

Client side WebSocket support is available through Http().singleWebSocketRequestHttp.get(system).singleWebSocketRequest, Http().webSocketClientFlowHttp.get(system).webSocketClientFlow and Http().webSocketClientLayerHttp.get(system).webSocketClientLayer.

客户端 WebSocket 支持通过 Http().singleWebSocketRequestHttp.get(system).singleWebSocketRequestHttp().webSocketClientFlowHttp.get(system).webSocketClientFlowHttp().webSocketClientLayerHttp.get(system).webSocketClientLayer 可获得。

A WebSocket consists of two streams of messages, incoming messages (a SinkSink) and outgoing messages (a SourceSource) where either may be signalled first; or even be the only direction in which messages flow during the lifetime of the connection. Therefore a WebSocket connection is modelled as either something you connect a Flow<Message, Message, Mat>Flow[Message, Message, Mat] to or a Flow<Message, Message, Mat>Flow[Message, Message, Mat] that you connect a Source<Message, Mat>Source[Message, Mat] and a Sink<Message, Mat>Sink[Message, Mat] to.

WebSocket 包含两种消息的流,传入消息(@apidoc[Sink])和传出消息(@apidoc[Source]),其中任何一个都可能先发出信号;或者是在连接的生命周期期间消息流动的唯一方向。 因此,WebSocket 连接被建模为连接 Flow<Message, Message, Mat>Flow[Message, Message, Mat] 到(对象)或者将 Source<Message, Mat>Source[Message, Mat]Sink<Message, Mat>Sink[Message, Mat] 连接到 Flow<Message, Message, Mat>Flow[Message, Message, Mat]

A WebSocket request starts with a regular HTTP request which contains an Upgrade header (and possibly other regular HTTP request properties), so in addition to the flow of messages there also is an initial response from the server, this is modelled with WebSocketUpgradeResponseWebSocketUpgradeResponse.

WebSocket 请求以包含 Upgrade 头域(可能还有其它常规 HTTP 请求属性)的常规 HTTP 请求开始, 因此除了消息流外,还有来自服务器的初始响应: WebSocketUpgradeResponseWebSocketUpgradeResponse

The methods of the WebSocket client API handle the upgrade to WebSocket on connection success and materializes the connected WebSocket stream. If the connection fails, for example with a 404 NotFound error, this regular HTTP result can be found in WebSocketUpgradeResponse.response

WebSocket 客户端 API 的方法在连接成功时处理升级到 WebSocket,并具体化已连接的 WebSocket 流。如果连接失败,例如 404 NotFound 错误, 这个常规 HTTP 结果可以在 WebSocketUpgradeResponse.response 中找到。

Note

Make sure to read and understand the section about Half-Closed WebSockets as the behavior when using WebSockets for one-way communication may not be what you would expect.

确保阅读并理解关于 半-关闭 WebSockets 行为,当使用 WebSocket 进行单向通信时,可能不是你期望的那样。

Message

消息

Messages sent and received over a WebSocket can be either TextMessageTextMessage s or BinaryMessageBinaryMessage s and each of those has two subtypes Strict (all data in one chunk) or Streamed. In typical applications messages will be Strict as WebSockets are usually deployed to communicate using small messages not stream data, the protocol does however allow this (by not marking the first fragment as final, as described in RFC 6455 section 5.2).

在 WebSocket 上面发送和接收消息可以是 TextMessageTextMessageBinaryMessageBinaryMessage ,并且每个都有两种子类型:Strict (所有数据在一个块里)或 Streamd。在典型应用程序中消息是 Strict 的,因为 WebSocket 通常部署为使用小消息而不是流数据进行通信, 但是协议允许这样做(通过不标记第一个片段为最终的,如 RFC 6455,5.2 节 所述)。

The strict text is available from TextMessage.StrictTextMessage.getStrictText and strict binary data from BinaryMessage.StrictBinaryMessage.getStrictData.

严格文本从 TextMessage.StrictTextMessage.getStrictText 获得,严格二进制数据从 BinaryMessage.StrictBinaryMessage.getStrictData 获得。

For streamed messages BinaryMessage.StreamedBinaryMessage.getStreamedData and TextMessage.StreamedTextMessage.getStreamedText will be used. In these cases the data is provided as a Source<ByteString, ?>Source[ByteString, _] for binary and Source<String, ?>Source[String, _] for text messages.

对于流消息,使用 BinaryMessage.StreamedBinaryMessage.getStreamedDataTextMessage.StreamedTextMessage.getStreamedText 。 在这样情况中,数据被提供为 Source<ByteString, ?>Source[ByteString, _] (二进制消息)及 Source<String, ?>Source[String, _] (文本消息)。

singleWebSocketRequest

singleWebSocketRequest takes a WebSocketRequestWebSocketRequest and a flow it will connect to the source and sink of the WebSocket connection. It will trigger the request right away and returns a tuple containing the Future[WebSocketUpgradeResponse]CompletionStage<WebSocketUpgradeResponse> and the materialized value from the flow passed to the method.

signleWebSocketRequest 接受一个 WebSocketRequestWebSocketRequest 和一个连接到 WebSocket 连接的 Source 和 Sink 的 flow。 立刻触发请求并且返回一个元组,其中包含 Future[WebSocketUpgradeResponse]CompletionStage<WebSocketUpgradeResponse> 和传递给方法的 flow 的具体化值。

The future will succeed when the WebSocket connection has been established or the server returned a regular HTTP response, or fail if the connection fails with an exception.

future 将在 WebSocket 连接已建立或服务器返回常规 HTTP 响应时成功,或者如果连接因异常而失败。

Simple example sending a message and printing any incoming message:

发送消息和打印任何传入消息的简单示例:

Scala
import akka.actor.ActorSystem
import akka.{ Done, NotUsed }
import akka.http.scaladsl.Http
import akka.stream.ActorMaterializer
import akka.stream.scaladsl._
import akka.http.scaladsl.model._
import akka.http.scaladsl.model.ws._

import scala.concurrent.Future

object SingleWebSocketRequest {
  def main(args: Array[String]) = {
    implicit val system = ActorSystem()
    implicit val materializer = ActorMaterializer()
    import system.dispatcher

    // print each incoming strict text message
    val printSink: Sink[Message, Future[Done]] =
      Sink.foreach {
        case message: TextMessage.Strict =>
          println(message.text)
      }

    val helloSource: Source[Message, NotUsed] =
      Source.single(TextMessage("hello world!"))

    // the Future[Done] is the materialized value of Sink.foreach
    // and it is completed when the stream completes
    val flow: Flow[Message, Message, Future[Done]] =
      Flow.fromSinkAndSourceMat(printSink, helloSource)(Keep.left)

    // upgradeResponse is a Future[WebSocketUpgradeResponse] that
    // completes or fails when the connection succeeds or fails
    // and closed is a Future[Done] representing the stream completion from above
    val (upgradeResponse, closed) =
      Http().singleWebSocketRequest(WebSocketRequest("ws://echo.websocket.org"), flow)

    val connected = upgradeResponse.map { upgrade =>
      // just like a regular http request we can access response status which is available via upgrade.response.status
      // status code 101 (Switching Protocols) indicates that server support WebSockets
      if (upgrade.response.status == StatusCodes.SwitchingProtocols) {
        Done
      } else {
        throw new RuntimeException(s"Connection failed: ${upgrade.response.status}")
      }
    }

    // in a real application you would not side effect here
    // and handle errors more carefully
    connected.onComplete(println)
    closed.foreach(_ => println("closed"))
  }
}
Java
ActorSystem system = ActorSystem.create();
Materializer materializer = ActorMaterializer.create(system);
Http http = Http.get(system);

// print each incoming text message
// would throw exception on non strict or binary message
final Sink<Message, CompletionStage<Done>> printSink =
  Sink.foreach((message) ->
    System.out.println("Got message: " + message.asTextMessage().getStrictText())
  );

// send this as a message over the WebSocket
final Source<Message, NotUsed> helloSource =
  Source.single(TextMessage.create("hello world"));

// the CompletionStage<Done> is the materialized value of Sink.foreach
// and it is completed when the stream completes
final Flow<Message, Message, CompletionStage<Done>> flow =
  Flow.fromSinkAndSourceMat(printSink, helloSource, Keep.left());

final Pair<CompletionStage<WebSocketUpgradeResponse>, CompletionStage<Done>> pair =
  http.singleWebSocketRequest(
    WebSocketRequest.create("ws://echo.websocket.org"),
    flow,
    materializer
  );

// The first value in the pair is a CompletionStage<WebSocketUpgradeResponse> that
// completes when the WebSocket request has connected successfully (or failed)
final CompletionStage<Done> connected = pair.first().thenApply(upgrade -> {
  // just like a regular http request we can access response status which is available via upgrade.response.status
  // status code 101 (Switching Protocols) indicates that server support WebSockets
  if (upgrade.response().status().equals(StatusCodes.SWITCHING_PROTOCOLS)) {
    return Done.getInstance();
  } else {
    throw new RuntimeException("Connection failed: " + upgrade.response().status());
  }
});

// the second value is the completion of the sink from above
// in other words, it completes when the WebSocket disconnects
final CompletionStage<Done> closed = pair.second();

// in a real application you would not side effect here
// and handle errors more carefully
connected.thenAccept(done -> System.out.println("Connected"));
closed.thenAccept(done -> System.out.println("Connection closed"));

The websocket request may also include additional headers, like in this example, HTTP Basic Auth:

WebSocket 请求也可能包含额外的头域,像这个例子,HTTP 基本认证:

Scala
val (upgradeResponse, _) =
  Http().singleWebSocketRequest(
    WebSocketRequest(
      "ws://example.com:8080/some/path",
      extraHeaders = Seq(Authorization(
        BasicHttpCredentials("johan", "correcthorsebatterystaple")))),
    flow)
Java
http.singleWebSocketRequest(
  WebSocketRequest.create("ws://example.com:8080/some/path")
    .addHeader(Authorization.basic("johan", "correcthorsebatterystaple")),
  flow,
  materializer);

webSocketClientFlow

webSocketClientFlow takes a request, and returns a Flow<Message, Message, Future<WebSocketUpgradeResponse>>Flow[Message, Message, Future[WebSocketUpgradeResponse]]Flow<Message, Message, CompletionStage<WebSocketUpgradeResponse>>Flow[Message, Message, CompletionStage[WebSocketUpgradeResponse]].

webSocketClientFlow 接受一个来请求,并返回一个 Flow<Message, Message, Future<WebSocketUpgradeResponse>>Flow[Message, Message, Future[WebSocketUpgradeResponse]]Flow<Message, Message, CompletionStage<WebSocketUpgradeResponse>>Flow[Message, Message, CompletionStage[WebSocketUpgradeResponse]]

The future that is materialized from the flow will succeed when the WebSocket connection has been established or the server returned a regular HTTP response, or fail if the connection fails with an exception.

当 WebSocket 连接已建立,或服务器返回一个常规 HTTP 响应时,从 flow 具体化的 future 将成功。或如果连接因异常而失败时。

Note

The FlowFlow that is returned by this method can only be materialized once. For each request a new flow must be acquired by calling the method again.

通过该方法返回的 FlowFlow 只能被具体化一次。对于每个请求,必须通过再次调用方法来得到新的 flow。

Simple example sending a message and printing any incoming message:

发送消息和打印任何传入消息的简单示例:

Scala
import akka.actor.ActorSystem
import akka.Done
import akka.http.scaladsl.Http
import akka.stream.ActorMaterializer
import akka.stream.scaladsl._
import akka.http.scaladsl.model._
import akka.http.scaladsl.model.ws._

import scala.concurrent.Future

object WebSocketClientFlow {
  def main(args: Array[String]) = {
    implicit val system = ActorSystem()
    implicit val materializer = ActorMaterializer()
    import system.dispatcher

    // Future[Done] is the materialized value of Sink.foreach,
    // emitted when the stream completes
    val incoming: Sink[Message, Future[Done]] =
      Sink.foreach[Message] {
        case message: TextMessage.Strict =>
          println(message.text)
      }

    // send this as a message over the WebSocket
    val outgoing = Source.single(TextMessage("hello world!"))

    // flow to use (note: not re-usable!)
    val webSocketFlow = Http().webSocketClientFlow(WebSocketRequest("ws://echo.websocket.org"))

    // the materialized value is a tuple with
    // upgradeResponse is a Future[WebSocketUpgradeResponse] that
    // completes or fails when the connection succeeds or fails
    // and closed is a Future[Done] with the stream completion from the incoming sink
    val (upgradeResponse, closed) =
      outgoing
        .viaMat(webSocketFlow)(Keep.right) // keep the materialized Future[WebSocketUpgradeResponse]
        .toMat(incoming)(Keep.both) // also keep the Future[Done]
        .run()

    // just like a regular http request we can access response status which is available via upgrade.response.status
    // status code 101 (Switching Protocols) indicates that server support WebSockets
    val connected = upgradeResponse.flatMap { upgrade =>
      if (upgrade.response.status == StatusCodes.SwitchingProtocols) {
        Future.successful(Done)
      } else {
        throw new RuntimeException(s"Connection failed: ${upgrade.response.status}")
      }
    }

    // in a real application you would not side effect here
    connected.onComplete(println)
    closed.foreach(_ => println("closed"))
  }
}
Java
ActorSystem system = ActorSystem.create();
Materializer materializer = ActorMaterializer.create(system);
Http http = Http.get(system);

// print each incoming text message
// would throw exception on non strict or binary message
Sink<Message, CompletionStage<Done>> printSink =
  Sink.foreach((message) ->
      System.out.println("Got message: " + message.asTextMessage().getStrictText())
  );

// send this as a message over the WebSocket
Source<Message, NotUsed> helloSource =
  Source.single(TextMessage.create("hello world"));


Flow<Message, Message, CompletionStage<WebSocketUpgradeResponse>> webSocketFlow =
  http.webSocketClientFlow(WebSocketRequest.create("ws://echo.websocket.org"));


Pair<CompletionStage<WebSocketUpgradeResponse>, CompletionStage<Done>> pair =
  helloSource.viaMat(webSocketFlow, Keep.right())
    .toMat(printSink, Keep.both())
    .run(materializer);


// The first value in the pair is a CompletionStage<WebSocketUpgradeResponse> that
// completes when the WebSocket request has connected successfully (or failed)
CompletionStage<WebSocketUpgradeResponse> upgradeCompletion = pair.first();

// the second value is the completion of the sink from above
// in other words, it completes when the WebSocket disconnects
CompletionStage<Done> closed = pair.second();

CompletionStage<Done> connected = upgradeCompletion.thenApply(upgrade->
{
  // just like a regular http request we can access response status which is available via upgrade.response.status
  // status code 101 (Switching Protocols) indicates that server support WebSockets
  if (upgrade.response().status().equals(StatusCodes.SWITCHING_PROTOCOLS)) {
    return Done.getInstance();
  } else {
    throw new RuntimeException(("Connection failed: " + upgrade.response().status()));
  }
});

// in a real application you would not side effect here
// and handle errors more carefully
connected.thenAccept(done -> System.out.println("Connected"));
closed.thenAccept(done -> System.out.println("Connection closed"));

webSocketClientLayer

Just like the Stand-Alone HTTP Layer Usage for regular HTTP requests, the WebSocket layer can be used fully detached from the underlying TCP interface. The same scenarios as described for regular HTTP requests apply here.

就像常规 HTTP 请求的 独立的 HTTP 层使用方法 ,WebSocket 层可以从底层 TCP 接口完全分离。 这里适用于常规 HTTP 请求的相同场景。

The returned layer forms a BidiFlow<Message, SslTlsOutbound, SslTlsInbound, Message, Future<WebSocketUpgradeResponse>>BidiFlow[Message, SslTlsOutbound, SslTlsInbound, Message, Future[WebSocketUpgradeResponse]]BidiFlow<Message, SslTlsOutbound, SslTlsInbound, Message, CompletionStage<WebSocketUpgradeResponse>>BidiFlow[Message, SslTlsOutbound, SslTlsInbound, Message, CompletionStage[WebSocketUpgradeResponse]].

返回的层形成一个 BidiFlow<Message, SslTlsOutbound, SslTlsInbound, Message, Future<WebSocketUpgradeResponse>>BidiFlow[Message, SslTlsOutbound, SslTlsInbound, Message, Future[WebSocketUpgradeResponse]] BidiFlow<Message, SslTlsOutbound, SslTlsInbound, Message, CompletionStage<WebSocketUpgradeResponse>>BidiFlow[Message, SslTlsOutbound, SslTlsInbound, Message, CompletionStage[WebSocketUpgradeResponse]]

Half-Closed WebSockets

半-关闭的 WebSocket

The Akka HTTP WebSocket API does not support half-closed connections which means that if either stream completes the entire connection is closed (after a “Closing Handshake” has been exchanged or a timeout of 3 seconds has passed). This may lead to unexpected behavior, for example if we are trying to only consume messages coming from the server, like this:

Akka HTTP WebSocket API 不支持半-关闭连接,这意味着任何流完成则整个连接被关闭(在交换了“关闭握手”或3秒超时后)。 这可能导致异外的行为,例如,如果我们试图只消费来自服务器的消息,像这样:

Scala

// we may expect to be able to to just tail // the server websocket output like this val flow: Flow[Message, Message, NotUsed] = Flow.fromSinkAndSource( Sink.foreach(println), Source.empty) Http().singleWebSocketRequest( WebSocketRequest("ws://example.com:8080/some/path"), flow)
Java

// we may expect to be able to to just tail // the server websocket output like this final Flow<Message, Message, NotUsed> flow = Flow.fromSinkAndSource( Sink.foreach(System.out::println), Source.empty()); http.singleWebSocketRequest( WebSocketRequest.create("ws://example.com:8080/some/path"), flow, materializer);

This will in fact quickly close the connection because of the Source.emptySource.empty() being completed immediately when the stream is materialized. To solve this you can make sure to not complete the outgoing source by using for example Source.maybeSource.maybe() like this:

这事实上将很快关闭连接,因为当具体化流时, Source.emptySource.empty() 将立即完成。要解决这个问题, 你可以通过使用如 Source.maybeSource.maybe() (这样的 Source)来确保不完成传出源,像这样:

Scala

// using Source.maybe materializes into a promise // which will allow us to complete the source later val flow: Flow[Message, Message, Promise[Option[Message]]] = Flow.fromSinkAndSourceMat( Sink.foreach[Message](println), Source.maybe[Message])(Keep.right) val (upgradeResponse, promise) = Http().singleWebSocketRequest( WebSocketRequest("ws://example.com:8080/some/path"), flow) // at some later time we want to disconnect promise.success(None)
Java

// using Source.maybe materializes into a completable future // which will allow us to complete the source later final Flow<Message, Message, CompletableFuture<Optional<Message>>> flow = Flow.fromSinkAndSourceMat( Sink.foreach(System.out::println), Source.maybe(), Keep.right()); final Pair<CompletionStage<WebSocketUpgradeResponse>, CompletableFuture<Optional<Message>>> pair = http.singleWebSocketRequest( WebSocketRequest.create("ws://example.com:8080/some/path"), flow, materializer); // at some later time we want to disconnect pair.second().complete(Optional.empty());

This will keep the outgoing source from completing, but without emitting any elements until the PromiseCompletableFuture is manually completed which makes the SourceSource complete and the connection to close.

这将保持传出源处于正在完成状态,但直到 PromiseCompletableFuture 被手动完成(使 SourceSource 完成并关闭连接)前不会发出任何元素。

The same problem holds true if emitting a finite number of elements, as soon as the last element is reached the SourceSource will close and cause the connection to close. To avoid that you can concatenate Source.maybeSource.maybe() to the finite stream:

如果发出有限数量的元素相同问题也成立,一旦到达最后个元素, SourceSource 就将关闭并导致连接关闭。要避免这种行为, 你可以将 Source.maybeSource.maybe() 连接到有限流:

Scala

// using emit "one" and "two" and then keep the connection open val flow: Flow[Message, Message, Promise[Option[Message]]] = Flow.fromSinkAndSourceMat( Sink.foreach[Message](println), Source(List(TextMessage("one"), TextMessage("two"))) .concatMat(Source.maybe[Message])(Keep.right))(Keep.right) val (upgradeResponse, promise) = Http().singleWebSocketRequest( WebSocketRequest("ws://example.com:8080/some/path"), flow) // at some later time we want to disconnect promise.success(None)
Java

// emit "one" and then "two" and then keep the source from completing final Source<Message, CompletableFuture<Optional<Message>>> source = Source.from(Arrays.<Message>asList(TextMessage.create("one"), TextMessage.create("two"))) .concatMat(Source.maybe(), Keep.right()); final Flow<Message, Message, CompletableFuture<Optional<Message>>> flow = Flow.fromSinkAndSourceMat( Sink.foreach(System.out::println), source, Keep.right()); final Pair<CompletionStage<WebSocketUpgradeResponse>, CompletableFuture<Optional<Message>>> pair = http.singleWebSocketRequest( WebSocketRequest.create("ws://example.com:8080/some/path"), flow, materializer); // at some later time we want to disconnect pair.second().complete(Optional.empty());

Scenarios that exist with the two streams in a WebSocket and possible ways to deal with it:

Scenario Possible solution
Two-way communication Flow.fromSinkAndSource, or Flow.map for a request-response protocol
Infinite incoming stream, no outgoing Flow.fromSinkAndSource(someSink, Source.maybe)Flow.fromSinkAndSource(someSink, Source.maybe())
Infinite outgoing stream, no incoming Flow.fromSinkAndSource(Sink.ignore, yourSource)Flow.fromSinkAndSource(Sink.ignore(), yourSource)

在 WebSocket 中存在两种流的场景,以及可能的处理它们的方式:

场景 可能解决方案
双向通信 Flow.fromSinkAndSourceFlow.map 用于请求-响应协议
无限传入流,无传出 Flow.fromSinkAndSource(someSink, Source.maybe)Flow.fromSinkAndSource(someSink, Source.maybe())
无限传出流,无传入 Flow.fromSinkAndSource(Sink.ignore, yourSource)Flow.fromSinkAndSource(Sink.ignore(), yourSource)

Automatic keep-alive Ping support

自动保持-存活 Ping 支持

Similar to the server-side kee-alive Ping support, it is possible to configure the client-side to perform automatic keep-alive using Ping (or Pong) frames.

类似于 服务器端保持存活 Ping 支持,配置客户端使用 Ping(或 Pong)帧执行自动保持-存活是可能的。

This is supported in a transparent way via configuration by setting the: akka.http.client.websocket.periodic-keep-alive-max-idle = 1 second to a specified max idle timeout. The keep alive triggers when no other messages are in-flight during the such configured period. Akka HTTP will then automatically send a Ping frame for each of such idle intervals.

这个支持通过配置以透明的方式设置:akka.http.client.websocket.periodic-keep-alive-max-idle = 1 second 指定最大空闲超时。 当在此配置的超时期间没有其它消息活跃,则触发保持存活。Akka HTTP 将在每个空闲间隔自动发送一个 Ping

By default, the automatic keep-alive feature is disabled.

默认,禁用自动保持-存活特性。

Custom keep-alive data payloads

定制保持-存活数据载荷

By default, pings do not carry any payload, as it is often enough to simply push any frame over the connection to ensure the connection stays healthy (or detect if it was severed), however you may configure them to carry a custom payload, to do this you can provide a function that will be asked to emit the payload for each of the ping messages generated:

默认,ping 不携带任何载荷,因为这通常足以在连接上简单的推送 任何 帧来确保连接保持健康(或者检测连接是否断开)。 但是,你可以配置它携带一个自定义载花,为此你可以提供一个函数,要求该函数为生成的每个 ping 消息发出载荷。

Scala
val defaultSettings = ClientConnectionSettings(system)

val pingCounter = new AtomicInteger()
val customWebsocketSettings =
  defaultSettings.websocketSettings
    .withPeriodicKeepAliveData(() => ByteString(s"debug-${pingCounter.incrementAndGet()}"))

val customSettings =
  defaultSettings.withWebsocketSettings(customWebsocketSettings)

val request = WebSocketRequest("ws://127.0.0.1")

Http().singleWebSocketRequest(
  request,
  Flow[Message],
  Http().defaultClientHttpsContext,
  None,
  customSettings,
  system.log
)
Java
ClientConnectionSettings defaultSettings = ClientConnectionSettings.create(system);

AtomicInteger pingCounter = new AtomicInteger();

WebSocketSettings customWebsocketSettings = defaultSettings.getWebsocketSettings()
    .withPeriodicKeepAliveData(() ->
        ByteString.fromString(String.format("debug-%d", pingCounter.incrementAndGet()))
    );

ClientConnectionSettings customSettings =
    defaultSettings.withWebsocketSettings(customWebsocketSettings);

Http http = Http.get(system);
http.singleWebSocketRequest(
    WebSocketRequest.create("ws://127.0.0.1"),
    clientFlow,
    ConnectionContext.noEncryption(),
    Optional.empty(),
    customSettings,
    system.log(),
    materializer
);

Uni-directional Pong keep-alive

单向 Pong 保持-存活

A Ping response will always be replied to by the client-side with an appropriate Pong reply, carrying the same payload. It is also possible to configure the keep-alive mechanism to send Pong frames instead of Ping frames, which enables an uni-directional heartbeat mechanism (in which case the client side will not reply to such heartbeat). You can configure this mode by setting: akka.http.client.websocket.periodic-keep-alive-mode = pong.

客户端将始终使用一个合适的 Pong 回复一个 Ping 响应,并携带相同的载荷。 也可以通过配置保持-活跃机制发送 Pong 帧来替代 Ping 帧。 你可以配置这个模式:akka.http.server.websocket.periodic-keep-alive-mode = pong 来启用 定向心跳 机制 (在这种情况下,客户端将 回复此类心跳)。

在此文档中发现错误?该页面的源代码可以在 这里 找到。欢迎随时编辑并提交 Pull Request。