リアルタイムメッセージング機能¶
概要¶
リアルタイムメッセージング機能は 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 の onReceiveSignaling
と onDataChannelMessage
を利用して以下のように処理します。
Configuration.mediaChannelHandlers.onReceiveSignaling でヘッダーの長さ(バイト数)を取得する
Configuration.mediaChannelHandlers.onDataChannelMessage で受けとったメッセージを先頭からヘッダーの長さ分とそれ以降に分割する
// 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
}