目次 - SDL_net 3.0

SDL_net 3.0への移行

概要

SDL_net 3.0(別名"SDL3_net")は以前のバージョンから大きく変わった. APIは完全に再設計された. 互換レイヤは存在しない. このライブラリを使用したいならば, 移行しなければならない.

SDL3_netにはSDL3が必要となる. このライブラリは内部と公開されたAPIの両方でSDL3の新しい機能を使用しているため, あなたのプロジェクトがSDL 1.2やSDL2を使用している場合, 同時にそのプロジェクトもSDL3に移行しなければならない.

しかし, 我々はSDL3_netとSDL3はどちらも素晴らしいソフトウェア――以前のバージョンから飛躍的に向上した――であり, 一度移行すればその良さをを実感できるはずだ.

単純な置き換えでは移行できない多くの事柄がある. 新しいAPIは多くが, より強力でシンプルになっている.

この移行ガイドでは重要な項目を順に説明するが, 同じ方法で行えない場合もある. 行き詰ったら気軽にバグレポートを開いて欲しい.

ドキュメントの最後の「要約」のセクションには対応するSDL2_net関数の名前と短い説明がある.

完全に削除された機能

SDL3_netのインクルード

正式なSDL3_netヘッダのインクルードの方法は:

#include <SDL3_net/SDL_net.h>

SDL3と同様に, 新しい方法では<>とサブディレクトリを使用すること.

シンボル名

SDL2_netでは, 全ての関数はSDLNet_, マクロはSDLNET_で始まっていた. SDL3_netでは, 全てNET_になった.

バージョン

バージョンは構造体ではなく1つのintにビットが詰められていて, 詰められたビットを戻すにはSDL3の通常の方法を使用する.

戻り値

SDL2_netでは多くの場合は成功のとき0, 失敗のとき-1を戻していた. SDL3_netでは, これらの戻り値はブール値になり, 成功のとき真, 失敗のとき偽を戻す. 移行するとき, このような場合はコンパイラは警告を発するが...

if (NET_Init() < 0) { /* 失敗! */ }

...これは警告を発しない...

if (!NET_Init()) { /* SDL2_netの場合は成功時だが, SDL3_netの場合は失敗時となる! */ }

複数プロトコル対応

SDL2_netのパブリックAPIではIPv4ネットワークの提供がハードコードされていた. SDL3_netではIPaddress構造体が不透明なNET_Addressに置き換えられた. これで将来の未知のプロトコルを含む異なるネットワークプロトコルのアドレスを扱えるようになった. しかし最も重要なのは, アプリケーションがIPv4とIPv6を抽象化して扱えるようになったことである.

(理論上は, 現在でも例えばIPXやAppleTalkにも対応できるが, SDL3_netのコードの若干の修正が必要となる. パッチを送ってください.)

NET_ResolveHostname()を使用すると, 複数の利用可能なプロトコルが得られ, OSがシステムにとって最もよい選択を行う場合がある.

さらにSDL3_netは「デュアルスタック」プロトコルに対応している. NULLアドレスにバインドされたサーバまたはデータデータグラムソケットを生成すると, 背後ではIPv4とIPv6のOSレベルのソケットを生成して両方でlistenするように試みる. これは単なるIPv4とIPv6のブリッジではない. 両方のプロトコルのトラフィックを同時に受信することが可能で, 例えば157.90.7.176と2a01:4f8:251:5583::2のようなクライアントのアドレスから同時に来る場合でも, あなたのサーバは簡単にこれらをブリッジできる.

ブロッキングと非ブロッキング

SDL2_netは(ほとんどが)ブロッキングAPIで, ネットワークの処理に時間がかかる場合, 待つ必要があった. カーネルの転送バッファにデータグラムを渡すような短い待ち時間の場合もあるが, ネットワークの状態によっては長い時間がかかった. ブロックしないためには, データを受信したかを問い合わせる「ソケットセット」に対するselect()呼び出しのラッパーが提供されていた. DNSルックアップには獲得と同じ時間が必要だった.

SDL3_netは逆の方法を取っており, 全て非ブロッキングになった. データを受信していないならば, 呼び出しから即座に戻り, それを報告する. データを今すぐ送信できない場合は, 後でライブラリが送信できるまで内部のキューに格納される. DNSへの問い合わせはバックグラウンドスレッドで行われ, アプリケーションは定期的にその結果をチェックする.

「ソケットセット」の考え方は1つの関数NET_WaitUntilInputAvailable()に集約された. これは様々なSDL_netオブジェクトの配列とタイムアウト時間を引数にとる. タイムアウト時間を0にするとブロックせず, -1にすると無限に待つ.

ブロックさせたい場合のため, 特定の項目を(タイムアウト時間を指定して)待つ関数も提供されている. これらを効率よく待つために呼び出したスレッドに置くことができる.

単純な非ブロッキング問い合わせ関数NET_GetAddressStatus, NET_GetConnectionStatus, NET_GetStreamSocketPendingWritesも存在する.

