ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
ネットワーキングの最新情報(パート2)
Bonjourの強化点、カスタムメッセージのフレーミングハンドラ、セキュリティの最新機能を活用して、ネットワーキングAppを次のレベルに高めましょう。このセッションでは、指標を収集することでネットワーキングのパフォーマンスを把握する方法と、Appleプラットフォームで最新のネットワーキングフレームワークを使用する最善の方法についても紹介します。
リソース
- Building a custom peer-to-peer protocol
- Collecting Network Connection Metrics
- Network
- URLSession
- プレゼンテーションスライド(PDF)
関連ビデオ
WWDC22
WWDC21
WWDC19
-
ダウンロード
(音楽) (拍手) “Advances in Networking, Part 2”へ ようこそ Part 1を見逃した方は 間もなくWebで配信されます Internet Technologiesの エリック・キニアです 今日は 同僚のトミーと スチュアートも登場します
内容は盛りだくさんです Bonjourブラウジングの新機能の 説明から始めます フレーム・プロトコルによる 効率的で簡単なメッセージ転送 改良されたメトリック収集 そして最後に最新情報と― ベストプラクティスを紹介します その前に注意事項です URLSessionとNetwork.frameworkを お使いの方は― 今日の内容をすべて活用できます そうでない方は 新しいAPIに 切り替えることをおすすめします
ではBonjourブラウジングへ
Bonjourは ネットワーク上のサービス検索機能です AirPrintでプリントする時 機内モードのデバイスにつなぐ時 HomeKitを使う時 IPアドレスやホスト名を入力せずに 接続する時に使われます BonjourはどのAppleプラットフォーム でも使えます Linux Android Chrome OSでも使えます Chromecastも検索に使います Microsoftも2015年からWindows 10で Bonjourをサポートしています つまり現在では― あらゆる 主要プラットフォームで使えます
今日は新たな機能強化を紹介します 使用可能なデバイスを今とは別の ネットワークで検索したい場合 複数ホップ先のサブネットで― 使いたいプリンターが見つかります マルチキャストパケットを ローカルで送りますが― 何も応答はありません これを探索プロキシが解決します ユニキャストパケットを 探索プロキシに送ると― プロキシがマルチキャストパケットを サブネットに送り その応答を返します これで直接 プリンターと接続できます (拍手) クライアント側のコードが デベロッパシード版に含まれており server実装と設定の説明が GitHubで入手可能です
皆さんのAppへの影響は? これまで ブラウジングする時 ドメインはnilを推奨してきました これは今後も ほぼすべての場合に推奨されます これからは 以前よりも大きな影響が現れます localを指定すると― あらゆるリモートやプロキシサービスを 見つけられません それは避けたいでしょう よってドメインの指定は ダブルチェックしましょう ではSwift内でも Bonjourのブラウジングを楽にする― Network.frameworkの新しい機能を 紹介しましょう 去年はNWListenerとNWConnectionを 紹介しました NWListenerに Bonjourサービスを広告させたり― NWConnectionを エンドポイントにつないだりできます しかし利用可能なサービスの探索に 他のAPIを使う必要があったり― 見つけてもエンドポイントに 変換する必要がありました しかし本日― NWBrowserオブジェクトによる ネイティブサポートを発表します
広告から探索 接続まで Browserが Network.frameworkオブジェクトで― コネクションとリスナーをつなぎます
Swiftに最適化された 最新の ディスパッチベースのAPIを使って― ネイティブサービス探索を提供します TXTレコードのサポートも含みます Appに必要ならエンドポイント全てに TXTレコードを求められます
使い方を見てみましょう 探索したい Bonjourサービスタイプと― ブラウズ方法を示すNWParameterを使い initを実行します 他のオブジェクトと同様です 次にbrowseResults ChangedHandlerを設定 利用可能なエンドポイントの リストを届けます そしてコールバックを受け取りたい ディスパッチキューで― ブラウザを開始します
もっと詳しく見てみましょう 選択肢は2つ 1つはアップデートによる変更の詳細な リストを受け取るハンドラの提供です 低レベルAPIと協調して あらゆる変更を見ることができます エンドポイントの 追加と削除が可能で― 変更された詳細の情報も持ちます 変更はフラグで示されます この場合― 追加や削除される インターフェイスもチェックします
また 最新の探索結果のみを見る ハンドラを― 提供することもできます エンドポイントのリストが変わるたび ハンドラは呼ばれるのでご注意を Appを適切にアップデートしましょう
NWBrowserの実例を見ましょう サービス探索と2デバイス間で 接続を確保するAppを作成します 三目並べのゲームです 大抵のものに応用できます NWListenerで対戦を広告し― NWBrowserで対戦をブラウズします そして参加する対戦を選んだら ブラウズ結果をNWConnectionに渡し― リスナーと接続します
Xcodeで見ましょう
こちらがAppです 対戦相手募集のディスプレイなど― 必要なコードは すでにこちらで書いてあります ブラウザに集中しましょう
PeerBrowserというクラスで― NWBrowserを管理します PeerBrowserDelegateの 提供によって― 探索したエンドポイントのリストが UIに表示可能になります
まずNWBrowserを インスタンスプロパティとして追加
PeerBrowserの initを実行すると― startBrowsingを呼びます 書き込みましょう
まずNWParameterをいくつか作り― ネットワークと どのようにやり取りしたいか示します 今回はデフォルトで結構です ただincludePeerToPeerをtrueに 同じネットワーク上にないデバイスでも 対戦を探索できます
次にNWBrowserを作成し tictactoe. tcpをブラウズします ドメインはnilを指定します 先ほど保存したパラメータを 使用します
次にstateUpdateHandlerを設定し ブラウザ状況やエラー等の アップデートを受け取ります
そしてbrowseResults ChangedHandlerを設定 シンプルです 結果のリストをUIに表示させるために デリゲートに渡します 最新リストを示すため 毎回 UIをリフレッシュするよう― デリゲートをコーディングします
最後に メインキューでブラウザを起動します
以上です これでNWBrowserに 潜在的に P2Pリンクも通じて対戦を探索させ― 利用可能な対戦リストを ユーザに表示します
これらのコードはサイトで サンプルとしてダウンロードできます 試す前にもう1つあります NWParameterは リスナーとコネクションに使います 他者があるプレーヤーの動きを見たり 設定を変更しないよう― デバイス間のコネクションの安全を 確保したいと考えます NWParameterの拡張の定義と― convenience initializerの作成で 安全を確保 パスコードはString ゲームのホストにパスコードを表示し 参加者にそれを打ち込むよう求めます それにより事前共有鍵を得て TOSでコネクションを確保します
このために TCPとTLSのオプションを作成します まずはTLSから
TLSを作成する関数を定義し― パスコードに渡します 今はデフォルトのTLSオプションです
次に新しい CryptoKitを使って― 認証鍵とコードをパスコードから得ます
事前共有鍵をプロトコルに追加し― 事前共有鍵をサポートする TLS暗号スイートを追加します
tlsOptionsを返します 多くの場合 デフォルトで結構ですが―
enableKeepaliveにもしておきます NWParameterを 先ほど下で作ったTLSと― TCPオプションでinitを実行
そして最後にここに― includePeerToPeerを 設定します コネクションとリスナーが― 違うネットワーク上でも近くの デバイスに接続可能になります
では試してみましょう
2つのデバイスでAppを走らせてます ゲームホストのUIです ブラウジングが始まり― 何もないので検索中と表示しています
名前を打ち込み ホストゲームをタップすると― パスコードがもらえます browseResultsChangedHandlerが呼ばれ リストが出ます この場合は私がホストするゲームで ユーザに対して表示されます 簡単です
参加をタップすると パスコードの打ち込みが求められます
承認すると 事前承認鍵が作成され リスナーと― 接続されたのが分かります そしてゲームが始まります できました (拍手)
これまでAppの序盤を構築し― 2つのデバイスの 接続を確立しました NWListenerで tictactoe. tcpを広告しました NWBrowserで ゲームをブラウズし表示しました そしてブラウザの結果を 直接 NWConnectionに渡し― リスナーと接続し 安全なコネクションを確保しました もちろん 対戦するにはやり取りが必要です ゲーム状況や プレーヤーの動きを伝えるなどです そこでトミーを呼び カスタム フレーミングプロトコルの説明を (拍手)
ありがとう エリック 今日はコネクションを拡張する― 新しい方法を紹介します 他のプロトコルと同じスレッドで 走るカスタムフレーミングプロトコルを 使ったものです 先ほどのゲームを完成するには― お互いにコマンドを送り合う方法が 必要になります 片方の動きを相手に伝える方法です これにはプロトコルが必要です
こんなプロトコルです 単純なtype-length-value またはTLVプロトコルです この4バイトのタイプは― “動き”などを示します プレーヤーが三目並べで ボード上のどこかに印を置くかなどです 次の4バイトは メッセージの長さを示します
次にメッセージのボディです 例えば“サルの顔を 1行目2列目に置く”などです それをTLSバイトストリームで繰り返す
このプロトコルでは構築されていない TLSバイトストリーム上でも― 構築されたメッセージを使っています Appはバイトストリームでなく― よく整理された情報で考えています ほぼ全てのネットワークAppも同様です ヘッダとボディがあるか― デリミターがあり メッセージの境を定義します
しかし例えばソケットのような 従来のAPIは― コネクションで 簡単にメッセージを読めませんでした App内で全て 処理する必要がありました そこでこの問題を把握するため― あなたのAppと ネットワークスタックの関係を見ます 上があなたのAppで― APIを通じて ネットワークスタックと交信しています Network.frameworkでは― TLSとTCPが App内において 1つの共有スレッドで走っています 去年 紹介した ユーザ空間ネットワーキングです バイトストリーム上でAppが メッセージをどう読むか見ましょう
三目並べに使っているような プロトコルなら― 固定長ヘッダかもしれません ここでは単に ヘッダの長さを読むことができます 8バイトを受け取る フルヘッダが来れば スタックに呼び戻されます メッセージの長さが分かるので それを読みます そして行き来し ヘッダとボディを交互に読みます
ただこれだと 何度も行き来しなくてはなりません メッセージごとに最低2回です 例えばより複雑なプロトコルで― 可変長のヘッダか デリミターがあると― より非効率になります 書くのは単純であってもです
効率を選ぶなら他の選択肢があります 1度にできる限り受け取る方法です でも今度は他に問題があります 1度に完全なメッセージを 受け取れない場合があることです あるいは複数受け取る場合や― ヘッダの一部を受け取る場合も 2~3バイトを保存し― 再構築しなくてはなりません あらゆるエッジケースに対処し 正しく処理するのは大変です そして実際に使用して初めて バグが起きることもよくあります
だからあまり見通しはよくない どう長所を生かすか? そして 効率的かつ構成可能で― シンプルなコードにするか? うれしいことに iOS 13とmacOS Catalinaでは― 同じネットワーキングスレッドで 自分のプロトコルコードを書けます かつてない前進です トランスポートネットワークAPI内で メッセージを定義する柔軟性において (拍手) ありがとう
すべてNWConnection内でできます Appにとっては普通のコネクションでの データグラムの読み書きと変わりません
さて どうなるでしょう? Appはまだ送受信しますが― TLSとTCPと同じスレッドで フレーミングコードを走らせられます receiveMessageを呼び出し― 1度のコールバックで 完全なメッセージが得られます 何度でもできます 1メッセージにつき1コールで― デバッグや状況把握も簡単です
これはすばらしい フレーミングプロトコルとして 何を実装できるか? 制限は何か? アプリケーションデータの変換を カプセル化 コード化するものは― ほぼ何でも書けます アプリケーションデータに 対応しないメッセージも送れます ハンドシェイクや キープアライブの実装などです
実装するプロトコルは IETFの標準プロトコルでもいいし― 三目並べのように カスタム化されたものでも構いません プロトコルの構築は2ステップ まずメッセージフレーミングを 定義する再利用可能コードを実装します これがプロトコルです 次に コネクション形成や メッセージの送受信に使えるよう― プロトコルをコネクションの プロトコルスタックに追加します
まずフレーミングプロトコルの 実装からはじめましょう ここでは ProtocolFramerImplementationに 準拠するクラスを作成します クラス内ではいろいろできます でも大事なのは2つ メッセージを送るhandleOutputと メッセージを解析するhandleInputです これができれば合格です
コードを見ましょう MyProtocolが― ProtocolFramerImplementationに 準拠します まずdefinitionオブジェクトの 作成を推奨します Appを通じて使える プロトコルのハンドルであり― コネクションに追加できます
次に多くの基本的な コールバックイベントに対処します 最も重要なものの1つがstartです プロトコルが コネクションにロードされるたびに― startが呼ばれます 何かとハンドシェイクが必要な場合は ここに実装できます あるいはシンプルなプロトコルで 設定が不要なら― ただreadyと書きます これが済めば handleOutputとhandleInputです 見てみましょう
これがhandleOutputです Appがメッセージを送るたび handleOutputで呼ばれます メッセージのメタデータと 必要ならカスタム値が与えられます Appが送ろうとしている メッセージの長さもです 今回のような ヘッダとボディのプロトコルなら― まずヘッダを構築しデータを シリアライズするといいでしょう メタデータから得られるタイプや― handleOutputで渡された長さが 含まれます
その後writeOutputを呼び― バイトがアウトプットストリームに 加えられます でもまだ送られません
次にボディを書きます この例ではデータ変換は不要です writeOutputNoCopyと書きます これにより Appバイトをストリームにキューします handleOutputから戻ると― 全バイトがコネクションに送られます
handleInputに進みましょう handleInputも 似てますが 少しだけ複雑です コネクション上で Appが新しいバイトを受け取るたびに handleInputで呼ばれます ヘッダとボディのプロトコルなら 仕事は2つ ヘッダの解析と ボディの解析が必要です ヘッダの解析から始めましょう このプロトコルでは固定長ヘッダで 8バイトです parseInputを呼び バイトのストリームを調べ始めます ヘッダを見たいので― 最小も最大も8バイトで呼びます
成功すればブロックに呼ばれ 実際にバイトのバッファを見て― 値を解析し 必要ならローカル変数に保存します
インプット解析の戻り値は― input cursorを 何バイト増やすかを示します 8バイトは調べ終わり― “もう必要ない 次へ進もう”と指示します
8バイトが まだ残っている場合も対処できます その場合 parseInput関数が失敗し さらにバイトが得られるまで待てます handleInputの戻り値は― 次に移るのに必要なバイト数を示します この場合は“8バイト確認後 知らせよ”ということです
ヘッダを読むのに成功すれば― データと共にAppに届けられる メッセージオブジェクトを作れます これにより任意のカスタム値やタイプ その他の識別子をAppに送れます
最後にdeliverInput またはdeliverInputNoCopyを呼びます これにより次のバイトを― 直接Appに届けられるようになります
そうするとブール値が戻ります すべてのバイトが入手可能で 無事 送られたか― またはコネクションを待機させるか 示すためにです よってギガバイトの長さのメッセージを deliverInputすれば― 1つのメッセージとして送られます すべてのバイトが届くのを待ったり 自分で対処する必要はありません
さて それでは三目並べのプロトコルを 実装してみましょう
さて
よし 先ほどのエリックの続きです GameProtocolという 新しいクラスを作ります これはProtocolFramer Implementationに準拠します このゲームのために2つの異なる タイプを定義しました 2種類のコマンドを送りたい 1つはキャラ選択 プレーヤーに 絵文字ファミリーを選ばせます サルか鳥か? キャラを選んだら 動きを送れるようになります ボディは長くなります キャラと行と列の値です
さて プロトコルの実装でまず必要なのは―
定義の作成です このハンドルでオブジェクトを― システムに登録し コネクションで使えるようになります
次に全ての コールバックに対処します ハンドシェイクは不要なので― startで呼ばれたら readyのStartResultを返します
次にメッセージの送信とカプセル化に 対処します
ここでhandleOutputの実装を定義します
ヘッダは8バイトで タイプと長さを含むので まずタイプを識別します これはAppのメッセージから得られます 後で出てきます
私のenumタイプを抽出できるよう メッセージに― カスタム拡張を作成しました キャラ選択か動きかを知るためです タイプが分かれば構造体とヘッダを― handleOutputで渡されたタイプと 長さと共にインスタンス作成します そのデータを こちらのコードでエンコードし― 8バイトレンジのバイトにします アウトプットストリームでキュー するためwriteOutputを呼び出します
ヘッダを書いたら 最後にボディを書きます writeOutputNoCopyを呼んで 次の一連のバイトはボディだと示します これが戻ればバイトは送られ― さらにメッセージの送受信ができます
さて 書いたら次は読みます handleInputです
まずヘッダを読んで解析したいと 思います ヘッダの長さは固定で8バイト 最小8最大8で解析します いつでも 8バイトが利用可能な時は― バッファで呼ばれます
バッファは有効と確認できます 構造体を作成し 8バイトのタイプと長さを解析します
成功すれば― インプットをインクリメントし これらのバイトを消費したと示します 完了しました
すべての8バイトを解析できなかった 場合にも対処する必要があります 5バイトしかない時などです 解析に失敗し handleInputから戻って― 次に進む前に 8バイトを待つ必要があると示します
でもここを過ぎれば― 残りのAppデータを届けるために 有効なヘッダを使用可能です
次にメッセージオブジェクトを作ります オブジェクト内に 私のメッセージタイプを保存します
そして最後に―
deliverInputNoCopyを呼びます 長さに基づき解析したAppデータに なるバイトをコネクションに伝え― Appがそれを メッセージと共に受け取ります 以上が私のプロトコルです 次にこれをどうやって ゲームコネクションに入れるか学びます
(拍手) どうも
幸い このパートは簡単です コネクションにプロトコルを追加して 再利用するには― 先ほどの定義から プロトコルオプションを作成します プロトコルオプションとは プロトコルスタックの元です TCPやTLSがあり カスタム プロトコルオプションもあります コネクションに対して パラメータを作成する時― TLSを使って安全を確保しましょう プロトコルスタックでTLSと共に― プロトコルの上に 直接 追加することができます マルチレイヤのために 複数 追加することもできます WebSocketもここで実装できます これは 今年紹介する 新しいシステムです あなたのコネクションに追加できます WebSocketは protocol framerとして実装されます 今お使いのAPIで使用可能です フレーミングプロトコルの便利さが 分かります しかし自分で書きたくない人は WebSocketを使えます
Appによっては 状況により違うプロトコルスタックを 使わなくてはなりません フレーミングプロトコルは プロトコルスタックが違う場合でも― Appとコネクション間の コントラクトを同じにするのに便利です その例として 我々はDNSのシステムで使っています DNSは通常 UDPを通じて データグラムメッセージを送ります
しかしストリーム上で 実行しなければならない時もあります その場合 Length-Valueの プロトコルが― DNSをTCPにエンコードします そこでフレーマで カプセル化を定義することにしました DNSデータグラムを送る同じコードが 上にあって― 同じロジックで UDPとTCPのどちらでも使えるように こうすれば問題を分けて― パーツごとにデバッグできます
これでフレーミングプロトコルを コネクションに追加できました メッセージを送受信し― カスタム値を ゲームと同じように使うことができます Framer.Messageはどんなタイプの キーバリューも保存できます 送信操作にカスタム値を追加して― プロトコル内で その情報を受け取ることができます メッセージを作成して設定したら― 転送するデータの コンテキストに加えることができます どの送信操作にもコンテキストと共に コンテンツが付きます ここではコンテキストは― データをどのように送るかを示します
受信もよく似ています receiveMessageを 呼び出すと― どのように受け取ったかを示す コンテキストも一緒に受信します プロトコルの定義を使って― 届けられた特定のメッセージの値を 見られます
さて これで 三目並べを完成できるでしょう
よし ゲームプロトコルはできました
コネクションに追加するには エリックのパラメータを使います すでにパスコードで― TCPとTLSを使うコネクションを 設定してます
あと必要なのは―
この2行を加えるだけです ゲームのプロトコル定義に従って オプションを作り― コネクションで使う Appプロトコルの配列に挿入します これで2つのデバイスが接続されると ストリームを通じてメッセージが コード化されます
そしてAppによるメッセージタイプの 送受信を楽にするため― コネクション内で簡易関数を使います このコネクションオブジェクトは― 先ほど定義したパラメータで NWConnectionを設定します コネクションの準備ができると ピアからメッセージを受け取り始めます そこでreceiveNextMessageを
コネクションで receiveMessageを呼びます コンテンツとコンテキストを入手し コンテキストから 私のゲーム用のプロトコル定義と― プロトコル用のメタデータを 探します これで メッセージオブジェクトを得て― Appにデータと メッセージタイプを届けられます メッセージを1つ受け取ったら― receiveNextMessageを呼び出し 繰り返します
送信のヘルパーも定義します プレーヤーがキャラ選択をする時― メッセージを作成し selectedCharacterタイプを追加して コンテキストに加えて送ります
move送信でも同じく 簡易関数でmoveを送るように― Appデータを コネクションに送信するだけです それだけです ではプレイしましょう エリックに戻ってきてもらいます
さて エリックがホストでしたが― 今回は私がホストです
エリック パスコードを打ち込んで
5176 誰にも言わないように (笑い) コネクションを確保できました TLSを使っています エリックは鳥を選んだようですね キャラ選択のメッセージが送られ― 私がreceiveMessageを 呼び出します 選択結果を知り 私はサルを選びます 次にエリックが キャラを置く場所を選びます moveメッセージが私に送られ― それを私が受信して 置いた場所を知ります そして私が選びます サルを右上に置きます エリックはどうする? 勝ちに向かって一直線ですね どうしようかな 三目並べは難しい 鳥が勝ったようです でも簡単にゲームを作れることは 分かったはず 皆さんならいろんなゲームやAppを 作れるでしょう 楽しみにしています (拍手) さて
次に進む前に フレーミングプロトコルについて NWConnectionsで STARTTLSなどのような手法を― どう生かせるのかと聞かれます STARTTLSは SMTPメールプロトコルの手法です TLSや安全なコネクションを サポートするか分からないserverと― まずハンドシェイクをします サポートされていたら― そのserverをコネクションの途中まで 追加できます 従来の方法のこの問題を フレーミングプロトコルが解決します STARTTLSフレーミングプロトコルを 作成してコネクションに追加すると Appの起動時に― serverとハンドシェイクして TLSをサポートするか確認できます そして他のプロトコルを プロトコルスタックに追加してから― 呼び出しできます これならAppに手を加えず― TLSを追加する心配もありません プロトコルで自動的に済ませられます いい解決法だと思います
では次へ Bonjourについては すでに話しました よりよいP2Pコネクションの作成や 探索の改善 フレーミングプロトコルの話も ここで少し戻り― App内のコネクションに関する メトリック収集を見てみましょう メトリック収集は非常に重要です 自分のAppやserverに 新しい機能を追加する時― 確認することができます その機能が実際に有効で パフォーマンスが向上するかどうか? それ以外にも ユーザが実際に遭遇する― 気づかないような問題を 特定する手助けもします
今年は解析を助ける 新たなメトリックを用意しました URLSessionは 今よりさらに数が多くなります Network.frameworkでは 初めてですが コネクションを調べて多角的に パフォーマンスを知ることができます
URLSessionでは すでにApp内でDNS TCP TLS や― HTTPメッセージの timing breakdownが得られます これからはより多くの コネクションプロパティでさえ― 各リクエストや応答に対して送る データと共に 調べることができます
Network.frameworkでは コネクション時の要約である― コネクション確立報告に アクセスできるようになります コネクションの各時間の動作が分かる データ転送報告も含みます それを同時に複数 走らせられます
URLSessionから始めましょう ちなみに― URLSessionのメトリックはすべて didFinishCollectingMetricsで入手可能
新しくアクセスできるものとして コネクションのエンドポイント― ローカルとリモートアドレスや ポートがあります セキュリティプロパティも確認できます TLS 1.3を使っていますか? 最も新しく最も安全な バージョンのTLSです パスプロパティも確認できます 調べられることは… コネクションが 制限されたネットワークを使ったか? セルラーネットワークを使ったか などです
Network.frameworkでは 同等のメトリックが確立報告にあります コネクションがready状態になった後 いつでも見れます 分かるのはDNSの時間や― TCPとTLSのプロトコルハンドシェイク プロキシの有無などです
こちらがコードです requestEstablishmentReportを呼ぶと キューを取り 報告を届けます それが済むと コネクションにかかった時間を見れます 個別のresolutionも確認できます Bonjour名で接続すると― それがホスト名 さらにアドレスに 転換されることもあります それらの時間も確認できます
TCP TLSのタイミングや― 観測された往復時間も確認できます
コネクション確立のパフォーマンスに 何より重要なのは― DNSの転換にかかる時間と― そしてDNSが来たソースがどこかです
多くのserverはDNSレコード上に とても短いTTLを設定しています これはserverがダウンした時 または負荷を 別のIPアドレスに分散したい時に― IPアドレスをすばやく変えて クライアントに適応させるためです 欠点は クライアントパフォーマンスの低下です TTLが短いので クライアントはほぼ必ず往復して― DNSに行き つなぐホスト名を聞く必要があります
高レイテンシのクライアントには これはよくありません 何百ミリ秒 あるいは数秒も接続時間が増えます 最もよくないのは 大抵 全くアドレスは変わっていないので― 余計な往復なのです Optimistic DNSは去年 発表した この問題の解決法です
Optimistic DNSは― ホスト名に対して最後に良好だった IPアドレスに接続します 現アドレスの新しい クエリと平行して実行します 何も変更がなければ― 古いIPアドレスに接続されます 変更があれば 新しいIPアドレスに接続されます
計測やテストを重ねました すばらしい解決法です 今年からはNetwork.frameworkと URLSessionでデフォルトになります
確立報告では ソースを見ると Optimistic DNSを使ったか分かります 失効したキャッシュなら― それはつまり Optimistic DNSを使ったということです メトリックで パフォーマンスを確認する方法と― Optimistic DNSとTLS 1.3の利点を 紹介しましょう
こちらのAppはコネクション メトリックを収集する単純なものです 指定のWebサイトを調べるだけです
“Run Probe”を押すと接続されました 早いですね Wi-Fiネットワークです
もっと現実的なシナリオを試して 違いを見たい場合― Xcodeのdevices and simulators パネル内で― デバイス状況にアクセスしたり 様々なリンク状況を シミュレートできます 潜在的なユーザの違う環境を 試すことができます (拍手) 便利です
高レイテンシのDNSリンクを見ましょう スタートを押すと 左上にグレーのボックスが出て― 動いていると分かります では また調べてみましょう
早いですね でも失効したキャッシュからです Optimistic DNSが使われました
デフォルトでオンです でも自由に オフにすることができます では もう1度
時間が過ぎていきます これは少しおおげさかも 3秒以上のレイテンシはないと 願いたいです でも大きな違いになります
もう少し現実的なシナリオを 例えば標準的な3Gネットワークです
スタートさせ もう1度調べます
初回ほど速くはありません 全体としては 約600ミリ秒でコネクションが確立
TLSだけだと― 約300ミリ秒 半分ほどの時間ですね
我々のserverは TLS 1.3をサポートしています TLS 1.3は基本 ハンドシェイクを1往復で済ませます 大幅な改善です でも皆さんのserverがTLS 1.2しか 使えない場合 あるいはAppのAPIが TLS 1.3をサポートしていない場合
こんなシナリオになるかも TLSだけで500ミリ秒以上かかり 余分な往復をしています 接続時間は4分の3秒以上 ほぼ1秒かかっています コネクションが多いと かなりのレイテンシになってしまいます Appのテストでは Network Link Conditionerを使って Appのパフォーマンスに問題がないか 試してください
他のメトリックはコネクション確立後の データ転送に関するものです URLSessionでアクセスできるのは… リクエストのヘッダとボディで送った バイトの数や― 応答のヘッダやボディから受け取る バイトの数などのメトリックです とても重要です 異なるURLを選んで 低データモードでダウンロードする場合 ユーザのバイトを節約していることを これで示します
Network.frameworkでは データ転送報告にアクセスできます バイトやパケットや コネクション上で与えられた期間の― 往復時間などのパフォーマンスを まとめたものです 同時に複数 走らせられ― Appのアクティビティに対応します バーストトラフィックは データ転送報告に入れましょう アイドル期間の報告を得るのは それほど面白くないです
まずコネクション上で startDataTransferReportを呼びます これでコネクションのパフォーマンスを 収集し始めます データを送り終えたらcollectを 呼び出せます これによって データをまとめた報告が得られます マルチパスのプロトコルなら― 使っていた各リンクで送られた総量の 分析結果が得られます でも大抵は集約パス報告で足ります ここでは送受信したパケットの数や― 転送されたバイト数 往復時間の詳細などが確認できます Appのパフォーマンス向上に つながるので― これらのメトリックを導入ください 有用な助言とアップデートを スチュアートに紹介してもらいます (拍手) ありがとう トミー 2時間に及ぶ すばらしいセッションの最後を― 飾ることができて とても光栄です
まずMacのiPad Appから始めます 楽しみな人も多いでしょう ネットワーキングに関しては Appleプラットフォームの違いは少ない 1つ注意点があります Xcodeの設定で Macのボックスをチェックすると― 新しいオプションが現れます デフォルトで 出接続が有効になっています 入接続が必要なら ボックスをチェックしてください
watchOSでは 新しいネットワーキング機能があります
AVFoundationを使って オーディオストリーミングするAppは ダイレクトネットワーキングが可能に ただしURLSessionか Network.frameworkを使う場合です ソケットは使えません
新しいTLS 1.3には いろんな利点があります 接続パフォーマンスの向上です TLS 1.2は 接続までに2往復かかりますが TLS 1.3は ほぼ必ず1往復で済ませます TLS 1.2は当時の暗号アルゴリズムを 使っていますが― 脆弱性が見つかりました 机上だけでなく 現実でも被害が出ています
それらはTLS 1.3で修正されました そしてTLS 1.3の暗号アルゴリズムは すべて― 関連データと前方秘匿性の 認証付き暗号をサポートします Appleはプライバシーを重視しています TLS 1.3ではプライバシーを強化 TLS 1.2ではヘッダや証明書の 多くは暗号化せず送られていました TLS 1.3では暗号化されます なのでTLS 1.3を 使い始めることをおすすめします サービスも アップデートするのをお忘れなく
Appleはプライバシーを重視します そしてWi-Fi情報へのアクセスは― 位置の推定に使えると気づきました そこで今後 Wi-Fi情報にアクセスするには― 他の位置情報と同じような権限が 必要になります まずXcodeでは プロジェクトに権限を追加するため アクセスのcapabilityを追加します Appは3つの基準のうち 1つは満たす必要があります ユーザがAppに位置情報を許可すれば Wi-Fi情報にもアクセスできます またはAppがVPN接続を許可されていれば アクセスできます そして最後に Appがホットスポット構成であれば アクセスできます ただし該当ネットワークでのみです 詳しくは Wi-Fi frameworkをご参照ください
繰り返しますがNetwork Link Conditionerを使うことが重要です
MacのSimulatorで ギガビット・イーサネットやローカルと 接続したまま 開発している時はとても楽なものです レイテンシもなく 帯域が無限のserverがあれば― パフォーマンスはいいでしょう でもその状態で開発すると― 実際の環境では うまく動かない可能性があります もし現実的な Network Link Conditionを選び― 開発するのが 習慣になっているとしましょう 最初から その状態で― Appをテストし シミュレートするようにすれば― バグが起きることもありません
長年 言い続けていますが― プリフライトチェックは避けましょう
セルラーネットワークを許可するなど 制限下で進めれば― より制御しやすいでしょう より楽です これに慣れれば プリフライトチェックなど忘れ去ります それにプリフライトは 競合状態があるので信頼できません ではここで 実例によってお見せしましょう これは私の好きなAppです ただうまく書かれていないAppが どうなるかを実演するために― かなり大げさな例にしました これはユーザにWi-Fiの確認と クリックを求めています でもユーザには どの経路で接続されるか決められません そこで大抵は Wi-Fiバーを探して幸運を祈ります でも今日 学んだように― Wi-Fiのパフォーマンスを 事前に知ることは不可能です Wi-Fiにつないでも うまくいかないこともあります Wi-Fi アシストが Wi-Fiからセルラーに切り替えると― Wi-Fiバーが消えます しかしすでに接続は されてしまっています だから― ユーザに推測させてしまっては いけません 接続させて 最善を祈らせないように 実際はどうするか見せましょう セルラーアクセスを制限された状態で コネクションを確立させます iOS 13からは allowsExpensiveNetworkAccessを使い システムに ネットワークを選ばせることができます
waitsForConnectivityもtrueに Appが再試行を繰り返しません コネクションが成功するまで システムはゆっくり待ちます
Appが接続を試してもWi-Fiがない時 taskIsWaitingForConnectivityが 呼ばれます そしてユーザに選択肢を与えます Wi-Fi環境に移動するか そのままセルラーデータを使うかです
いくつかの廃止情報です file://やftp://で PACファイルをまだ使っている方へ サポートは終了です SPDYを使っている方へ あれも実験的でよかったですね でも今はHTTP/2が代替です こちらをAppleはサポートするので ぜひ移行を Secure Transportは TLS 1.3をサポートしていません その予定もありません URLSessionか Network.frameworkにぜひ移行を
ではまとめです 今朝は広範な Bonjourの探索について話をしました
そして三目並べゲームの広告も 皆さんの中には― あのゲームが登録されているか 知りたい方もいるでしょう 答えはイエスです サイトでご確認を (笑い)
トミーが話したのは フレーミングプロトコルについて Appを書いて パフォーマンスを計測するのに メトリック収集が役立つことです
さらにPart 1では― 低データモードで ユーザのデータ節約に配慮すること 非同期作業をつなぐため URLSessionを組み合わせること そしてWebSocketです AppでWebSocketを使ってserverと やり取りする場合― そのserverを ネイティブiOS Appに使用できます そしてクリストフがマルチパスTCPと Wi-Fi アシストの改善点について 紹介しました それに関連し 皆さんもご存じのようにACM SIGCOMMは ネットワーク分野の研究における 世界有数の学会です 毎年 ネットワーク分野で― 最も大きな影響を与えたものに対して 賞を贈っています 今日 発表されましたが 今年はその賞が クリストフとそのチームに贈られました (拍手)
明日のラボにもぜひご参加ください Network Kernel Extensionsに 興味ある方は― ぜひ明日のセッションにご参加ください ありがとうございました (拍手)
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。