リアルタイムメッセージング機能

概要

リアルタイムメッセージング機能は WebRTC の DataChannel を利用して、データの送受信を行う機能です。

詳細は Sora ドキュメント リアルタイムメッセージング機能 を参照してください。 また、サンプルアプリケーション を用意していますのでこちらも参考にしてください。

メッセージを送受信する

Sora 接続時にリアルタイムメッセージング用 DataChannel を設定する

Sora の 接続時に Configuration.dataChannels に リアルタイムメッセージング用 DataChannel を指定できます。 JSONSerialization で JSON に変換可能な形式で指定します。

// 接続の設定
var config = Configuration(url: soraURL,
                           channelId: soraChannelId,
                           role: role,
                           multistreamEnabled: true)

// DataChannel 経由のシグナリングを有効にする
config.dataChannelSignaling = true

// リアルタイムメッセージング機能に利用する DataChannel を指定する
config.dataChannels = [[
          "label": "#spam",
          "direction": "sendrecv",
      ], [
          "label": "#egg",
          "max_retransmits": 0,
          "ordered": false,
          "protocol": "abc",
          "compress": false,
          "direction": "recvonly",
          "header": [
              ["type": "sender_connection_id"]
          ],
]]

メッセージを受信する

Configuration.mediaChannelHandlers.onDataChannelMessage を利用してメッセージの受信を行います。バイナリデータ (Data 型) での受信になりますので適宜必要な型に変換してください。

// メッセージ受信時の処理をハンドラーに定義する
config.mediaChannelHandlers.onDataChannelMessage = { (mediaChannel, label, data) in
  // label をチェックする
  guard label.starts(with: "#spam") else {
    return
  }
  ...
}

受信したメッセージをヘッダーとメッセージに分割する

リアルタイムメッセージングにヘッダーが追加されている場合、onDataChannelMessage で受け取るデータにはヘッダーとメッセージが結合された状態で入っています。 これを分離するには Configuration.mediaChannelHandlers の onReceiveSignalingonDataChannelMessage を利用して以下のように処理します。

// dataChannels の label ごとにヘッダーの長さを保持するための変数
var headerLengths: [String: Int] = [:]

// offer で入ってくる data channel header の長さを取得する
config.mediaChannelHandlers.onReceiveSignaling = { [weak self] signaling in
    guard let self else {
        return
    }
    switch signaling {
    case let .offer(offer):
        guard let dataChannels = offer.dataChannels else {
            return
        }
        for dataChannel in dataChannels {
            // ラベルが "#" で始まる場合のみ処理する
            let label: String = dataChannel["label"] as! String
            guard label.starts(with: "#") else {
                continue
            }

            // dataChannel["header"] が nil ではない場合のみ後続処理を行う
            guard let headers = dataChannel["header"] as? [[String: Any]] else {
                continue
            }
            for header in headers {
                if header["type"] as! String == "sender_connection_id" {
                    let length = header["length"] as! Double
                    headerLengths[label] = Int(length)
                    print("kensaku: \(label) \(Int(length))")
                }
            }
        }
    default:
        break
    }
}

// メッセージ受信時の処理を定義する
config.mediaChannelHandlers.onDataChannelMessage = { [weak self] _, label, data in
    guard let self = self else {
        return
    }

    // "#" で始まるラベル以外は無視する
    guard label.starts(with: "#") else {
        return
    }

    // ヘッダーの長さを取得し、ヘッダーとメッセージを分離する
    let headerLength = self.headerLengths[label] ?? 0
    if headerLength == 0 {
        print(String(data: data, encoding: .utf8) ?? data.map(\.description).joined(separator: ", "))
        return
    }
    let header = data.prefix(headerLength)
    let message = data.suffix(from: headerLength)
    ...
}

メッセージを送信する

MediaChannel.sendMessage を利用してメッセージの送信を行います。バイナリデータ (Data 型) の送信のみ可能です。文字列を送信したい場合はバイナリデータに変換してください。

// メッセージを送信する
// type: switched を受信した後からメッセージが送信できる
let error = mediaChannel.sendMessage(label: "#spam", data: "egg".data(using: .utf8)!)
guard error == nil else {
    // エラー処理
    ...
    return
}
© Copyright 2018-2024, Shiguredo Inc. Created using Sphinx 8.1.3