

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 應用程式與 Web 用戶端之間的資料通道通訊
<a name="data-channels"></a>

 資料通道可讓您在 Amazon GameLift Streams 應用程式與 Web 用戶端 （在最終使用者的 Web 瀏覽器中執行的 JavaScript 程式碼） 之間安全地通訊任意訊息。這可讓最終使用者透過檢視串流的 Web 瀏覽器，與 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 串流*一節[資料通道](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. 連接至資料通道連接埠
<a name="data-channels-using-application-1"></a>

當您的應用程式啟動時，請連線至 `40712`上的連接埠`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)
```