ハイライト
iOS 15 と macOS Monterey ではデフォルトで HTTP/3 が有効になり、コードを変更せずに URLSession を自動的にアップグレードできます。同時に、Network.framework に QUIC トランスポート層のサポートが追加され、開発者が NWConnectionGroup を使用してカスタム多重化プロトコルを構築できるようになりました。
主な内容
HTTP プロトコルの進化: HTTP/1 から HTTP/3 へ
リソースをリクエストするたびに、接続の確立、リクエストの送信、処理の待機、応答の受信という 4 つの手順を実行する必要があります。複数のリソースが同時にリクエストされた場合、HTTP/1 はリソースごとに個別の接続を確立する必要があり、接続の確立に多くの時間が無駄になります。
HTTP/1 の改良点は、単一の接続を再利用することですが、リクエストは連続して送信する必要があり、前の応答が完了するまで次のリクエストは送信できません。これは行頭ブロックです。ブラウザは複数の並列接続を開くことでこの問題を軽減しますが、これによりクライアントとサーバーの両方で非効率的なネットワーク動作が発生します。
HTTP/2 は単一の TCP 接続上で複数のストリームを多重化し、リクエストをより早く発行でき、異なるストリームからのデータをインターリーブできます。アイドル待ち時間が大幅に短縮されます。
HTTP/3 はこれに基づいてさらに改善され、接続がより速く確立され、リクエストをより早く発行できるようになります。さらに重要なことは、HTTP/3 ストリームは互いに独立していることです。すべての HTTP/2 ストリームは TCP 接続を共有しており、ネットワーク パケット損失はすべてのストリームに影響を与える可能性があります。 HTTP/3 では、パケット損失は対応するストリームにのみ影響し、他のストリームからのデータは正常に配信されます。
QUIC: HTTP/3 の基礎となるエンジン
QUIC は、TCP と同じ概念に基づいた IETF 標準化された信頼性の高いトランスポート プロトコルですが、エンドツーエンドの暗号化、多重化ストリーム、および認証を提供します。セキュリティは TLS 1.3 に基づいて構築されています。
QUIC の主な利点:
- 高速ハンドシェイク: QUIC は TLS 1.3 に依存して安全なハンドシェイクを完了し、TCP の 3 ウェイ ハンドシェイクを必要とせず、ハンドシェイク時間を 1 往復に短縮します。
- 行頭ブロックなし: 多重化ストリームは QUIC の中核概念であり、TCP のようにブロックされません。
- より優れたパケット損失回復: QUIC エンドポイントは、より複雑なパケット収集情報をピアに送信でき、TCP による制限を受けず、より強力な接続パケット損失回復機能を備えています。
- 接続の移行: セッションを再確立することなく、異なるネットワーク インターフェイス (セルラーから Wi-Fi など) の間で接続をシームレスに切り替えることができます。
URLSession の HTTP/3
iOS 15 と macOS Monterey では、デフォルトで HTTP/3 が有効になります。URLSession を使用するアプリケーションはコードを変更する必要がなく、サーバーが HTTP/3 をオンにした後に自動的に使用できます。
サポートされるバージョンには、今後の HTTP/3 RFC と以前の Draft 29 が含まれます。
**アプリケーションが HTTP/3 を使用していることを確認するにはどうすればよいですか? **
Xcode 13 の Instruments には、追加の構成なしで URLSession に直接接続する新しい HTTP トラフィック検査ツールが追加されています。各リクエストの HTTP バージョンと応答ヘッダーを表示できます。
サーバーが渡されましたAlt-Svc応答ヘッダーは HTTP/3 サポートを宣言します。
Alt-Svc: h3=":443"; ma=2592000
URLSession は、「サービス検出」と呼ばれる後続の接続のためにこの情報を記憶します。より推奨される方法は、DNS で HTTPS リソース レコードを構成することです。h3この文字列は HTTP/3 を宣言しているため、最初の接続で HTTP/3 が確立されます。
サーバーが HTTP/3 をサポートしていることが確実で、検出プロセスを高速化したい場合は、次のように設定できます。assumesHTTP3Capable財産。これにより、HTTP スタックはサーバーが HTTP/3 をサポートしていると想定できますが、HTTP/3 が使用されることは保証されません。ネットワークが HTTP/3 をブロックするか、サーバーが実際に HTTP/3 をサポートしていない可能性があり、その場合は自動的に HTTP/2 にフォールバックします。
HTTP/3優先モデル
HTTP/2 の優先順位スキームは、その複雑さのためにサーバーによってほとんど尊重されませんでした。 HTTP/3 では古いモデルが削除され、HTTP ヘッダーに基づくより単純なスキームが採用されました。優先のためurgencyパラメータ (0 ~ 7) およびオプションincrementalパラメータが指定されています。
URLSession の API は変更されず、引き続き使用されます。priority属性の優先順位を設定するには、次を使用します。prefersIncrementalDelivery増分配信を有効にします。デフォルトの優先順位は 3 です。URLSession は、非同期データ メソッドなどの便利な API が使用されているかどうかに基づいて増分配信を推測します。
リソースの優先順位付けを動的に調整することもサポートされています。たとえば、最初に優先度の低い写真をプリロードし、ユーザーが対応する領域にスライドしたときに優先度を上げることができます。
Network.framework での QUIC サポート
HTTP/3 は QUIC に基づいていますが、QUIC の機能は HTTP に限定されません。アプリケーションがリクエスト/レスポンス以外のデータを交換する場合、基礎となるトランスポート コンテキストを共有するために多重化ストリームが必要な場合、またはカスタム プロトコル (P2P 通信、RPC 呼び出しなど) を実装する場合は、QUIC トランスポートを直接使用できます。
iOS 15 および macOS モントレーでは、NWProtocolQUICNetwork.framework に参加します。QUIC 接続の作成は、他のプロトコルと同様です。
let connection = NWConnection(
host: "example.com",
port: 443,
using: .quic(alpn: ["myproto"])
)
connection.stateUpdateHandler = { newState in
switch newState {
case .ready:
print("Connected using QUIC!")
default:
break
}
}
connection.start(queue: queue)
接続が確立されると、他の接続と同じようにデータが送受信されます。NWConnection同じ。
NWConnectionGroup は多重化ストリームを管理します
QUIC トンネルは、基礎となるトランスポート コンテキスト上で複数の一方向または双方向 QUIC ストリームを多重化します。ストリームは任意のエンドポイントで作成でき、データを同時に送信したり、他のストリームとインターリーブしたりすることができ、従来の TCP ストリームと同様の状態を持つことができます。
NWMultiplexGroupは、このタイプの接続を管理する新しいタイプです。
let descriptor = NWMultiplexGroup(to: .hostPort(host: "example.com", port: 443))
let group = NWConnectionGroup(with: descriptor, using: .quic(alpn: ["myproto"]))
group.stateUpdateHandler = { newState in
switch newState {
case .ready:
print("Group ready!")
default:
break
}
}
group.start(queue: queue)
新しい送信ストリームを作成します。
let connection = NWConnection(from: group)
リモート エンドポイントによって開始された受信ストリームを受信します。
group.newConnectionHandler = { newConnection in
newConnection.stateUpdateHandler = { newState in
// Handle stream states
}
newConnection.start(queue: queue)
}
サーバーが QUIC トンネルを受信します
使用NWListenerサーバーを実行するときに、次のように渡すことができますnewConnectionGroupHandler新しい QUIC トンネルを受信します。
listener.newConnectionGroupHandler = { group in
group.stateUpdateHandler = { newState in
// Handle tunnel states
}
group.newConnectionHandler = { stream in
// Set up and start new incoming streams
}
group.start(queue: queue)
}
QUIC メタデータにアクセスする
合格NWProtocolQUIC.Metadataストリーム情報は取得および変更できます。
if let metadata = connection.metadata(definition: NWProtocolQUIC.definition)
as? NWProtocolQUIC.Metadata {
print("QUIC Stream ID is \(metadata.streamIdentifier)")
// Set application error before cancelling
metadata.applicationError = 0x100
connection.cancel()
}
QUIC 接続のデバッグ
デバッグ時に qlog ファイルを出力するように環境変数を設定します。qlog は、IETF によって提案された標準化されたログ形式で、従来のパケット キャプチャよりも豊富な QUIC 接続情報が含まれています。テストが完了したら、Xcode の [デバイス] ウィンドウでアプリケーション コンテナーをダウンロードして qlog ファイルを取得し、オープン ソースの視覚化ツールを使用してそれを分析します。
##詳細
QUIC接続を作成する
(13:20)
let connection = NWConnection(
host: "example.com",
port: 443,
using: .quic(alpn: ["myproto"])
)
connection.stateUpdateHandler = { newState in
switch newState {
case .ready:
print("Connected using QUIC!")
default:
break
}
}
connection.start(queue: queue)
キーポイント:
.quic(alpn:)パラメーターは ALPN (アプリケーション層プロトコル ネゴシエーション) 文字列を指定しますstateUpdateHandler接続ステータスの変化を監視し、.ready接続が確立されていることを示しますstart(queue:)指定されたキューで接続を開始します
QUIC トンネルを確立する
(15:08)
let descriptor = NWMultiplexGroup(to: .hostPort(host: "example.com", port: 443))
let group = NWConnectionGroup(with: descriptor, using: .quic(alpn: ["myproto"]))
group.stateUpdateHandler = { newState in
switch newState {
case .ready:
print("Connected using QUIC!")
default:
break
}
}
group.start(queue: queue)
キーポイント:
NWMultiplexGroupQUIC トンネルの記述子ですNWConnectionGroup基礎となるQUICトンネルを共有する複数のフローを管理する- 状態ハンドラーは、個々のフローではなく、トンネルの状態を追跡します。
管理フロー
(15:45)
// Create new outgoing stream
let connection = NWConnection(from: group)
// Receive incoming streams
group.newConnectionHandler = { newConnection in
newConnection.stateUpdateHandler = { newState in
// Handle stream states
}
newConnection.start(queue: queue)
}
キーポイント:
NWConnection(from: group)グループから新しい送信フローを作成するnewConnectionHandlerリモートエンドポイントによって開始された受信フローを処理する- 各ストリームは独自の状態を独立して管理します
QUIC メタデータにアクセスする
(17:22)
if let metadata = connection.metadata(definition: NWProtocolQUIC.definition)
as? NWProtocolQUIC.Metadata {
print("QUIC Stream ID is \(metadata.streamIdentifier)")
// Set application error before cancelling
metadata.applicationError = 0x100
connection.cancel()
}
キーポイント:
streamIdentifierQUICストリームIDの取得applicationErrorストリームを閉じる前にアプリケーション層エラーをピアに報告するために使用されます。- メタデータは、接続がキャンセルされる前に変更された場合にのみ有効です。
重要ポイント
- サーバーの HTTP/3 を有効にする
- 対処方法: CDN または自作サーバーで HTTP/3 を有効にして、iOS 15 以降のユーザーのネットワーク エクスペリエンスを自動的に高速化します。
- 実行する価値がある理由: URLSession はデフォルトで有効になっており、ユーザーはアプリケーションを更新せずに 0-RTT ハンドシェイク、行頭ブロックなし、接続移行などの利点を享受できます。
- 開始方法: CDN ユーザーは、サービス プロバイダーの HTTP/3 スイッチを確認する必要があります。自作サーバーには nginx 1.25+ または Cloudflare quiche を使用してください。DNS HTTPS レコードを設定し、
Alt-Svc応答ヘッダー
- Instruments を使用して HTTP/3 が有効であることを確認します
- やるべきこと: Xcode 13 の HTTP Traffic Instrument を使用して、アプリが実際に HTTP/3 を使用していることを確認します。
- 実行する価値がある理由: HTTP/3 はサービス検出に依存しており、最初の接続は依然として HTTP/2 を経由する可能性があります。構成が正しいかどうかを確認する必要がある
- 開始方法: ネットワーク プロファイリング テンプレートを選択し、アプリケーションの実行を記録し、HTTP バージョン列を確認して、
Alt-Svc応答ヘッダー
- リアルタイム通信アプリケーション用に QUIC を接続
- 内容: Network.framework の QUIC サポートを使用してカスタム プロトコルを構築し、独自の TCP ベースのプロトコルを置き換えます
- 実行する価値がある理由: QUIC は、独自に開発した TCP ベースのプロトコルよりも信頼性の高い、組み込みの TLS 1.3、多重化、および接続移行を提供します。
- 開始方法: を使用します
NWConnectionGroup+NWMultiplexGroupQUIC トンネルとストリームを管理します。設定QUIC.Options送信パラメータを設定する
- 接続移行に適した長時間接続を実現
- 内容: QUIC の接続移行機能を利用して、Wi-Fi ネットワークと携帯電話ネットワークを切り替えるときにアプリの接続を維持します。
- 実行する価値がある理由: 従来の TCP 接続は、ネットワークが切り替わると切断され、再ハンドシェイクが必要になります。QUIC の接続 ID メカニズムによりシームレスな移行が可能
- 開始方法: を使用します
NWConnection+.quic()既存の TCP 接続を置き換えます。追加のコードは必要ありません。Network.framework が自動的に移行を処理します。
関連セッション
- URLSession で async/await を使用する — URLSession で async/await を使用してネットワーク コードを簡素化します
- アプリのネットワーク遅延を削減する — アプリケーションのネットワーク遅延を削減する戦略
- Swift の async/await について — Swift async/await 入门
コメント
GitHub Issues · utterances