スレッドセーフ

SDL3_netの全ての関数は, 同一ソケットを複数のスレッドから同時に操作してはならないこと以外はスレッドセーフである. 2つのスレッドが別々の2つのソケットを同時に使用するのは問題ない.

ネットワークバイトオーダー

SDL2_netでは, ネットワークアドレスやポート番号は「ネットワークバイトオーダー」(ビッグエンディアン)に変換する必要がある場合があった. SDL3_netでは, これらの値は常に「ホストバイトオーダー」(システムのネイティブバイトオーダー)であり, バイト交換は必要に応じてSDL3_netが行う.

無論, インターネット越しのペイロードのバイト列は様々なコンピュータの様々なバイト順であるため, アプリケーションはSDL2_netのときと同じように処理する必要がある.

初期化

以前のバージョンではSDL2_netはSDLNet_Init()の前にSDL_Init()が呼ばれていることを想定していた. 現在はその必要はない:

if (!NET_Init()) {
    SDL_Log("NET_Init 失敗: %s", SDL_GetError());
} else {
    SDL_Log("SDL_netの準備が完了した!");
}

NET_Init()は参照カウントされているため, 複数回呼んでも安全であり, 実際に初期化されるのは最初の呼び出しのみである. 同様に, NET_Quit()による実際の終了処理はNET_Init()と同じ回数が呼ばれた場合のみである. そのため, SDL3_netを使用するために初期化するとき, 他の箇所でも初期化しているかを気にする必要はない.

ストリームソケット

SDL2_netでは, 接続ソケットとサーバ(「リスナー」)ソケットは同じ型のTCPsocketオブジェクトだった. SDL3_netでは, 2つのオブジェクトNET_ServerとNET_StreamSocketに分けられた.

NET_StreamSocketはクライアントとサーバの間で実際にデータを送るためのもので, 接続の両端に1つずつ存在し, 両方が読込または書込を行える.

NET_Serverはクライアントからの新しい接続をlistenするためのもので, 接続のたびにサーバ側のNET_StreamSocketを生成する.

NET_ServerのアドレスにNULLを指定して生成すると, 内部ではIPv4とIPv6ネットワークの別々のlistenが行われる場合がある.

データグラムソケット

SDLNet_UDP_Open()はポートは指定するがINADDR_ANYに対してのみバインドした. SDL3_netでは, NET_CreateDatagramSocket()は指定のインターフェースに, またはアドレスにNULLを指定してINADDR_ANYと同等(しかしIPv4とIPv6インターフェースの両方でlistenできる)のバインドを行える.

ブロードキャスト

SDL2_netでは, UDPパケットをサブネットのブロードキャストアドレス(指定できれば), または255.255.255.255(これはWindowsでは使用できない)に送信できた.

SDL3_netでは, データグラムソケットをNET_PROP_DATAGRAM_SOCKET_ALLOW_BROADCAST_BOOLEANプロパティを指定して生成し, アドレスをNULLとしてNET_SendDatagram()を呼ぶと行える.

これは異なるプロトコルでのブロードキャストを抽象化する(IPv6を含む. SDL3_netは背後で疑似的なブロードキャストを行う). SDL3_netは全てのネットワークインターフェースに対して, そして異なるネットワークプロトコル対してブロードキャストを行おうとする. そのため, 1つのブロードキャストパケットが1つのアプリケーションに複数のインターフェースで届けられる場合や, IPv4とIPv6のアドレスの別のコンピュータから届けられたように見える場合もある. このことを考慮して重複したパケットを無視する必要がある.

さらに, クライアントからブロードキャストするサーバを検索する場合, サーバがIPv4とIPv6の両方でlistenしていると, 別のサーバから2つの応答があるように見える場合がある. ペイロードに同一マシンであるかを識別できる要素を加えるか, サーバブラウザ上でIPv4/IPv6サーバを分ける必要がある.

(当然ながら, アプリケーションのブロードキャストのデータ量には注意し, サブネットの良き住民であること!)

失敗のシミュレーション

SDL2_netにはSDLNet_UDP_SetPacketLoss()があった. SDL3_netでは, これが拡張されネットワーク層で別々になった. 個別にストリームソケット(信頼性のため遅延が生じたり, 切断されたりする)や, DNSルックアップ(パケットがDNSサーバに到達しないため時間がかかる, パケットが届かないまたは不明な理由でドメインが存在せず失敗する)の失敗率を個別に設定できる.

これはNET_SimulateAddressResolutionLoss(), NET_SimulateStreamPacketLoss(), NET_SimulateDatagramPacketLoss()関数で行える.

要約

SDL2_netとの対応と短いコメント

「SDL3_netには対応するものはない」ものがあるが, 要望があれば追加される可能性がある. もし行き詰ったならば, イシューに投稿してもらえれば議論できる.

SDL Wikiへのリンク

SDL3_net/README-migration