

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 应用程序和 Web 客户端之间的数据通道通信
<a name="data-channels"></a>

 数据通道允许您在 Amazon GameLift Streams 应用程序和 Web 客户端（在最终用户的网络浏览器中运行的 JavaScript 代码）之间安全地传输任意消息。这允许最终用户通过观看直播的网络浏览器与 Amazon GameLift Streams 正在流式传输的应用程序进行交互。

以下是 Amazon GameLift Streams 中数据通道的一些示例用例：
+ 用户可以在其本地浏览器 URLs 中打开应用程序。
+ 用户可以将剪贴板中的内容来回传递给应用程序。
+ 用户可以将内容从本地计算机上传到应用程序。
+ 开发人员可以在向应用程序发送命令的浏览器中实现 UI。
+ 用户可以传递架构来控制可视化图层的显示。

## 特征
<a name="data-channels-features"></a>

**邮件大小限制**  
Amazon GameLift Streams Web SDK 对每条消息的最大大小限制为 64 KB（65536 字节）。这样可以确保消息大小限制与大多数浏览器兼容，并且通信对流总带宽的影响很小。

**指标**  
 直播会话结束时，有关您的数据通道使用情况的指标将发送到您的 AWS 账户。有关更多信息，请参阅 “*监控 Amazon GameLift Streams*” 一节中的。[数据渠道](monitoring-cloudwatch.md#monitoring-data-channels)

## 使用数据通道
<a name="data-channels-using"></a>

Amazon GameLift Streams Web SDK 提供了将消息作为字节数组发送到应用程序的`sendApplicationMessage`功能。消息由您定义的回调函数处理。`clientConnection.applicationMessage`

如果客户端在应用程序连接到数据通道端口之前发送消息，则消息将排队。然后，当应用程序连接时，它会收到消息。但是，如果应用程序在客户端连接到数据通道端口之前发送消息，则消息将丢失。在发送消息之前，应用程序必须检查客户端的连接状态。

## 在客户端
<a name="data-channels-using-client"></a>

在 Web 客户端应用程序中编写以下代码。

1.  定义回调函数以接收来自应用程序的传入消息。

   ```
   function streamApplicationMessageCallback(message) {
       console.log('Received ' + message.length + ' bytes of message from 
       Application');
   }
   ```

1.  设置`clientConnection.applicationMessage`为你的回调函数。

   ```
   clientConnection: {
       connectionState: streamConnectionStateCallback,
       channelError: streamChannelErrorCallback,
       serverDisconnect: streamServerDisconnectCallback,
       applicationMessage: streamApplicationMessageCallback,
   }
   ```

1.  调用该`GameLiftStreams.sendApplicationMessage`函数向您的应用程序发送消息。只要直播会话处于活动状态并且附加了输入，您就可以随时调用此方法。

例如，请参阅 Amazon GameLift Streams Web SDK 示例客户端，该客户端演示了如何在客户端设置简单的数据通道。

## 在应用程序方面
<a name="data-channels-using-application"></a>

在您的应用程序中编写以下逻辑。

### 第 1 步：Connect 连接到数据信道端口
<a name="data-channels-using-application-1"></a>

当您的应用程序启动时，连接到 `40712` on 端口`localhost`。您的应用程序应在整个执行期间保持此连接。如果应用程序关闭连接，则无法重新打开连接。

### 第 2 步：收听活动
<a name="data-channels-using-application-2"></a>

事件以固定大小的标头开头，后面是可变长度的关联数据。当您的应用程序收到事件时，请解析该事件以检索信息。

**事件格式**
+ **标题**：格式为 4 字节的标题 `abcc`
  +  `a`: 客户端 ID 字节。如果有多个连接（由于断开连接和重新连接），这将标识特定的客户端连接。
  +  `b`: 事件类型字节。 `0`-客户端已连接，`1`-客户端已断开连接，`2`-客户端发送消息。将来的 Amazon GameLift Streams 服务更新可能会收到其他类型的事件，因此应将其忽略。
  +  `cc`：关联事件数据的长度。这表示为 2 个字节，按大端顺序排列（第一个字节最重要）。如果事件类型为 2，则事件数据表示来自客户端的消息内容。
+ **数据**：其余字节包含事件数据，例如客户端消息。标题中用表示`cc`数据的长度。

**监听事件**

1. 读取四个标头字节以检索客户端 ID、事件类型和事件数据的长度。

1. 根据标题中描述的长度读取可变长度的事件数据，无论客户端 ID 和事件类型如何。无条件读取数据很重要，这样事件数据就永远不会留在缓冲区中，因为缓冲区可能会与下一个事件标头混淆。不要根据事件类型对数据长度做出假设。

1. 如果您的应用程序可以识别，则根据事件类型采取适当的措施。此操作可能包括记录传入的连接或断开连接，或者解析客户端消息和触发应用程序逻辑。

### 第 3 步：向客户端传输消息
<a name="data-channels-using-application-3"></a>

应用程序应使用与传入事件相同的四字节标头格式来传输消息。

**向客户端传送消息**

1. 使用以下属性编写标题：

   1. `a`: 客户端 ID 字节。如果您的消息是对客户端消息的响应，则它应重复使用与传入的客户端消息相同的客户端 ID，以避免竞争条件，例如将来自旧客户端连接的响应传送到新重新连接的客户端。如果您的应用程序向客户端发送未经请求的消息，则应将客户端 ID 设置为与最近的 “客户端连接” 事件（事件类型 0）相匹配。

   1. `b`：传出消息的事件类型必须始终为 2。客户端会忽略其他事件类型的消息。

   1. `cc`：消息的长度，以字节为单位。

1. 写入消息字节。

除非客户端断开连接，否则该消息将传送到指定的客户端。当断开连接的客户端重新连接时，将通过连接客户端的事件分配新的客户端 ID。所有未传送的旧客户端 ID 的消息都将被丢弃。

**Example**  
以下伪代码演示了在应用程序端传送消息的逻辑。有关使用 Winsock 的完整示例，请参阅 Windows Sockets 2 [文档中的完整 Winsock 客户端代码](https://learn.microsoft.com/en-us/windows/win32/winsock/complete-client-code)。  

```
connection = connect_to_tcp_socket("localhost:40712")
loop:
    while has_pending_bytes(connection):
        client_id = read_unsigned_byte(connection)
        event_type = read_unsigned_byte(connection)
        event_length = 256 * read_unsigned_byte(connection)
        event_length = event_length + read_unsigned_byte(connection)
        event_data = read_raw_bytes(connection, event_length)
        if message_type == 0:
            app_process_client_connected(client_id)
        else if message_type == 1:
            app_process_client_disconnected(client_id)
        else if message_type == 2:
            app_process_client_message(client_id, event_data)
        else:
            log("ignoring unrecognized event type")
    while app_has_outgoing_messages():
        target_client_id, message_bytes = app_next_outgoing_message()
        message_length = length(message_bytes)
        write_unsigned_byte(connection, target_client_id)
        write_unsigned_byte(connection, 2)
        write_unsigned_byte(connection, message_length / 256)
        write_unsigned_byte(connection, message_length mod 256)
        write_raw_bytes(connection, message_bytes)
```