アプリケーションが意図的に無線通信モジュールを利用した無線通信を行うために、以下のライブラリが用意されています。
- ローカル通信(UDS 通信)
- ニンテンドー3DSダウンロードプレイ
- 自動接続
- 信頼性のあるローカル通信(RDT 通信)
この章では、それぞれのライブラリを使用したアプリケーションの開発に必要な情報とプログラミング手順について説明します。
3.1. ローカル通信(UDS 通信)
3DS ではローカル通信として、ゲームでの利用を想定した任天堂独自の通信方式である「UDS 通信」を使用します。
UDS 通信は以下の特徴を持っています。
- 1 つのネットワークに対して、最大 16 台(現時点では 13 台以上は動作保証外です)が同時接続可能
- パケットのブロードキャストをサポート
- ピクチャーフレームに同期しない
- 送信データの到達は保証されない
- 伝送経路は暗号化されている
- データの送受信を定期的に行わなくとも接続が維持される
開発用の本体(デバッガ、開発実機)と製品版の本体との間では UDS 通信を行うことができません。
UDS 通信には、CTR-SDK で用意されている UDS ライブラリを使用します。
UDS ライブラリで使用する用語は以下のとおりです。
ノード / Node
UDS 通信のネットワーク上にある、通信(データの送受信)を行う端末です。
後述する、Master と Client を総称したものです。
ノード ID
UDS 通信のネットワークへの接続時に割り当てられる ID です。
詳細については「3.1.8. ノード ID の割り当て」を参照してください。
Master
新規に UDS 通信のネットワークを開設し、必要に応じてネットワークの属性を変更する権限を持つノードです。
DS / DSi の MP 通信における MP Parent に相当します。
Client
Master が開設した既存のネットワークに接続し、データの送受信を行うノードです。
DS / DSi の MP 通信における MP Client に相当します。
Spectator
Master が開設した既存のネットワークに接続し、データの受信のみを行う端末ですがノードではありません。
Client と異なり、ネットワークの同時接続台数としてカウントされません。Spectator の同時接続可能台数にはライブラリによる上限はありません。また、アプリケーションからその数を制限することもできません。
Unicast 通信
Unicast 通信は、あるノードから別の 1 つのノードに対してパケットを送信する方式で、基本的に Master と Client 間でのみ通信が可能です。アプリケーションからは Client 同士での通信も同じように扱うことができますが、Master を経由する必要がありますので、ライブラリ内で 2 回の送信が行われます。この方式では、すべて Master との通信で処理されるため Client からの送信レイテンシは高くなりますが、送信先のノードが通信可能範囲外になることはありません。
Broadcast 通信
Broadcast 通信は、あるノードから自分以外のすべてのノードに対してパケットを送信する方式で、1 回の送信で複数のノードにパケットを送信することができます。この方式では、ネットワーク内のノード数に関係なく 1 回の送信で処理されるため Client からの送信レイテンシも低くなりますが、Client から通信可能範囲外にあるノードに送信されるパケットが到達しなくなります。
3.1.1. UDS 通信の内部ステート
UDS ライブラリは、図 3-1 のように遷移する内部ステートを持っています。
内部ステートには状態を表すステートと、現在のステートから目的のステートへ遷移する際に経由する中間ステートがあります。中間ステートはライブラリ内部の処理用として存在しているステートのため、基本的にアプリ開発者が意識する必要はありませんが、もし現在のステートが中間ステートだった場合は、通常のステートに遷移するまで待つようにしてください。
UDS ライブラリはステートによって呼び出すことのできる関数に制限があり、そのステートで使用できない関数を呼び出した場合は即座にエラーが返されます。
ステート名 |
説明 |
---|---|
None |
UDS ライブラリ初期化前の状態です。 |
Disconnected |
UDS ライブラリの初期化が完了していて、ネットワークに属していない状態です。 |
Master |
ネットワークを新規に構築し、Master として動作している状態です。 |
Client |
既存のネットワークに Client として接続している状態です。 |
Spectator |
既存のネットワークに Spectator として接続している状態です。 |
中間ステート名 |
説明 |
---|---|
Initializing |
UDS ライブラリの初期化中です。成功した場合に Disconnected に遷移します。 |
Finalizing |
UDS ライブラリを終了中です。終了後 None に遷移します。 |
Creating Network |
設定に基づきネットワークを構築中です。終了後 Master に遷移します。 |
Connecting Network |
既存のネットワークに Client/Spectator として接続中です。 成功した場合に Client/Spectator に遷移します。 |
Destroying Network |
すべての Client/Spectator をネットワークから切断し、ネットワークを破棄しています。 終了後 Disconnected に遷移します。 |
Disconnecting Network |
現在接続中のネットワークから離脱中です。終了後 Disconnected に遷移します。 外的要因によりネットワークから切断された場合にもこのステートになります。 |
これらのステートは nn::uds::State
で定義されており、ステート名と定義名の対応は以下のようになっています。
ステート名 |
定義名 |
---|---|
None |
|
Disconnected |
|
Master |
|
Client |
|
Spectator |
|
Initializing |
(このステートをアプリケーションで取得することはありません) |
Finalizing |
(このステートをアプリケーションで取得することはありません) |
Creating Network |
|
Connecting Network |
|
Destroying Network |
|
Disconnecting Network |
|
3.1.2. 初期化
UDS ライブラリの初期化は、nn::uds::Initialize()
を呼び出して行います。
nn::Result nn::uds::Initialize( nn::os::Event* pStatusUpdateEvent, void* receiveBuffer, const size_t bufferSize); nn::Result nn::uds::Initialize( nn::os::Event* pStatusUpdateEvent, void* receiveBuffer, const size_t bufferSize, nn::cfg::UserName* pUserName);
pStatusUpdateEvent
には nn::os::Event
クラス型のインスタンスへのポインタを指定します。インスタンスは自動リセットイベントとして初期化されます。
Event
インスタンスは nn::uds::Initialize()
内で初期化され、以下のタイミングでアプリケーションに対する通知が行われます。
- 自身がネットワークを構築した、もしくはネットワークに接続したとき
- 自身がネットワークから離脱した、もしくは切断されたとき
- 自身以外のノードがネットワークに接続したとき
- 自身以外のノードがネットワークから離脱した、もしくは切断されたとき
receiveBuffer
と bufferSize
には UDS ライブラリ内で使用する受信データバッファとそのサイズを指定します。バッファはアプリケーションでデバイスメモリ以外から確保し、先頭アドレスが nn::uds::BUFFER_ALIGNMENT
( 4096 Byte ) アライメントでサイズが nn::uds::BUFFER_UNITSIZE
( 4096 ) の倍数でなければなりません。受信データバッファに指定したメモリ領域は、nn::uds::Finalize()
による終了処理が完了するまで、アプリケーションからアクセスしないでください。
pUserName
は Mii につけられた名前など、本体設定と異なるユーザー名を使用する場合に指定します。ユーザー名の取り扱いは UGC ガイドラインに従ってください。NULL
を指定すると、pUserName
を引数に持たない関数で初期化した場合と同様に、本体設定のユーザー名が使用されます。
なお、返り値に nn::uds::ResultAlreadyOccupiedWirelessDevice
が返されたときは、無線通信モジュールがほかの通信で利用されているために UDS 通信を開始することができません。また、無線オフモードになっているときには、nn::uds::ResultWirelessOff
が返されます。
3.1.3. 接続
UDS ライブラリの初期化後、UDS 通信のネットワークの構築を行います。ここでは、Master によるネットワークの構築および Client/Spectator によるネットワークへの接続の手順を説明します。
3.1.3.1. ローカル通信 ID の生成
UDS 通信のネットワークの構築や検索を行う関数の呼び出しには、ネットワークを識別するためにローカル通信 ID(32 bit)を指定しなければなりません。ローカル通信 ID は、以下の関数でユニーク ID(20 bit)から変換して生成します。
bit32 nn::uds::CreateLocalCommunicationId(bit32 uniqueId, bool isDemo = false);
uniqueId
には、弊社業務部がタイトルごとに割り当てたユニーク ID を指定します。異なるタイトル間で通信を行う際は、どちらか片方の ID を指定してください。ID 0 はライブラリで予約されていますので指定しないでください。
isDemo
には、ユニーク ID が共通となる製品版とダウンロードアプリ型体験版の間で UDS 通信を行いたくない場合に、体験版側のみ true
を指定してください。体験版との通信を行うかどうかに関わらず、製品版では必ず false
を指定してください。
弊社からユニーク ID を割り当てられていないタイトルや実験プログラムで UDS 通信を使用する場合は、uniqueId
にゲームソフト試作用コード(0xFF000~0xFF3FF)を指定してください。ただし、製品版では必ず弊社より割り当てられたユニーク ID を指定してください。
3.1.3.2. ネットワークの新規構築
UDS 通信のネットワークの構築は、Master が nn::uds::CreateNetwork()
を呼び出すことで行われます。
nn::Result nn::uds::CreateNetwork( u8 subId, u8 maxEntry, bit32 localId, const char passphrase[], size_t passphraseLength, u8 channel); nn::Result nn::uds::CreateNetwork( u8 subId, u8 maxEntry, bit32 localId, const char passphrase[], size_t passphraseLength, u8 channel, const void* pData, size_t dataSize);
subId
は通信モード識別用 ID です。アプリケーション側で自由に設定することができます。たとえば、"通信対戦"や"データ交換" など、通信モードの区別に使用することを想定しています。subId
に 0xFF は指定しないでください。0xFF は Client がすべての subId
を対象にネットワークを探索する際に使用されます。
maxEntry
にはネットワークに接続できるノードの最大数を指定します。Master、Client を含めて最大 16 まで指定可能ですが、13 台以上での同時接続は十分な動作確認が行われていません。そのため、現時点では 13 以上を指定した場合の動作は保証の範囲外とします。Master 自身が台数に含まれていることと、Spectator が台数に含まれないことに注意してください。
localId
にはユニーク ID から生成したローカル通信 ID を指定します。
passphrase
には無線レイヤの暗号化に使用する暗号鍵の種となる文字列(パスフレーズ)を指定します。localId
と passphrase
の組み合わせが、Master と Client/Spectator で一致する場合に通信が成立します。極端に短いものや連想されやすいもの、ほかのタイトルでも使っているものなどを指定して第三者に特定されたり、文字列そのものが漏洩したりしないように注意してください。
passphraseLength
には passphrase
の長さを指定します。指定可能な値の範囲は 8 以上、255 以下です。
channel
には UDS 通信で使用するチャンネルを 0, 1, 6, 11 のいずれかから指定します。0 を指定した場合はチャンネルを自動で選択します。チャンネルの指定はデバッグ用途でのみ使用します。
pData
と dataSize
には、構築と同時にビーコンに独自データをセットしたい場合に、データとそのバイトサイズ指定します。dataSize
に 0 を指定したときに限り、pData
に NULL
を指定してもエラーにはなりません。ビーコンにセットするデータについては「3.1.3.5. 独自データのビーコンへのセット」を参照してください。
製品版の本体での実行時には、チャンネルが常に自動で選択されます。
チャンネルを指定したネットワークの構築を行うには、デバッグモードに設定(Config ツールで「Debug Setting」の「Debug Mode」を「enable」に設定)する必要があります。
UDS 通信ではネットワークを再構築したことを識別するために、ネットワークを構築するたびに TemporaryID を乱数生成しています。ただし再構築までの時間が短い場合、かつ Client からの接続がなく、機器間の通信が成立していない場合は TemporaryID を更新しません。
3.1.3.3. 周囲にあるネットワークの探索
Client/Spectator は、同じローカル通信 ID で構築されたネットワークが周囲にあるかどうかを nn::uds::Scan()
を呼び出して探索する必要があります。
nn::Result nn::uds::Scan( void * pBuffer, size_t bufferSize, u8 subId, bit32 localId);
pBuffer
には 探索で発見したネットワークの情報を格納するバッファのアドレスを指定します。バッファに必要なサイズはネットワーク 1 つあたり 1 KByte 程度です。バッファのサイズを超える分のネットワークの情報は格納されません。
subId
は探索の対象とする通信モード識別用 ID です。この引数で指定した値と同じ subId
で構築されたネットワークが探索の対象となります。subId
に 0xFF を指定したときは、すべてのネットワークが探索の対象となります。
localId
にはユニーク ID から生成したローカル通信 ID を指定します。
開発用の本体(デバッガ、開発実機)と製品版の本体とでは、双方とも相手をスキャンで発見することができません。
次に、nn::uds::Scan()
を実行して得られたバッファの内容を nn::uds::ScanResultReader
クラスのコンストラクタに渡して、探索結果を解析するインスタンスを生成します。
生成したインスタンスからは、メンバ関数の GetFirstDescription()
で最初の探索結果を受け取ることができます。GetNextDescription()
では、その次の探索結果を受け取ることができます。なお、探索結果は受信信号強度(RSSI 値)の大きなものから格納されています。
探索結果は nn::uds::NetworkDescriptionReader
クラス型のインスタンスです。このインスタンスのメンバ関数でネットワーク情報の解析を行うことができ、GetNetworkDescription()
でネットワーク情報を、GetNodeInformationList()
でノードの一覧を、GetRadioStrength()
で電波強度(4 段階)を取得することができます。また、CompareWith()
ではネットワーク情報の比較を行い、比較結果を「完全に一致」、「同じネットワークだがデータが異なる」、「違うネットワーク」の 3 種類で取得することができます。
3.1.3.4. ネットワークへの接続
Client/Spectator は、解析された探索結果から得られる情報のうち、GetNetworkDescription()
で取得したネットワーク情報を元に nn::uds::ConnectNetwork()
を呼び出して既存のネットワークに接続することができます。
nn::Result nn::uds::ConnectNetwork( const NetworkDescription & networkDescription, ConnectType type, const char passphrase[], size_t passphraseLength);
networkDescription
にはネットワークの探索で得られたネットワーク情報を指定します。
type
には接続する端末の種別を指定します。端末の種別は以下の 2 種類から選択することができます。
設定値 |
説明 |
---|---|
|
Client(送受信が行え、接続時にノード ID が付与されるノード)としてネットワークに接続します。 |
|
Spectator(受信のみが可能な、接続時にノード ID が付与されないノード)としてネットワークに接続します。 |
Spectator としてネットワークに接続できるかどうかは、ネットワーク情報(nn::uds::NetworkDescription
クラス)のメンバ関数 CanConnectAsSpectator()
の返り値が true
であるかどうかで判断することができます。
passphrase
には無線レイヤの暗号化に使用する暗号鍵の種となる文字列(パスフレーズ)を指定します。Master が構築したネットワークのローカル通信 ID とパスフレーズの組み合わせと一致する場合に通信が成立します。
passphraseLength
には passphrase
の長さを指定します。指定可能な値の範囲は 8 以上、255 以下です。
満員のネットワークに対して接続したときに返されるエラーは ResultAlreadyNetworkIsFull
、解散済みのネットワークに対して接続したときに返されるエラーは ResultNotFoundNetwork
です。
3.1.3.5. 独自データのビーコンへのセット
ネットワークを構築した Master は、最大 NET_DESC_APPDATA_SIZE_MAX
バイトの独自データを、ネットワークのビーコンに設定することができます。
nn::Result nn::uds::SetApplicationDataToBeacon( const void* pData, size_t dataSize);
pData
と dataSize
には、独自データの先頭アドレスとそのサイズを指定します。
ビーコンに設定する独自データは、セッションの状態(対戦者待ちや対戦中など)やコメントなどを設定し、ネットワークを探索してきた Client や Spectator にネットワークの状態を知らせるといった、アプリケーション独自の接続状況の管理に使用することを想定しています。ただし、独自データをセットし直しても、すでにネットワークに接続されている Client や Spectator には変更が通知されないことに注意してください。
独自データの内容は暗号化されません。ビーコンは PC などで容易に傍受できることに注意してください。ビーコンの改竄は検出できますが、傍受したビーコンをそのまま利用される可能性があります。特に、ビーコンで配信した特別なデータが不正に拡散されないようにする場合は、傍受したビーコンが別の場所で配信されても、ビーコンを受信するだけでは受け取れないようにする対策(Spectator としてネットワークに接続するなど)が必要になります。
3.1.3.6. ビーコンにセットされた独自データの取得
Master がビーコンにセットした独自データを Client/Spectator が取得できるタイミングは、ネットワークを探索した際に取得したネットワーク情報のメンバ関数を呼び出したときと、ネットワーク接続中に取得用の関数を呼び出したときです。
size_t nn::uds::NetworkDescription::GetApplicationData( bit8* buffer, const size_t bufferSize) const; nn::Result nn::uds::GetApplicationDataFromBeacon( void* pBuffer, size_t* pDataSize, size_t bufferSize);
buffer
と bufferSize
、pBuffer
と bufferSize
にはそれぞれ、独自データを格納するバッファの先頭アドレスとそのサイズを指定します。バッファのサイズには独自データの最大サイズ(NET_DESC_APPDATA_SIZE_MAX
)を指定し、バッファは最大サイズで確保してください。
取得した独自データのサイズは、nn::uds::NetworkDescription::GetApplicationData()
は返り値で、nn::uds::GetApplicationDataFromBeacon()
は pDataSize
で、それぞれ確認することができます。
3.1.4. 通信
これまでの処理で UDS 通信でデータの送受信を開始する準備が整いました。続いてデータの送受信について説明します。
3.1.4.1. 端点の生成
データの送受信を行う前に、ネットワークの端点(Endpoint)を生成する必要があります。端点はデータの送受信を行う "入り口" のようなもので、nn::uds::CreateEndpoint()
で生成することができます。
現時点では、同時に生成可能な端点は 16 個までです。
送信用と受信用に別々の端点を生成することを推奨します。同じ端点を送信と受信で使うことは可能ですが、送受信を行っている最中の端点を別の送受信に使用しないように注意してください。
受信用の端点は、nn::uds::Attach()
を呼び出し、ポート番号とノード ID を関連付けることでデータを受信できるようになります。この時、ライブラリは nn::uds::Initialize()
で確保したメモリブロック上に受信用バッファを作成します。
nn::Result nn::uds::Attach( EndpointDescriptor * pEndpointDesc, u16 srcNodeId, u8 port, size_t receiveBufferSize = ATTACH_BUFFER_SIZE_DEFAULT);
pEndpointDesc
には nn::uds::CreateEndpoint()
で生成した端点を指定します。
srcNodeId
には送信元となるノード ID を指定します。指定されたノード以外から送られたデータは受信用バッファに格納されません。BROADCAST_NODE_ID
を指定すると、すべてのノードから送信されたデータを受信用バッファに格納します。
port
には受信するポート番号を指定します。指定したポート番号以外からのデータは受信用バッファに格納されません。ポート番号 0 はライブラリで予約されていますので指定しないでください。
receiveBufferSize
には、確保する受信用バッファのサイズを ATTACH_BUFFER_SIZE_MIN
以上で指定しなければなりません。nn::uds::Initialize()
で確保したメモリブロックから確保されますので、受信バッファの合計が確保したメモリブロックのサイズよりも大きくならないよう注意してください。また、32 Byte ごとにメモリを確保しますので、32 の倍数にすることでメモリの使用効率がよくなります。デフォルトでは ATTACH_BUFFER_SIZE_DEFAULT
(UDS_PACKET_PAYLOAD_MAX_SIZE
の 8 倍)の領域を受信用バッファとして確保します。
データの受信時に送信元のノード ID を取得することもできますので、複数のノードが同じポートに送信するデータを 1 つの端点で受け取っていても送信元を特定することができます。ただし、受信用バッファはリングバッファのため、サイズが十分でなければ先に受信したパケットから消失する可能性があります。
ネットワークからの切断時など、端点を介しての通信が不要になったときは、nn::uds::DestroyEndpoint()
で端点を破棄してください。受信用の端点を破棄したときは、nn::uds::Attach()
を呼び出したときに作成した受信用バッファはライブラリによって解放されます。
3.1.4.2. 送信
生成した端点を通して、ノードに nn::uds::SendTo()
でデータを送信します。送信処理が完了するまでブロックされることに注意してください。
nn::Result nn::uds::SendTo( const EndpointDescriptor & endpointDesc, const void * data, size_t dataSize, u16 destNodeId, u8 port, bit8 option = 0x00);
endpointDesc
には生成した端点を指定します。
data
には送信するデータへのポインタを、dataSize
にはそのサイズを指定します。1 回の呼び出しで送信できるデータの最大サイズは UDS_PACKET_PAYLOAD_MAX_SIZE
で定義されています。dataSize
に最大サイズを超える値を指定した場合、ResultTooLarge
のエラーが返されます。送信データの先頭アドレスが 4 Byte アライメントでない場合、ResultMisalignedAddress
のエラーが返されます。
destNodeId
には送信先のノード ID を指定します。BROADCAST_NODE_ID
(= 0xFFFF)を指定すると送信データはブロードキャストされます。Spectator が受信することのできるデータは、この方法でブロードキャストされたデータのみです。特定の Spectator に向けての送信はできません。また、Spectator はデータの送信を行うことができません。
port
にはポート番号を指定します。ポート番号 0 はライブラリで予約されていますので指定しないでください。
option
には、以下のフラグの論理和を指定します。これらのオプション設定を利用しない場合は、この引数の指定を省略する(0x00 を指定する)ことができます。オプション設定を使用しない場合は、Client から Client への Unicast 通信は Master を経由します。また、経由のために Master へ送信されるパケットを除き、送信バッファによるパケットのバッファリングが有効になります。
-
NO_WAIT
このフラグを指定した場合、UDS 内部の送信バッファにデータをためず、即座に送信を行いますが、送信が完了するまで処理がブロックされます。
このフラグを指定しない場合は、なるべく送信遅延が起こらないように、送信バッファを無線送信の一回分(UDS_PACKET_PAYLOAD_MAX_SIZE
とほぼ同等)という小さなサイズで確保します。これが一杯になった、もしくはバッファにある最も古いデータがためられてから一定時間(最大送信遅延時間)経った場合に、ライブラリがバッファ内のデータを送信します。そのため、このフラグを指定せずに送信を繰り返し実行した場合の処理には、バッファにデータをためるだけのケースとバッファが一杯になり送信処理も行うケースが存在し、後者は前者に比べてブロック時間が長くなります。
なお、最大送信遅延時間のデフォルトは 10 ms に設定されていますが、nn::uds::SetMaxSendDelay()
により、5~100 ms の範囲で指定可能です。
NO_WAIT
を指定するかどうかは、通信に求めるリアルタイム性に依存します。
送信先にデータが到達するまでの時間は最小限であることが望ましく、データ交換にかかる時間をなるべく短くしたい場合は NO_WAIT
を指定してください。
インターネット通信と同程度のレイテンシが許容できる場合は、NO_WAIT
を指定しないことで、より多くのパケットが扱えるようになります。複数のパケットをまとめて送信するようになるため、サイズの小さいパケットを大量に送信したときは、見た目の通信帯域が向上します。スループットは向上しませんが、1 秒間に送信できるパケットは増えます。
-
FORCE_DIRECT_BC
どの送信先に対してでも、Broadcast 通信による送信を行います。Client 間の Unicast 通信も Master を経由しません。送信レイテンシは低くなりますが、通信可能範囲外のノードを考慮する必要があります。
UDS 通信による送信の流れ
SendTo()
で処理されたパケットは、NO_WAIT
オプションが設定されていなければ送信バッファに可能な限りためられてから、NO_WAIT
オプションが設定されていればすぐに、ライブラリによって無線フレーム(無線規格の通信単位)に詰められて送信されます。
各ノードが受信した無線フレームはライブラリによってパケットに分解され、それぞれの受信バッファに振り分けられます。このとき、Master 以外は自分宛ではないパケットを捨てますが、スター型ネットワークである UDS 通信で Client 間通信を行うために、Master はほかのノード宛のパケットを自身の送信バッファにためて再送するように設計されています。
送信パケットの順序の保証について
同じ送信先に同じオプション設定(FORCE_DIRECT_BC
)で送信する場合には、パケットの順序は保証されています。
オプションを指定しない場合やオプションを使い分けた場合、経由するパスの違いから Unicast 通信と Broadcast 通信の間でパケットの順序が入れ替わる可能性がありますが、Unicast 通信で送信するパケットと Broadcast 通信で送信するパケットの順序が入れ替わること自体が問題となるケースはないと思われますので、基本的にはパケットの順序が保証されると考えてください。
送信の遅延について
ライブラリは無線通信の物理レイヤでの効率を良くするために、サイズの小さなパケットをまとめて送信するように設計されています。そのため、nn::uds::SendTo()
により送信されたデータは設定された送信遅延時間の間、送信を待つ可能性があります。この送信遅延を回避し、回線効率よりもレイテンシを重視したい場合は nn::uds::SendTo()
の引数 option
に NO_WAIT
を含む指定を行ってください。
nn::uds::SetMaxSendDelay()
では、送信遅延時間の最大値を設定することができます。
nn::Result nn::uds::SetMaxSendDelay(nn::fnd::TimeSpan maxDelay);
maxDelay
には、最大送信遅延時間を 5~100 ms の範囲で指定してください。デフォルトは 10 ms です。
この関数は、ネットワークに接続していない状態でのみ実行可能です。
3.1.4.3. 受信
Attach()
によってポート番号とノード ID との関連付けられた端点を介してデータの受信を行います。送信元のノード ID を取得する必要がない場合は nn::uds::Receive()
を、取得する必要がある場合は nn::uds::ReceiveFrom()
を呼び出してください。ここでは、nn::uds::ReceiveFrom()
について説明します。
nn::Result nn::uds::ReceiveFrom( const EndpointDescriptor & endpointDesc, void * pBuffer, size_t * pReceivedSize, u16 * pSrcNodeId, size_t bufferSize, bit8 option = 0x00);
endpointDesc
には nn::uds::CreateEndpoint()
で生成し、nn::uds::Attach()
で関連付けた端点を指定します。
pBuffer
には受信データ格納先のポインタを指定します。バッファの先頭アドレスとサイズは 4 Byte のアライメントである必要があります。バッファの先頭アドレスとサイズが 4 Byte アライメントでない場合、それぞれ ResultMisalignedAddress
と ResultMisalignedSize
のエラーが返されます。
pReceivedSize
には受信データサイズの格納先のポインタを指定します。1 回の呼び出しで受信できるデータの最大サイズは UDS_PACKET_PAYLOAD_MAX_SIZE
で定義されています。
pSrcNodeId
には送信元のノード ID の格納先のポインタを指定します。
bufferSize
には受信データ格納先のバッファのサイズを指定します。
option
に NO_WAIT
を指定すると、データを受信していない場合でも即座に終了します。option
を指定しない場合は、データを受信もしくはエラーが発生するまで受信を終了しません。
受信パケットが壊れている可能性について
UDS 通信では、データ破壊のチェックはハードウェアでは行われていますが、速度と適応性を重視しているため、ソフトウェアレベルでパケット単位の完全性チェックは行っていません。
壊れたり改竄されたりすると困るようなデータ(アイテム交換など)の場合は、アプリケーションで、ファイル単位などの適切な粒度でチェックを行ってください。改竄されても特に困らない場合やリアルタイム性が要求される場合は、パケット単位でのチェックをする必要はありません。
3.1.4.4. パケット消失の要因
ここでは、UDS 通信でパケットが消失する要因を説明します。
環境の悪化
通信する本体間の距離による電波強度の低下、第三者が送信する電波による干渉、ノイズなどの外的要因によって環境が悪化するとパケットの消失率が高くなります。
パケット消失率の実測値(ライブラリで保証するものではありません)は、良好な環境では 1% 程度、接続が維持できる程度に劣悪な環境では最大 10% 程度です。経験的な情報ですが、システムやデバイスでの処理が追いつかなくなると、それまでは低かった消失率が急激に高くなる傾向があります。
回線速度
UDS 通信の最大通信量の目安は、ネットワーク全体で秒間 500~600 パケット程度で、これを大きく超えた場合や周囲に別のネットワークが存在することで通信帯域が圧迫された場合にパケットが消失することがあります。
このケースでは、消失率は送信されるパケット量に左右されますので、接続台数を減らすとパケットの消失率が低くなります。
現在、com_demo1 デモのように単純な通信プログラムで単一のネットワークのみが通信を行った場合、秒間 800 パケットを超えたあたりで消失率が高くなる現象が確認されています。
受信バッファのバッファあふれ
アプリケーションが受信関数を呼び出す頻度よりもパケットを受信する頻度が高い場合、受信バッファがあふれて、もっとも古いパケットが消失することがあります。このケースでは、通信負荷の上昇に伴って徐々に消失率が高くなります。受信バッファのサイズを大きくすることで一時的な負荷の増大には対応できますが、恒常的なバッファあふれには対応できないことに注意してください。
SNAKE と CTR のプロセッサの処理速度の違いで通信に問題が生じます。そのため、プロセッサの処理速度に大きく依存した通信処理にしないでください。
たとえば、送信過多にならないようにパケットの送信後にタイマーを用いた遅延処理を行う、送信側は受信側の受信状況を必要に応じて確認するなどの対応を行ってください。
3.1.4.5. 通信の効率を高めるには
UDS 通信のベースとなっている規格(IEEE 802.11b/g)では、同一チャンネルのネットワーク全体で無線帯域を共有しています。また、無線フレームの送信には干渉回避のための無通信時間(バックオフ)があるため、同じサイズのデータを送信する場合でも、無線フレームで送信可能な最大のデータサイズまでパケットをまとめるなどして、無線フレームの送信回数を減らすことで効率がよくなります。
UDS 通信のネットワークトポロジは、Master にすべての Client が接続するスター型です。通常の Unicast 通信では、Master から Client または Client から Master への送信が 1 パスで行われますが、Client から Client への送信は Master を経由するため 2 パスで行われます。同じデータを複数のノードに送信する場合は、Broadcast 通信を利用することによりネットワーク全体で送信されるパケットの数を減らすことができます。ネットワークに接続されているすべてのノードに一斉に送信する Broadcast 通信は 1 パスで行われ、しかもノードの個数に関わらず 1 回で送信することができます。ただし、Master からの Broadcast 通信ではすべてのノードが通信可能範囲に存在することが保証されていますが、Client からの Broadcast 通信では通信可能範囲外にノードが存在する可能性を考慮する必要があります。
メッシュ型のネットワークを UDS 通信で実装する
メッシュ型のネットワークではすべてのノードが同じ処理を行うため、通信処理の実装が比較的容易になります。しかし、すべてのノードがほかのすべてのノードにパケットを送信するため、通信台数が増加するとネットワーク全体で単位時間に送信するパケットの数が指数関数的に増加してしまいます。
UDS 通信では、Broadcast 通信を利用することで送信するパケットの数をノード数に比例する値にまで減らすことができますが、Client からの Broadcast 通信ではパケットが届かないノードが存在する可能性に注意しなければなりません。そのため、パケットの不達を検知した Client がパケットの再送を要求する処理が必要となります。再送処理は Unicast 通信で行うことになりますので、往復かつ Master を経由することによるレイテンシを考慮しなければなりません。
通常、Broadcast 通信ではすべてのノードに同じパケットを送信します。すべてのノードに同じデータを送信する場合は問題ありませんが、ノードごとに送信するデータが異なる場合はパケットの数を減らすことができません。送信オプションに FORCE_DIRECT_BC
を指定した場合は Unicast 通信も Broadcast 通信のように 1 パスで送信され、受信したノードで自分宛ではないパケットが破棄されます。この機能とパケットをまとめて送信する機能を利用することで、ネットワーク全体で単位時間に送信するパケットの数を減らすことができます。ただし、この方法では通信可能範囲外にノードが存在する可能性を考慮する必要があります。
3.1.5. 切断
ここでは Master による Client/Spectator の切断および、Client/Spectator によるネットワークからの切断の手順を説明します。
3.1.5.1. Master による Client/Spectator の切断
Master は次の方法で Client/Spectator をネットワークから切断することができます。
- 特定の Client をネットワークから切断
- すべての Client をネットワークから切断
- すべての Spectator をネットワークから切断
Client をネットワークから切断するには、nn::uds::EjectClient()
を呼び出してください。
nn::Result nn::uds::EjectClient(u16 nodeId);
特定の Client をネットワークから切断するには、nodeId
に切断したい Client のノード ID を指定してください。
すべての Client を一括でネットワークから切断する場合は、nodeId
に BROADCAST_NODE_ID
(= 0xFFFF)を指定してください。
Spectator をネットワークから切断するには、nn::uds::EjectSpectator()
を呼び出してください。
nn::Result nn::uds::EjectSpectator(void); nn::Result nn::uds::AllowToSpectate(void);
nn::uds::EjectSpectator()
は、すべての Spectator をネットワークから切断し、以降は Spectator の接続を拒否するようになります。ネットワークを構築したときの初期設定では、Spectator の接続が許可されています。再度 Spectator の接続を許可するには、nn::uds::AllowToSpectate()
を呼び出してください。
3.1.5.2. Master によるネットワークの破棄
Master はすべての Client/Spectator を切断し、ネットワークを破棄することができます。ネットワークを破棄するには、nn::uds::DestroyNetwork()
を呼び出してください。
nn::Result nn::uds::DestroyNetwork(void);
Master がネットワークを破棄した場合、Client が受け取る切断要因(nn::uds::ConnectionStatus
構造体の disconnectReason
)は DISCARDED_FROM_NETWORK
です。ただし、通信環境によっては CONNECTION_LOST
となる可能性があります。
3.1.5.3. Client/Spectator による切断
Client/Spectator が現在接続中のネットワークから離脱するには、nn::uds::DisconnectNetwork()
を呼び出してください。
nn::Result nn::uds::DisconnectNetwork(void);
3.1.5.4. Master による Client からの接続の制御
Master は Client のネットワークへの接続の拒否や許可を制御することができます。
nn::Result nn::uds::DisallowToConnect(bool isDisallowToReconnect = false); nn::Result nn::uds::AllowToConnect();
nn::uds::DisallowToConnect()
は、現在の接続を維持したまま、以降の Client からの接続を拒否します。ただし、isDisallowToReconnect
に false
を指定した場合は、この関数を呼び出したあとに切断された Client であれば再接続を許可します。ネットワークを構築したときの初期設定では、Client の接続が許可されています。
nn::uds::AllowToConnect()
は、Client からの接続の拒否を解除します。nn::uds::EjectSpectator()
による Spectator の接続拒否は解除されません。
3.1.5.5. スリープ状態への遷移、無線オフモードへの切り替えによる切断
事前に UDS 通信を終了させずにスリープ状態へ遷移した場合や、無線スイッチにより無線オフモードに切り替わった場合、UDS 通信の内部ステートはエラー(nn::uds::STATE_ERROR
)へと遷移します。この状態で nn::uds::Finalize()
以外の関数を呼び出すと、スリープ状態へ遷移していた場合は nn::uds::ResultInvalidState
を、無線オフモードに切り替えていた場合は nn::uds::ResultWirelessOff
を返すようになります。そのため、内部ステートがエラーから復帰する場合は nn::uds::Finalize()
で UDS 通信を終了させ、再び初期化から行わなければなりません。無線オフモードへの切り替えで切断された場合は、無線オンモードに切り替わってから初期化する必要があります。
UDS 通信中にスリープ状態へと遷移する場合、アプリケーションは事前に UDS 通信を終了してからスリープ状態に遷移することを推奨します。
3.1.6. 終了処理
nn::uds::Finalize()
を呼び出して、UDS 通信を終了します。
nn::Result nn::uds::Finalize(void);
このとき、ライブラリは nn::uds::Initialize()
で確保したメモリブロック領域と nn::os::Event
インスタンスを解放します。UDS 通信を使用するアプリケーションは、その終了までに UDS 通信を終了させていなければなりません。「3DS プログラミングマニュアル - システム編」の「終了処理の注意事項」を参照してください。
3.1.7. ネットワーク状態の同期
UDS ライブラリは、ネットワークに接続しているノードの以下の情報について、自動的に同期を取っています。
- 現在の接続数
- 各ノードのノード ID
- 各ノードのローカルフレンドコード(プライバシー保護のためスクランブルがかけられています)
- 各ノードのユーザー名
アプリケーションでノードの情報の更新を明示的に通知する必要はありません。
また、これらの情報は Master/Client のすべてのノードで同期を取りますので、Client 側でも各ノードの情報を取得したり、新規接続・切断を検知したりすることができます。
3.1.8. ノード ID の割り当て
UDS 通信では、ネットワークに接続しているノードをノード ID という 16 bit の番号で管理しています。Master と Client にはノード ID が割り当てられますが、Spectator にはノード ID が割り当てられません。
同じネットワーク内では、MP 通信の AID と異なり、一度使用されたノード ID がその後別のノードに割り当てられることはありません。また、Client が同じネットワークに再接続すると、前回の接続時と同じノード ID が割り当てられます。
再接続時に前回と同じノード ID を割り当てるため、Master は内部で 64 台分の切断済みノード情報を保持しています。これを超える台数の切断が発生した場合は、もっとも昔に切断した Client のノード情報が破棄されます。切断済みノード情報から破棄されたノードがネットワークに再接続されると、前回とは異なるノード ID が割り当てられます。 なお、以下のノード ID は予約されているため、Client に割り当てられることはありません。
予約済みノードID |
説明 |
---|---|
0 |
存在しないノードです。ライブラリの内部処理で使用します。 |
1 |
Master に固定で割り当てられます。 |
0xFFFF( |
ブロードキャストする場合に指定するノード ID です。 |
3.1.9. 各種情報の取得
自身の接続状態、リンクレベルなどの情報を取得することができます。
3.1.9.1. 接続状態の取得
nn::uds::GetConnectionStatus()
を呼び出して、現在の接続状態を取得することができます。
nn::Result nn::uds::GetConnectionStatus(nn::uds::ConnectionStatus* pStatus);
pStatus
には接続状態を受け取る nn::uds::ConnectionStatus
構造体へのポインタを渡してください。構造体は以下のように定義されています。
struct nn::uds::ConnectionStatus { State nowState; DisconnectReason disconnectReason; u16 myNodeId; bit16 updateNodeBitmap; u16 nodeIdList[NODE_MAX]; u8 nowEntry; u8 maxEntry; bit16 slotBitmap; };
nowState
には現在のステートが格納されます。ステートについては「3.1.1. UDS 通信の内部ステート」を参照してください。
disconnectReason
には切断理由が格納されます。格納される値と切断理由の対応は下表のとおりです。
値 |
切断理由 |
---|---|
|
まだ通信をしていない状態です。 |
|
切断されていません。接続は維持されています。 |
|
自身の操作により接続を切断しました。 |
|
スリープ状態や無線オフモードへの遷移など、3DS 本体からの要求で接続が切断されました。 |
|
Master の操作により接続が切断されました。 |
|
通信状況の悪化により接続が維持できなくなりました。 |
|
切断の原因は不明です。 |
myNodeId
には自身のノード ID が格納されます。Spectator の場合は 0xFFFF が格納されています。
updateNodeBitmap
には、前回の呼び出しから変更のあったノードを示すビットマップ情報が格納されます。1 になっているビットに対応する要素が変更のあったノードのノード ID です。ビットマップの 1 ビットが nodeIdList
の 1 要素に対応し、下位ビットから上位ビットの順に先頭から最後の要素へと対応しています。
nowEntry
と maxEntry
には、ネットワークに接続しているノードの数と同時接続の最大数が格納されます。nowEntry
には現在の状況が反映されますが、maxEntry
は通信中に変化することはありません。
slotBitmap
には、ノード情報が格納されているスロットを示すビットマップ情報が格納されます。前回の差分ではなく、現在の状況が反映されています。
3.1.9.2. リンクレベルの取得
nn::uds::GetLinkLevel()
を呼び出して、現在のリンクレベル(通信品質)を取得することができます。
nn::Result nn::uds::GetLinkLevel(nn::uds::LinkLevel* pLinkLevel); nn::uds::LinkLevel nn::uds::GetLinkLevel();
pLinkLevel
にはリンクレベルを受け取る変数へのポインタを渡してください。リンクレベルは下表のように定義されています。
値 |
説明 |
---|---|
|
通信品質が非常に悪い状態、もしくは通信が成立していません。 |
|
通信品質が悪い状態です。 |
|
通信品質はあまり良くありません。 |
|
通信品質は良好です。 |
電波強度はアプリケーションで常に表示しなければならない情報ではなく、この関数は処理をブロックするため、高頻度(ゲームフレームごとなど)で呼び出すことは推奨しません。
Client/Spectator がネットワークに接続する前に、指標として電波強度を表示する場合は、nn::uds::Scan()
の結果から取得した nn::uds::NetworkDescriptionReader
クラスの GetRadioStrength()
を利用してください。
3.1.9.3. ノード情報の取得
nn::uds::GetNodeInformation()
を呼び出して、指定したノードのユーザー情報を取得することができます。
nn::Result nn::uds::GetNodeInformation(nn::uds::NodeInformation* pNodeInfo, u16 nodeId);
pNodeInfo
には、ノード情報を格納する nn::uds::NodeInformation
構造体へのポインタを指定します。
nodeId
には、ノード情報を取得したいノードのノード ID を指定してください。
3.1.9.4. チャンネルの取得
3.2. ニンテンドー3DSダウンロードプレイ
ニンテンドー3DSダウンロードプレイ(以降、3DSダウンロードプレイと呼びます)は、DSダウンロードプレイと同じように、無線通信で親機から子機プログラムを配信する機能です。 3DSダウンロードプレイは以下のような特徴を持っています。
- 配信可能なプログラム(子機プログラム)のサイズは 32 MByte(33,554,432 Byte)まで
- 配信可能なプログラム(子機プログラム)は標準モードで動作するアプリのみ
- 必要であれば子機プログラムの配信前に、無線通信でシステム更新を行う
- 子機プログラムでローカル通信をするときに、親機を識別するための情報を取得可能
- 子機プログラムは専用領域に保存され、ほかの子機プログラムがダウンロードされたときに上書きされる
- 子機プログラムは HOME メニューには表示されない(HOME メニューからは起動できない)
開発用の本体(デバッガ、開発実機)と製品版の本体との間では 3DSダウンロードプレイを行うことができません。
子機プログラムのサイズはインポート後のサイズです。DevMenu の SDMC タブまたは HIO タブで子機プログラムを選択したときに表示される Required Size で確認することができます。
アプリケーションで 3DSダウンロードプレイに対応するには、CTR-SDK で用意されている DLP ライブラリを使用します。
クライアント側のプログラムはシステムアプリで用意されていますので、配信のためにアプリケーションが準備しなければならないのは、配信する子機プログラムと配信を制御するためのサーバーコードです。また、擬似ダウンロードプレイ子機の機能をサポートするためのクラスも用意されています。通信処理などはライブラリ内部で行われますので、アプリケーションは状態を確認して必要な関数を呼び出すだけです。
以下の図は、3DSダウンロードプレイで行うサーバー側での処理の流れを、親機(サーバー)と子機(クライアント)の状態の遷移を軸に図示したものです。
3DSダウンロードプレイを行うと、本体バージョンの低い開発実機上のクライアントはダウンロード完了後に FatalError になることがありますので、サーバーとクライアントの本体バージョンをそろえておく必要があります。実際の製品では、カードアプリならば無線通信によるクライアントのシステムアップデートが行われます。ダウンロードアプリならば本体更新開始直後に通信が中断します。
3.2.1. 初期化
サーバーコードで必要な関数は nn::dlp::Server
クラスにまとめられています。DLP ライブラリ自体の初期化は必要なく、nn::dlp::Server
クラスの Initialize()
を呼び出してサーバーの初期化を行います。
static nn::Result nn::dlp::Server::Initialize( bool* pNotice, nn::Handle eventHandle, u8 maxClientNum, u8 childIndex, void* pBuffer, size_t bufferSize, size_t blockBufferSize = MIN_NETWORK_BLOCK_BUFFER_SIZE * 2, size_t blockBufferNum = MIN_NETWORK_BLOCK_BUFFER_NUM); static size_t nn::dlp::Server::GetBufferSize(u8 maxClientNum, size_t blockBufferSize = MIN_NETWORK_BLOCK_BUFFER_SIZE * 2, size_t blockBufferNum = MIN_NETWORK_BLOCK_BUFFER_NUM);
pNotice
には、ダウンロードプレイの警告メッセージを表示しなければならないかどうかが格納されます。true
が返されたときは、ガイドラインに従ってメッセージを表示してください。メッセージは、ダウンロードアプリがサーバーになったときに、システムバージョンの低いクライアントからの接続が警告なく切断されることへの警告メッセージです。なお、今後の更新でシステムがダウンロードアプリによるシステムアップデートに対応した場合、メッセージの表示は必要なくなり pNotice
に true
が返されなくなります。システムアップデートの対応状況に応じてメッセージ表示の有無を切り替えられるよう、必ず pNotice
の結果に応じてメッセージを表示してください。また、カードアプリでは pNotice
に true
が返されることはありません。しかし、pNotice
に応じたメッセージ表示を組み込んでおくと、 カードアプリをダウンロードアプリとしてリリースする際にソースコードの変更が不要になります。
eventHandle
には、3DSダウンロードプレイからの通知を待つ nn::os::Event
クラスのハンドルを渡します。ハンドルを渡すイベントはアプリケーションで初期化する必要があります。
maxClientNum
には、サーバーに接続可能なクライアントの最大数を 1 ~ MAX_CLIENT_NUM
の範囲で指定します。
childIndex
には、配信する子機プログラムのインデックス(rsf ファイルの ChildIndex)を指定してください。クライアントに表示されるアイコンやタイトルなどは、この引数で指定された子機プログラムのヘッダ情報から取得されます。
pBuffer
と bufferSize
には作業バッファとそのサイズ、blockBufferSize
と blockBufferNum
にはブロックバッファ(子機プログラムの読み出しバッファ)のサイズと個数を渡します。作業バッファはアプリケーションでデバイスメモリ以外から確保し、先頭アドレスが nn::dlp::Server::BUFFER_ALIGNMENT
( 4096 Byte ) アライメントでサイズが GetBufferSize()
に maxClientNum
、blockBufferSize
、blockBufferNum
を渡して取得したサイズ以上の nn::dlp::Server::BUFFER_UNITSIZE
( 4096 ) の倍数でなければなりません。引数に範囲外の値を指定した場合、GetBufferSize()
は不定値を返します。
ServerWithName
クラスは Server
クラスの Initialize()
にユーザー名の指定機能を追加したものです。このクラスを利用してユーザー名を設定する場合は、必ず UGC のガイドラインに従って NG ワードのチェックを行ってください。チェックの結果、NG ワードが含まれている場合は Initialize()
に渡す構造体の isNgUserName
を true
にし、userName
には伏せ文字などの加工を行っていないユーザー名をそのまま設定します。ServerWithName
クラスの Initialize()
以外の動作は Server
クラスと同じです。
Initialize()
で返される可能性のある返り値とその原因を以下に示します。
返り値 |
原因 |
---|---|
|
処理に成功しました。 |
|
無線通信モジュールがほかの無線処理で占有されています。インフラストラクチャ通信などを終了させてください。 |
|
範囲外の値を引数に指定しています。 |
|
不正なポインタを引数に指定しています。 |
|
不正なハンドルを引数に指定しています。 |
|
ライブラリ内で回復不能なエラーが発生しました。 |
|
指定されたインデックスの子機プログラムが存在しないか、カードが抜けている可能性があります。 |
|
子機プログラムが配信可能なサイズを超えています。 |
|
親機と子機プログラムのリージョンが異なっています。 |
|
無線通信ができない状態(スリープ中または無線オフモード)です。 |
3.2.2. 状態の確認
サーバー側の状態を確認する方法には、初期化で渡したイベントがシグナル状態になったときに GetEventDesc()
でイベントの詳細を取得する方法と、定期的に GetState()
で状態を取得する方法があります。
static nn::Result nn::dlp::Server::GetEventDesc( nn::dlp::EventDesc* pEventDesc); static nn::Result nn::dlp::Server::GetState(nn::dlp::ServerState* pState);
GetEventDesc()
で取得することのできるイベントの詳細は 32 個までキューに保持され、いっぱいになると古いものから捨てられます。イベントがない場合はすぐに処理を戻し、返り値に nn::dlp::ResultNoData
が返されます。
イベントのシグナル状態を監視しなければサーバーとして機能できないわけではありません。定期的に GetState()
で状態を確認し、適宜処理を行う方がサーバーコードの実装は簡単になるでしょう。
サーバーの実装には、GetState()
による状態のポーリングを強く推奨します。セッション参加の受付中やダウンロード中に、スリープに移行したり無線オフモードに切り替えられたりして、通信状態を維持できなくなったときは必ずエラー状態になるため、エラーへの対処が容易になります。
GetState()
で取得することのできるサーバーの状態は下表のとおりです。
定義 |
説明 |
---|---|
|
まだ初期化を行っていない状態です。 |
|
初期化完了後の状態です。 |
|
セッションへの参加を受け付けている状態です。クライアントの募集はこの状態でのみ行えます。 |
|
配信を行っている状態です。 |
|
配信が完了した状態です。 |
|
配信後にサーバーがクライアントをリブートしている状態です。すべてのクライアントが切断されたことを確認してから 3DSダウンロードプレイを終了してください。 |
|
エラーが発生し、再初期化が必要な状態です。この状態に遷移するのは、 |
GetEventDesc()
、GetState()
で返される可能性のある返り値とその原因を以下に示します。
返り値 |
原因 |
---|---|
|
処理に成功しました。 |
|
取得できるデータがありません。 |
|
不正なポインタを引数に指定しています。 |
3.2.3. セッションの開始と停止
3DSダウンロードプレイの処理で、クライアントからの配信要求を受け付け可能になる状態から、配信を完了してクライアントをリブートするまでをセッションと呼びます。
セッションの開始と停止は OpenSessions()
と CloseSessions()
で行うことができます。
static nn::Result nn::dlp::Server::OpenSessions( bool isManualAccept = false, u8 channel = 0); static nn::Result nn::dlp::Server::CloseSessions();
isManualAccept
に true
を渡してセッションを開始した場合は、セッションに参加しようとしているクライアントへの接続の許可と拒否をアプリケーションで行うことができます。引数を省略した場合や false
を渡して開始した場合は、自動的に接続が許可されます。
channel
には、通信で使用するチャンネルを 0, 1, 6, 11 のいずれかから指定します。0 を指定した場合はチャンネルを自動で選択します。通常は何も指定する必要はありません。
製品版の本体での実行時には、チャンネルが常に自動で選択されます。
セッションの途中でクライアントが切断されるなど、サーバーの状態遷移が止まってしまったときは、CloseSessions()
を呼び出してセッションを停止してください。また、配信後にリブートされたクライアントが必ずしもサーバーに再接続するとは限りませんので、サーバーコードはユーザーが任意のタイミングでセッションを停止できるように実装しなければなりません。
OpenSessions()
、CloseSessions()
で返される可能性のある返り値とその原因を以下に示します。
返り値 |
原因 |
---|---|
|
処理に成功しました。 |
|
不適切な状態で関数を呼び出しました。 |
|
範囲外の値を引数に指定しています。 |
|
無線通信ができない状態(スリープ中または無線オフモード)です。 |
3.2.4. 接続状況の確認
セッションを開始したあとは、エラーが発生していない状態ならば、GetConnectingClients()
で接続されているクライアントの数とそのノード ID を確認することができます。また、取得したノード ID からクライアントの情報や状態を GetClientoInfo()
と GetClientState()
で確認することができます。
static nn::Result nn::dlp::Server::GetConnectingClients( u16* pNum, u16 clients[], u16 size); static nn::Result nn::dlp::Server::GetClientInfo( nn::dlp::NodeInfo* pClientInfo, u16 nodeId); static nn::Result nn::dlp::Server::GetClientState( nn::dlp::ClientState* pClientState, u16 nodeId); static nn::Result nn::dlp::Server::GetClientState( nn::dlp::ClientState* pClientState, size_t* pTotalNum, size_t* pDownloadedNum, u16 nodeId);
GetConnectingClients()
の引数 pNum
にはセッションに接続されているクライアントの数が、pClients
にはクライアントのノード ID の一覧が、それぞれ格納されます。pNum
に渡すのが u16
型の変数へのポインタ、clients
に渡すのが u16
型の配列であることに注意してください。size
には clients
に指定した配列のサイズを渡します。配列のサイズは初期化時に指定した maxClientNum
以上でなければなりません。
GetClientInfo()
または GetClientState()
の引数 nodeId
には、情報を取得したいクライアントのノード ID を指定してください。存在しないクライアントやサーバーのノード ID を指定した場合は nn::dlp::ResultNoData
が返されます。
GetClientInfo()
の引数 pClientoInfo
で指定された nn::dlp::NodeInfo
構造体に格納される情報は、「3.1.9.3. ノード情報の取得」で取得するノード情報と同じです。ユーザーの名前の表示などに利用することができます。
GetClientState()
の引数 pClientState
で指定された nn::dlp::ClientState
型のポインタには、クライアントの状態が格納されます。pTotalNum
には子機プログラムの総パケット数が、pDownloadNum
にはダウンロード済みのパケット数がそれぞれ格納されます。pTotalNum
には 0 が格納されることがありますので、進捗の表示で除数に使用する場合は注意が必要です。
pClientState
にはクライアントの状態が格納されています。
定義 |
説明 |
---|---|
|
サーバーからセッション参加を許可され、セッションに招待されるのを待っている状態です。 |
|
ネットワークに接続されたクライアントが、サーバーからのセッション参加許可を待っている状態です。 |
|
セッションに参加している状態です。 |
|
配信が開始され、ダウンロードを行っている状態です。配信開始直後などで、サーバーからデータを受け取っていない場合も含みます。 |
|
ダウンロードが完了した状態です。 |
GetConnectingClients()
、GetClientInfo()
、GetClientState()
で返される可能性のある返り値とその原因を以下に示します。
返り値 |
原因 |
---|---|
|
処理に成功しました。 |
|
不適切な状態で関数を呼び出しました。 |
|
不正なポインタを引数に指定しています。 |
|
指定されたノード ID を持つクライアントは接続されていません。 |
3.2.5. 接続の許可と拒否
セッションを開始してから配信を開始するまでの間、サーバーはクライアントからのセッションへの参加要求を受け付けます。引数 isManualAccept
に true
を渡してセッションを開始していた場合、アプリケーションでクライアントに対するセッションへの接続許可を制御することができます。
static nn::Result nn::dlp::Server::AcceptClient(u16 nodeId); static nn::Result nn::dlp::Server::DisconnectClient(u16 nodeId);
AcceptClient()
で接続を許可し、DisconnectClient()
で接続を拒否することができます。 クライアントの状態が CLIENT_STATE_WAITING_ACCEPT
でなければ、これらの関数を呼び出して接続の許可や拒否を行うことができません。
AcceptClient()
、DisconnectClient()
で返される可能性のある返り値とその原因を以下に示します。
返り値 |
原因 |
---|---|
|
処理に成功しました。 |
|
不適切な状態で関数を呼び出しました。 |
|
指定されたノード ID を持つクライアントは接続されていません。 |
|
無線通信ができない状態(スリープ中または無線オフモード)です。 |
3.2.6. 配信の開始
配信先のクライアントを確定したあとは、StartDistribute()
を呼び出してセッションへの参加を締め切り、クライアントへの配信を開始することができます。
static nn::Result nn::dlp::Server::StartDistribute();
配信を開始し、サーバーの状態が SERVER_STATE_PREPARING_FOR_TITLE_DISTRIBUTION
に遷移します。
サーバーの状態が SERVER_STATE_COMPLETE_DISTRIBUTION
に遷移して配信が完了するまで、クライアントの接続状況を確認して進捗などを表示しながら待機するだけです。
配信が開始されても、状態が CLIENT_STATE_JOINED_SESSION
のままになっているクライアントはシステム更新が必要なクライアントです。その状態のままシステムアップデート(ダウンロード、リブート、再接続)され、クライアントの状態が CLIENT_STATE_DOWNLOADING
に遷移するまで、サーバーはクライアントの再接続を待ち受けている状態になっています。しかし、ダウンロードのキャンセルや電源の切断、無線オフモードへの切り替え、通信品質の悪化など、サーバーとクライアントが通信できない状況になる可能性があり、すべてのクライアントが必ずしも再接続されるとは限りません。そのため、ユーザーの操作でセッションをいつでも停止(CloseSessions()
)できるように実装してください。
StartDistribute()
で返される可能性のある返り値とその原因を以下に示します。
返り値 |
原因 |
---|---|
|
処理に成功しました。 |
|
不適切な状態で関数を呼び出したか、配信すべき子機が存在しません。 |
|
無線通信ができない状態(スリープ中または無線オフモード)です。 |
|
メディアにアクセスできませんでした。カードが抜けている可能性があります。 |
配信中にサーバーのカード抜けなどによって接続が切断されても、クライアントが中断画面に遷移しないのは仕様です。また、アプリケーションでこの事象に対応する必要はありません。
3.2.7. クライアントのリブート
配信が完了し、サーバーの状態が SERVER_STATE_COMPLETE_DISTRIBUTION
に遷移したときは、セッションに参加していたすべてのクライアントをリブートするために RebootAllClients()
を呼び出してください。
static nn::Result nn::dlp::Server::RebootAllClients( const char passpharase[] = NULL);
passphrase
には、リブート後の子機とローカル通信を行うために必要となる、ネットワーク構築時のパスフレーズを指定します。指定可能な文字列の長さは NULL
を含めて、最大 MAX_CHILD_UDS_PASSPHRASE_LENGTH
文字です。パスフレーズはダウンロードセッションごとに異なる値を指定し、容易に推測できるような文字列を指定しないでください。
配信完了後にこの関数を呼び出した場合は、すべてのクライアントのリブートを行い、サーバーの状態が SERVER_STATE_REBOOTING_CLIENTS
に遷移します。以降はクライアントの接続状況を確認し、すべてのクライアントの切断を確認してから終了処理を行ってください。
RebootAllClients()
で返される可能性のある返り値とその原因を以下に示します。
返り値 |
原因 |
---|---|
|
処理に成功しました。 |
|
不適切な状態で関数を呼び出しました。 |
3.2.8. 終了処理
すべてのクライアントのリブートとセッションからの切断を確認したあとは、Finalize()
でサーバーの終了処理を行うことができます。また、蓋が閉じられたことによるスリープ状態への遷移時には Finalize()
を呼び出さなければなりません。
static nn::Result nn::dlp::Server::Finalize();
終了処理の完了で 3DSダウンロードプレイは終了します。
配信した子機プログラムとローカル通信を行う場合は、終了処理以降に UDS ライブラリでネットワークを構築してクライアントの接続を待ちます。しかし、必ずしもクライアントからの再接続が行われるとは限らないことには注意が必要です。
Finalize()
で返される可能性のある返り値とその原因を以下に示します。
返り値 |
原因 |
---|---|
|
処理に成功しました。 |
3.2.9. 子機プログラムについて
3DSダウンロードプレイで子機プログラムをダウンロードしたあと、クライアントはサーバーの指示でリブートします。子機プログラムでサーバーだった本体を親機とのローカル通信を行う場合の補助として、親機への再接続のための情報を nn::dlp::GetRebootInfo()
で取得することができます。
nn::Result nn::dlp::GetRebootInfo(nn::dlp::RebootInfo* pRebootInfo);
この関数は DLP ライブラリの初期化を行わずに呼び出すことができます。 pRebootInfo
には、再接続のための情報を格納する nn::dlp::RebootInfo
構造体へのポインタを渡してください。
typedef struct { u8 bssid[6]; char passphrase[MAX_CHILD_UDS_PASSPHRASE_LENGTH]; NN_PADDING1; } nn::dlp::RebootInfo;
bssid
と passphrase
には親機が構築するネットワークの BSSID とパスフレーズが格納されています。
これらの情報をもとに、どのネットワークに接続するのかをアプリケーションが自動的に判断することもできます。
3.2.9.1. 組み込み方法
rsf ファイルで指定する、子機プログラムとサーバー側のアプリケーションのユニーク ID は同じでなければなりません。また、子機プログラムの Category は DlpChild でなければなりません。なお、作成する子機プログラムに CTR アイコンは必要ですが、CTR タイトルバナーは必要ありません。
アプリケーションのビルドに OMake を利用している場合は、サーバー側のアプリケーションの OMakefile のビルド変数 "CHILD_APPS[]" に子機プログラムの cia ファイルを指定してください。
OMake を利用していない場合は、コマンドラインツールの ctr_makeciaarchive で cfa ファイルを作成し、ctr_makerom の引数 "-content" で cci ファイルに含めてください。
3.2.9.2. 強制ダウンロード
通常、クライアントは自身の持っていない子機プログラムか、自身にインポートされているものより新しい子機プログラムでなければダウンロードしませんが、Config ツールの強制インポート設定を有効にすることでクライアントに子機プログラムのバージョンを無視して強制的にダウンロードさせることができます。
3.2.9.3. デバッグ方法
子機プログラムのデバッグは、ダウンロードプレイで子機プログラムの実行が始まったあとに、以下の手順で行うことができます。
- デバッガの ATTACHA コマンドで子機プログラムにアタッチする
- 実行が一時停止するので、LS コマンドで axf ファイルからデバッグ情報を読み込む
- 実行を再開する
3.2.9.4. 子機プログラムの判定
3.2.10. 擬似クライアント
擬似クライアント(nn::dlp::FakeClient
クラス)は、ダウンロードとリブートを行わないクライアントとして、アプリケーションからダウンロードプレイのセッションに参加するために用意されています。
擬似クライアントを利用すると、ダウンロードプレイで参加する 3DS と同じセッションに参加することができるため、アプリケーションと子機プログラムが混在するネットワークでの無線通信へと円滑に移行することができます。また、ソフトを持っていないユーザーとソフトを持っているユーザーをネットワークに混在させることができるという利点以外にも、セッションの管理などを DLP ライブラリにまかせることができるという利点があります。
擬似クライアントを利用するタイトルをパッチや改訂版で修正する場合は、リマスターバージョンの異なる擬似クライアントが DLP サーバーに接続する場合について配慮する必要があります。
詳しくは「パッチマニュアル」の「疑似クライアント機能」を参照してください。
以下の図は、3DSダウンロードプレイで行う擬似クライアント側での処理の流れを、親機(サーバー)と子機(擬似クライアント)の状態の遷移を軸に図示したものです。
3.2.10.1. 初期化
擬似クライアントのコードで必要な関数は nn::dlp::FakeClient
クラスにまとめられています。DLP ライブラリ自体の初期化は必要なく、nn::dlp::FakeClient
クラスの Initialize()
を呼び出して擬似クライアントの初期化を行います。
static nn::Result nn::dlp::FakeClient::Initialize( u8 scanNum, nn::Handle eventHandle, void* pBuffer, const size_t bufferSize); static size_t nn::dlp::FakeClient::GetBufferSize(u8 scanNum);
scanNum
には、1 回のスキャン要求で取得可能なタイトルの最大数を 1 ~ MAX_SCAN_NUM
の範囲で指定します。
eventHandle
には、3DSダウンロードプレイからの通知を待つ nn::os::Event
クラスのハンドルを渡します。ハンドルを渡すイベントはアプリケーションで初期化する必要があります。
pBuffer
と bufferSize
には作業バッファとそのサイズを渡します。作業バッファはアプリケーションでデバイスメモリ以外から確保し、先頭アドレスが nn::dlp::FakeClient::BUFFER_ALIGNMENT
( 4096 Byte ) アライメントでサイズが GetBufferSize()
に scanNum
を渡して取得したサイズ以上の nn::dlp::FakeClient::BUFFER_UNITSIZE
( 4096 ) の倍数でなければなりません。引数に範囲外の値を指定した場合、GetBufferSize()
は不定値を返します。
FakeClientWithName
クラスは FakeClient
クラスの Initialize()
にユーザー名の指定機能を追加したものです。このクラスを利用してユーザー名を設定する場合は、必ず UGC のガイドラインに従って NG ワードのチェックを行ってください。チェックの結果、NG ワードが含まれている場合は Initialize()
に渡す構造体の isNgUserName
を true
にし、userName
には伏せ文字などの加工を行っていないユーザー名をそのまま設定します。FakeClientWithName
クラスの Initialize()
以外の動作は FakeClient
クラスと同じです。
Initialize()
で返される可能性のある返り値とその原因を以下に示します。
返り値 |
原因 |
---|---|
|
処理に成功しました。 |
|
無線通信モジュールがほかの無線処理で占有されています。インフラストラクチャ通信などを終了させてください。 |
|
範囲外の値を引数に指定しています。 |
|
不正なポインタを引数に指定しています。 |
|
不正なハンドルを引数に指定しています。 |
|
ライブラリ内で回復不能なエラーが発生しました。 |
|
無線通信ができない状態(スリープ中または無線オフモード)です。 |
3.2.10.2. 状態の確認
擬似クライアント側の状態を確認する方法には、初期化で渡したイベントがシグナル状態になったときに GetEventDesc()
でイベントの詳細を取得する方法と、定期的に GetMyStatus()
で状態を取得する方法があります。
static nn::Result nn::dlp::FakeClient::GetEventDesc( nn::dlp::EventDesc* pEventDesc); static nn::Result nn::dlp::FakeClient::GetMyStatus( nn::dlp::ClientStatus* pStatus);
GetEventDesc()
で取得することのできるイベントの詳細は 32 個までキューに保持され、いっぱいになると古いものから捨てられます。イベントがない場合はすぐに処理を戻し、返り値には nn::dlp::ResultNoData
が返されます。
イベントのシグナル状態を監視しなければ擬似クライアントとして機能できないわけではありません。定期的(ゲームフレームごとなど)に GetMyStatus()
で状態を確認し、適宜処理を行う方がコードの実装は簡単になるでしょう。
擬似クライアントの実装には、GetMyStatus()
による状態のポーリングを強く推奨します。サーバーのスキャン中やダウンロード中に、スリープに移行したり無線オフモードに切り替えられたりして、通信状態を維持できなくなったときは必ずエラー状態になるため、エラーへの対処が容易になります。
GetMyStatus()
で取得することのできる nn::dlp::ClientStatus
は以下のように定義されています。
typedef struct { u16 nodeId; SessionType sessionType; ClientState state; size_t totalNum; size_t downloadedNum; } nn::dlp::ClientStatus;
state
メンバに擬似クライアントの状態が格納されます。格納される値の定義は下表のとおりです。
定義 |
説明 |
---|---|
|
まだ初期化を行っていない状態です。 |
|
初期化完了後、またはサーバーから切断された状態です。 |
|
サーバーのスキャン中です。 |
|
ネットワークへの接続を待っている状態です。 |
|
セッションへの招待を待っている状態です。 |
|
セッションに参加している状態です。 |
|
配信が開始され、ダウンロードを行っている状態です。擬似クライアントでは、サーバーからデータを受け取りません。 |
|
ダウンロードが完了した状態です。 |
|
擬似クライアントでは、この状態に遷移することはありません。 |
|
サーバーからのリブート要求を受信した状態です。 |
|
エラーが発生し、再初期化が必要な状態です。
|
state
メンバ以外のメンバに格納される値は、擬似クライアントの実装には必要ありません。
3.2.10.3. サーバーのスキャン
サーバー(タイトル)の一覧を取得するために、ダウンロードプレイのセッションを開始しているサーバーをスキャンします。
static nn::Result nn::dlp::FakeClient::StartScan( u32 uniqueId, u8 childIndex, const u8* pMac = NULL); static nn::Result nn::dlp::FakeClient::StopScan();
StartScan()
を呼び出して、サーバーのスキャンを開始します。uniqueId
、childIndex
、pMac
を指定することで、スキャン対象のタイトルやサーバーを絞り込むことができます。
異なるアプリケーションのサーバーを選択すると、そのサーバーの動作に悪影響を及ぼす可能性があります。擬似クライアントは uniqueId
と childIndex
を必ず指定し、特定のタイトルを配信しているサーバーだけをスキャンの対象にしてください。
サーバーのスキャンは StopScan()
で停止するまで続けられ、スキャンの結果は再度 StartScan()
を呼び出すまで、最大で Initialize()
の 引数 scanNum
に指定した数が保持されます。
StartScan()
で返される可能性のある返り値とその原因を以下に示します。
返り値 |
原因 |
---|---|
|
処理に成功しました。 |
|
不適切な状態( |
3.2.10.4. スキャン結果の取得
スキャン結果は GetTitleInfo()
を呼び出して、タイトル情報(nn::dlp::TitleInfo
)の一覧として取得します。また、取得したタイトル情報をもとに、GetServerInfo()
でサーバー情報(nn::dlp::ServerInfo
)を取得することができます。これらの関数は擬似クライアントの状態が CLIENT_STATE_INVALID
や CLIENT_STATE_ERROR
で呼び出すと nn::dlp::ResultInvalidState
を返します。
static nn::Result nn::dlp::FakeClient::GetTitleInfo( nn::dlp::TitleInfo* pTitleInfo, bool isReset = false); static nn::Result nn::dlp::FakeClient::GetTitleInfo( nn::dlp::TitleInfo* pTitleInfo, const u8* pMac, u32 uniqueId, u8 childIndex); static nn::Result nn::dlp::FakeClient::GetServerInfo( nn::dlp::ServerInfo* pServerInfo, const u8* pMac); static nn::Result nn::dlp::FakeClient::DeleteScanInfo(const u8* pMac);
GetTitleInfo()
のオーバーロードのうち、引数に isReset
を持つものはタイトル情報一覧の取得に使用するためのものです。通常は、まだ GetTitleInfo()
で取得していないタイトル情報を取得するように、isReset
には false
を指定します。返り値のインスタンスで IsSuccess()
が true
を返したときは、新たなタイトル情報を取得しています。未取得のタイトル情報がない場合や、これ以上タイトル情報を保持できない場合は、nn::dlp::ResultNoData
が返されます。isReset
に true
を指定すれば、取得済みのタイトル情報を含め、タイトル情報の一覧を先頭から再取得することができます。StartScane()
でスキャンを開始してから StopScan()
でスキャンを中止するまでサーバーのスキャンは続けられ、最大で Initialize()
で指定した scanNum
個のタイトル情報を保持することができます。タイトル情報の一覧は StartScan()
を再度呼び出したときにクリアされます。
引数に pMac
、uniqueId
、childIndex
を持つオーバーロードは、サーバーの MAC アドレス、ユニーク ID、子機プログラムのインデックスが分かっている場合に使用するためのものです。
nn::dlp::TitleInfo
は、以下のように定義されています。
typedef struct { u32 uniqueId; u8 childIndex; NN_PADDING3; u8 mac[6]; u16 programVersion; bit8 ratingInfo[RATING_INFO_SIZE]; wchar_t shortTitleName[SHORT_TITLE_NAME_LENGTH]; wchar_t longTitleName[LONG_TITLE_NAME_LENGTH]; nn::dlp::IconInfo icon; u32 importSize; nn::cfg::CfgRegionCode region; bool ulcd; NN_PADDING2; } nn::dlp::TitleInfo;
uniqueId
、childIndex
、mac
の 3 つはタイトル情報を特定するために利用します。
GetServerInfo()
は、サーバーの情報の取得に使用します。pMac
には、サーバー情報を取得したいサーバーの MAC アドレスを指定します。指定された MAC アドレスの機器がない、もしくはダウンロードプレイのサーバーとして動作していない場合は nn::dlp::ResultNoData
が返されます。
サーバー情報にはリンクレベルや接続されているクライアントの一覧などの情報が含まれており、以下のように定義されています。
typedef struct { u8 mac[6]; u8 channel; nn::uds::LinkLevel linkLevel; u8 maxNodeNum; u8 nodeNum; bit16 dlpVersion; NN_PADDING4; NodeInfo nodeInfo[MAX_NODE_NUM]; nn::os::Tick lastUpdateTick; } nn::dlp::ServerInfo;
状態がスキャン中(CLIENT_STATE_SCANNING
)であれば、リンクレベル(linkLevel
)、サーバーを含んだ接続ノード数(nodeNum
)、ノードの詳細情報(nodeInfo
) には、その時点での情報が反映されています。それ以外の状態では変化しませんので、「3.2.10.6. 副次的な情報の取得」で紹介する関数を利用してください。
ダウンロードプレイのサーバーのノード ID には、ローカル通信での Master と同じように、必ず 1 が割り当てられています。サーバー情報のノード情報(nodeInfo
)のうち、ノード ID(nodeId
)が 1 であるノード情報のユーザー名(userName.userName
)を表示するなど、スキャン対象を絞った状態でも複数のサーバーが一覧に表示される可能性を考慮してください。ただし、ユーザー名の情報(userName
)の isNgUserName
が true
の場合は、ユーザー名に NG ワードが含まれていますので、ガイドラインに従って処理しなければなりません。
スキャン結果から特定のタイトル情報を削除したいときは DeleteScanInfo()
を呼び出します。pMac
に指定された MAC アドレスのサーバーのスキャン結果が削除されます。一定時間サーバーの情報が更新されなかった場合などに使用してください。指定された MAC アドレスのサーバーが存在しなかった場合は nn::dlp::ResultNoData
を返します。また、状態が CLIENT_STATE_DISCONNECTED_NETWORK
または CLIENT_STATE_SCANNING
以外のときに呼び出された場合は、nn::dlp::ResultInvalidState
を返します。
3.2.10.5. セッションへの参加
サーバー(タイトル情報)の一覧から接続するサーバーを選択し、StartFakeSession()
でセッションに参加します。擬似クライアントは、システムアプリのダウンロードプレイでセッションに参加してきたクライアントと同じようにサーバーからは見えますが、子機プログラムのダウンロードは行われません。
セッションに参加する前に、必ず StopScan()
でサーバーのスキャンを停止しておいてください。
static nn::Result nn::dlp::FakeClient::StartFakeSession( const u8* pMac, u32 uniqueId, u8 childIndex); static nn::Result nn::dlp::FakeClient::StopFakeSession();
引数 pMac
、uniqueId
、childIndex
に指定する値は、GetTitleInfo()
で取得したタイトル情報から得ることができます。
StartFakeSession()
で返される可能性のある返り値とその原因を以下に示します。
返り値 |
原因 |
---|---|
|
処理に成功しました。 |
|
不適切な状態( |
|
|
|
無線通信ができない状態(スリープ中または無線オフモード)です。 |
|
|
|
すでに接続クライアント数が最大に達していたため、サーバーに接続できませんでした。接続クライアント数が減少しない限り、接続できません。 |
|
サーバーが子機プログラムの配信中のため、接続を拒否されました。 |
|
一定時間以内に接続が成立しませんでした。電波状況が悪い場合や、通信負荷が高い場合に返されます。 |
|
ライブラリ内で不整合によるエラーが発生しました。 |
セッションの途中で状態の遷移が止まってしまったときは、StopFakeSession()
を呼び出してセッションを停止してください。この関数で返されるインスタンスは必ず IsSuccess()
が true
を返します。擬似クライアントもサーバーと同様に、ユーザーが任意のタイミングでセッションを停止できるように実装しなければなりません。
3.2.10.6. 副次的な情報の取得
擬似クライアントの実装には必要ではない、副次的な情報を取得するための関数が用意されています。
static nn::Result nn::dlp::FakeClient::GetConnectingNodes( u8* pNum, u16* pNodeIds, u16 size); static nn::Result nn::dlp::FakeClient::GetNodeInfo( nn::dlp::NodeInfo* pNodeInfo, u16 nodeId); static nn::Result nn::dlp::FakeClient::GetLinkLevel( nn::uds::LinkLevel* pLinkLevel);
GetConnectingNodes()
は、セッションに接続中のノードの一覧を取得することができます。
GetNodeInfo()
は、セッションに接続中のノードの詳細情報を取得することができます。pNodeInfo
に返される nn::dlp::NodeInfo
はローカル通信の nn::uds::NodeInfomation
と同じです。ノードの詳細情報については、「3.1.9.3. ノード情報の取得」を参照してください。
GetLinkLevel()
は、サーバーとの間のリンクレベル(通信品質)を取得することができます。pLinkLevel
に返される値については、「3.1.9.2. リンクレベルの取得」を参照してください。
3.2.10.7. ダウンロードの終了
擬似クライアントの状態が CLIENT_STATE_DOWNLOAD_COMPLETE
に遷移したことは、このクライアントのダウンロードが完了していることを示しているだけで、ほかのクライアントはダウンロード中である可能性があります。ダウンロードプレイのセッション全体としてはまだ完了していませんので、サーバーに接続しているすべてのクライアントがダウンロードを完了するまで待機してください。
サーバーに接続していたすべてのクライアントがダウンロードを完了すると、サーバーはすべてのクライアントに対してリブート要求を送信します。リブート要求を受信すると、擬似クライアントの状態は CLIENT_STATE_REBOOTING
へと遷移します。サーバーから指定されたローカル通信のパスフレーズは、この段階でのみ GetPassphrase()
で取得することができます。
パスフレーズを取得し終えたら、Finalize()
を呼び出して擬似クライアントの終了処理を行ってください。
static nn::Result nn::dlp::FakeClient::GetPassphrase(char* pPassphrase); static nn::Result nn::dlp::FakeClient::Finalize();
GetPassphrase()
の pPassphrase
には、MAX_CHILD_UDS_PASSPHRASE_LENGTH
バイト以上のバッファを指定してください。CLIENT_STATE_REBOOTING
以外の状態では ResultInvalidState
が返されます。
Finalize()
は必ず成功しますので、返されるインスタンスの IsSuccess()
は常に true
を返します。
そのまま親機(サーバー)とのローカル通信を行う場合は、セッションの開始時に選択したサーバーの MAC アドレスと GetPassphrase()
で取得したパスフレーズを利用してください。
スリープ状態への移行時や HOMEメニューを起動しなければならないときは、必ず Finalize()
を呼び出して、終了処理を行ってください。
3.3. 自動接続
AC(自動接続)ライブラリは、本体設定のネットワーク設定による無線 LAN のアクセスポイントやニンテンドー Wi-Fi USB コネクタ、ニンテンドーゾーン、Wi-Fi ステーション、公共無線 LAN アクセスポイントを経由してのインターネット接続を自動的に行うライブラリです。3DS の無線通信モジュールに直接アクセスするライブラリは用意されていませんので、3DS から外部のサーバーにアクセスする際には AC ライブラリを利用してインターネット接続(もしくは LAN 接続)を確立しなければなりません。
AC ライブラリは本体設定のネットワーク設定に従って無線 LAN のアクセスポイントなどにアクセスします。そのため、事前に本体設定で有効な無線 LAN アクセスポイントへの接続設定を記録しておかなければなりません。
本体設定を行うメニューの代わりにネットワーク設定の変更を行う、NetworkSetting ツールが用意されています。また、AC ライブラリでは、デバッグ用途でのみ利用可能な関数として、ネットワーク設定 1 を変更する nn::ac::DebugSetNetworkSetting1()
、自動接続で確立する接続がインターネットへの接続まで必要なのか、それともアクセスポイントへの接続まででよいのかを指定する nn::ac::DebugSetNetworkArea()
、接続対象とするアクセスポイントの種類を ApType
列挙子(表 3-22. nn::ac::ApType 列挙子)の論理和で指定する nn::ac::DebugSetApType()
が用意されています。
ただし、これらの関数はデバッグモードに設定(Config ツールで「Debug Setting」の「Debug Mode」を「enable」に設定)されているときにのみ動作します。
AC ライブラリを利用してインターネット接続を確立させると、デーモンマネージャがインフラストラクチャ通信モードで無線通信モジュールを独占し、すれちがい通信などのバックグラウンド通信が行われるのを阻害する可能性があります。
AC ライブラリの関数は処理結果を nn::Result
クラスのインスタンスで返します。処理が正常に行われたときは、IsSuccess()
で true
が返されます。
3.3.1. 初期化
AC ライブラリの初期化は nn::ac::Initialize()
の呼び出しで行われます。
nn::Result nn::ac::Initialize(); bool nn::ac::IsInitialized();
nn::ac::ResultAlreadyInitialized
が返されたときは、すでにアプリケーションで初期化を行っていることを示します。エラーではありませんが、ライブラリは初期化回数の参照カウンタを保持していますので、Initialize()
を呼び出した回数と同じ回数 Finalize()
を呼び出さなければなりません。nn::ac::IsInitialized()
を呼び出すと、すでに初期化が行われているかどうかを判断することができます。
3.3.2. 自動接続
自動接続処理は nn::ac::CreateDefaultConfig()
で作成された接続条件に従って行われます。
nn::Result nn::ac::CreateDefaultConfig(nn::ac::Config* config);
config
に渡された nn::ac::Config
構造体に接続条件が格納されます。構造体はアプリケーションで確保しなければなりません。この引数に NULL
を指定した場合は nn::ac::ResultInvalidData
が返されます。
作成された接続条件を nn::ac::Connect()
または nn::ac::ConnectAsync()
の引数 config
に渡して自動接続処理を開始します。
nn::Result nn::ac::Connect(nn::ac::Config& config); nn::Result nn::ac::ConnectAsync(nn::ac::Config& config, nn::os::Event* event); nn::Result nn::ac::CancelConnectAsync(); nn::Result nn::ac::GetConnectResult(); bool nn::ac::IsConnected();
自動接続処理中に EULA 同意のチェックが行われるため、事前に FS ライブラリの初期化を行う必要があります。
nn::ac::ConnectAsync()
は nn::ac::Connect()
の非同期版です。非同期版では、処理が成功したかどうかに関わらず、自動接続処理が終了した時点で event
に渡したイベントがシグナル状態になります。イベントがシグナル状態になったあとは、必ず nn::ac::GetConnectResult()
で処理結果を取得してください。処理結果を取得するまでローカル通信の開始に失敗し、すれちがい通信が行われない状態になります。自動接続処理を途中でキャンセルする場合は nn::ac::CancelConnectAsync()
を呼び出してください。処理がキャンセルされるかキャンセルされる前に処理が成功したときは、nn::ac::ConnectAsync()
の event
に渡されたイベントがシグナル状態になります。処理がキャンセルされたときは、処理結果として nn::ac::ResultCanceled
が返されます。
バックグラウンド通信の接続要求が処理されているときに nn::ac::CancelConnectAsync()
を呼び出すと、アプリケーションの接続要求を取り下げて nn::ac::ResultSuccess
を返します。
自動接続の接続先は、ユーザー設定がニンテンドーゾーンやホットスポットよりも優先されます。ユーザー設定内の接続先優先順位は不定ですので、接続先を確実に指定するにはユーザー設定を一つにしてください。デバッグモードでは nn::ac::DebugSetApType()
で接続先を指定できます。
自動接続処理が成功すると、インターネット接続が確立したときは nn::ac::ResultWanConnected
が、無線 LAN 接続が確立したときは nn::ac::ResultLanConnected
が処理結果として返されます。以降、ソケット通信などの無線通信モジュールを利用したネットワーク経由の通信を行うことができるようになります。接続可能なアクセスポイントを発見できなかったときは nn::ac::ResultNotFoundAccessPoint
が返されます。
すでにバックグラウンド通信によって接続が確立していた場合、すぐに成功を返すことがあります。逆に接続が確立していない場合は処理に時間がかかることがあり、インターネット接続を確立できないアクセスポイントに接続した場合は通常よりも処理に時間がかかることがあります。
処理結果に nn::ac::ResultWifiOff
が返されたときは、無線オフモードになっているために無線通信モジュールを利用することができない状態です。処理結果に nn::ac::ResultNotAgreeEula
が返されたときは、EULA 同意チェックに失敗した場合とアプリケーションにアイコンファイルが設定されていない場合とがあります。そのほかの処理結果については関数リファレンスを参照してください。
nn::ac::IsConnected()
を呼び出して、アプリケーションが行った自動接続処理によって確立した接続で、アクセスポイントに接続中かどうかを確認することができます。すでにアクセスポイントなどとの接続がバックグラウンド通信で確立していることが判明していても、必ずアプリケーションで自動接続処理を行ってください。アプリケーションで接続処理を行っていない場合、バックグラウンドで接続していたプロセスが終了したときに、意図せず接続が切断される可能性があります。
3.3.3. 接続状態の取得
自動接続処理で接続を確立したアクセスポイントの種類や、接続中のアクセスポイントの電波強度を、以下の関数で取得することができます。
nn::Result nn::ac::GetConnectingApType(nn::ac::ApType* apType); nn::Result nn::ac::GetConnectingNintendoZoneBeaconSubset( nn::ac::NintendoZoneBeaconSubset* beacon); nn::Result nn::ac::GetConnectingHotspotSubset(nn::ac::HotspotSubset* hotspot); nn::Result nn::ac::GetLinkLevel(nn::ac::LinkLevel* linkLevel); nn::ac::LinkLevel nn::ac::GetLinkLevel();
nn::ac::GetConnectingApType()
は接続しているアクセスポイントの種類を apType
に返します。接続されていない状態で呼び出した場合は、返り値に nn::ac::ResultNotConnecting
が返されます。アクセスポイントの種類は nn::ac::ApType
列挙子に、以下のように定義されています。アクセスポイントの種類は、ファームウェアのアップデートが行われた際に追加される可能性がありますので、記載されているもの以外を取得した場合についてもアプリケーションの実装で考慮する必要があります。
列挙子 |
アクセスポイント |
---|---|
|
なし |
|
ネットワーク設定 1 |
|
ネットワーク設定 2 |
|
ネットワーク設定 3 |
|
ニンテンドー Wi-Fi USB コネクタ |
|
ニンテンドーゾーン |
|
Wi-Fi ステーション |
|
フリースポット( |
|
ホットスポット |
|
上記すべて( |
nn::ac::GetConnectingNintendoZoneBeaconSubset()
は接続中のニンテンドーゾーンのゾーンビーコンを beacon
に格納します。nn::ac::GetConnectingHotspotSubset()
は接続中のホットスポットの情報を hotspot
に格納します。どちらの関数も、対応するアクセスポイント以外に接続しているときに呼び出した場合には nn::ac::ResultInvalidLocation
を返します。
接続中のアクセスポイントの電波強度は、nn::ac::GetLinkLevel()
で取得することができます。返り値または引数 pLinkLevel
で取得できる電波強度は nn::ac::LinkLevel
列挙子に、以下のように定義されています。電波強度を取得する処理でエラーが発生したときは LINK_LEVEL_0
を返すため、エラー発生時に pLinkLevel
の値を書き換える必要はありません。
列挙子 |
電波強度 |
---|---|
|
通信品質が非常に悪い、もしくは通信が成立していない |
|
通信品質が悪い |
|
通信品質があまりよくない |
|
通信品質がよい |
ニンテンドー3DSステーションへの接続中は、機材間の距離によって AP_TYPE_WIFI_STATION
が返る場合と AP_TYPE_NINTENDO_ZONE
が返る場合があります。
いつの間に通信拠点へ接続中は、nn::ac::AP_TYPE_WIFI_STATION、
nn::ac::AP_TYPE_NINTENDO_ZONE
、 nn::ac::AP_TYPE_HOTSPOT
のいずれかが返ります。いつの間に通信拠点への接続中であることを検出したい場合はこれらの論理和で判断 してください。
3.3.4. 切断
自動接続処理で確立した接続は、アプリケーションで明示的に切断する場合と、電波状況の悪化などの外因によって切断される場合とがあります。
nn::Result nn::ac::Close(); nn::Result nn::ac::CloseAsync(nn::os::Event* event); nn::Result nn::ac::GetCloseResult(); nn::Result nn::ac::RegisterDisconnectEvent(nn::os::Event* event);
nn::ac::Close()
または nn::ac::CloseAsync()
を呼び出すことで、確立していた接続をアプリケーションで明示的に切断することができます。nn::ac::CloseAsync()
は nn::ac::Close()
の非同期版です。処理が成功したかどうかに関わらず、切断処理が終了した時点で event
に渡したイベントがシグナル状態になります。イベントがシグナル状態になったあと、nn::ac::GetCloseResult()
で処理結果を取得してください。
切断されたかどうかについては、処理結果で返されるインスタンスの IsSuccess()
が true
を返すかどうかでのみ判断してください。バックグラウンドで動作しているデーモンが自動接続で接続を確立していた場合、切断後の状態が未接続(nn::ac::STATUS_IDLE
)に遷移するとは限りません。
電波状況の悪化やアクセスポイントの圏外になるなどの理由で、確立していた接続が切断される場合があります。接続が切断されたことは、nn::ac::RegisterDisconnectEvent()
の event
に渡したイベントがシグナル状態になることによってアプリケーションに通知されます。自動接続処理で接続を確立したあとは、この関数で渡したイベントからの通知を待ち受けるスレッドを作成して外因による切断を検知してください。なお、イベントがシグナル状態になった時点でアクセスポイントとの接続が切断されていることは保証されていますので、ライブラリの終了処理前にアプリケーションで特別な処理を行う必要はありません。
3.3.5. 終了
3.4. 信頼性のあるローカル通信(RDT 通信)
CTR-SDK では、信頼性のあるローカル通信を実現する RDT ライブラリを用意しています。
RDT ライブラリは、UDS ライブラリの上位ライブラリとして位置づけられており、以下の特徴があります。
- 1 対 1 で接続した相手にデータを欠落させることなく届ける
- 送信したバイト列は、その順序を保った形で受信できることが保証される
- 送信側から受信側への一方向通信(通信路を 2 つ用意すれば、双方向通信の実現は可能ですが、メモリなどのリソース消費は 2 倍になります)
RDT ライブラリの設計は、サイズの小さなデータを定期的に送信する用途には向いていませんが、ある程度サイズの大きなデータを送信する用途に向いています。
RDT ライブラリで使用する用語は以下のとおりです。
用語 |
説明 |
---|---|
Sender |
RDT 通信で、データを送信する側のクラス |
Receiver |
RDT 通信で、データを受信する側のクラス |
3.4.1. 初期化処理
RDT ライブラリは、UDS 通信を利用して信頼性のある通信を実現します。そのため、RDT ライブラリを使用するためには、事前に UDS 通信によるネットワーク接続が確立している必要があります。つまり、Master 側は nn::uds::CreateNetwork()
の、Client 側は nn::uds::ConnectNetwork()
の実行が完了していなければなりません。
ネットワーク接続を確立させた後に、Sender と Receiver の初期化を行います。それぞれ、nn::rdt::Sender
クラスと nn::rdt::Receiver
クラスのインスタンスを生成し、メンバ関数の Initialize()
を呼び出してください。Master/Client に関係なく、データの送信方向だけを考慮して、生成するインスタンスが Sender なのか Receiver なのかを決めてください。
通信路を 2 つ用意して双方向通信を行う場合は、Sender と Receiver で異なるポート番号を使用してください。
3.4.1.1. Sender の初期化
Initialize()
を呼び出して、Sender の初期化を行います。初期化が成功すると、Sender は内部で暗黙的に nn::uds::EndpointDescriptor
を 2 つ生成します。関数内で nn::uds::Attach()
を呼び出し、暗黙的に受信バッファを確保しますので、UDS ライブラリの初期化時に指定した受信バッファのサイズが十分に大きくなければ nn::uds::RestultOutOfMemory
を返して初期化に失敗します。
nn::Result nn::rdt::Sender::Initialize(const nn::rdt::SenderConfig &config);
config
には、初期化パラメータをまとめた構造体(nn::rdt::SenderConfig
)を指定します。構造体は以下のように定義されています。
struct nn::rdt::SenderConfig { void *pWorkBuf; void *pSendBuf; u16 sendBufSize; u16 nodeId; u8 port; u8 padding[3]; };
pWorkBuf
には、Sender 用に確保したワークメモリの先頭アドレスを指定します。ワークメモリは nn::rdt::Sender::SENDER_WORKBUF_ALIGNMENT
( 8 Byte ) アライメント、nn::rdt::Sender::SENDER_WORKBUF_SIZE
( 32,768 Byte ) 以上のバッファでなければなりません。
pSendBuf
と sendBufSize
には、送信用バッファの先頭アドレスとサイズを指定します。
nodeId
と port
には、通信相手(送信先)のノード ID とポート番号を指定します。ノード ID とポート番号は UDS ライブラリでの指定と同じです。
初期化で渡したワークメモリと送信用バッファは、Finalize()
を呼び出すまで解放しないでください。
3.4.1.2. Receiver の初期化
Initialize()
を呼び出して、Receiver の初期化を行います。初期化が成功すると、Receiver は内部で暗黙的に nn::uds::EndpointDescriptor
を 2 つ生成します。関数内で nn::uds::Attach()
を呼び出し、暗黙的に受信バッファを確保しますので、UDS ライブラリの初期化時に指定した受信バッファのサイズが十分に大きくなければ nn::uds::RestultOutOfMemory
を返して初期化に失敗します。
nn::Result nn::rdt::Receiver::Initialize( const nn::rdt::ReceiverConfig &config);
config
には、初期化パラメータをまとめた構造体(nn::rdt::ReceiverConfig
)を指定します。構造体は以下のように定義されています。
struct nn::rdt::ReceiverConfig { void *pWorkBuf; void *pRecvBuf; u16 recvBufSize; u16 nodeId; u8 port; u8 padding[3]; };
pWorkBuf
には、Receiver 用に確保したワークメモリの先頭アドレスを指定します。ワークメモリは nn::rdt::Receiver::RECEIVER_WORKBUF_ALIGNMENT
( 8 Byte ) アライメント、nn::rdt::Receiver::RECEIVER_WORKBUF_SIZE
( 128 Byte ) 以上のバッファでなければなりません。
pRecvBuf
と recvBufSize
には、受信用バッファの先頭アドレスとサイズを指定します。
nodeId
と port
には、通信相手(送信元)のノード ID とポート番号を指定します。ノード ID とポート番号は UDS ライブラリでの指定と同じです。
初期化で渡したワークメモリと受信用バッファは、Finalize()
を呼び出すまで解放しないでください。
3.4.2. 状態の取得
Sender と Receiver には、それぞれ内部状態が存在します。状態を取得するには、双方のクラスに用意されているメンバ関数 GetStatus()
を呼び出してください。
enum nn::rdt::SenderState nn::rdt::Sender::GetStatus(void) const; enum nn::rdt::ReceiverState nn::rdt::Receiver::GetStatus(void) const;
Sender と Receiver それぞれの、典型的な状態の遷移を以下に示します。
Sender の状態は nn::rdt::SenderState
で定義されています。上の状態遷移図では、それぞれの状態を定義の先頭にある "SENDER_STATE_
" を省略して表記しています。
定義 |
説明 |
---|---|
|
インスタンス生成直後など、初期化が行われていない状態です。 |
|
初期化後などに遷移する、インスタンスの初期状態です。 |
|
接続を開始した直後に遷移する状態です。 |
|
通信相手に接続要求を送出し、反応を待っている状態です。 |
|
接続が確立し、データ送信が可能になった状態です。 |
|
|
|
送信を完了し、相手にデータ転送終了通知を送出した状態です。 |
Receiver の状態は nn::rdt::ReceiverState
で定義されています。上の状態遷移図では、それぞれの状態を定義の先頭にある "RECEIVER_STATE_
" を省略して表記しています。
定義 |
説明 |
---|---|
|
インスタンス生成直後など、初期化が行われていない状態です。 |
|
初期化後などに遷移する、インスタンスの初期状態です。 |
|
通信相手からの接続要求を待っている状態です。 |
|
接続が確立し、データ受信が可能になった状態です。 |
|
データ転送終了通知を受け、確認応答を返した状態です。 |
|
確認応答が相手に届いたと考えられる状態です。 |
後述する Cancel()
などによって、通信の中断が指示されると CLOSED への遷移が発生します。
RDT の関数は、それぞれ呼び出すことのできる状態が決められています。現在の状態に対して不適切な関数が呼び出された場合は、即座にエラー(nn::rdt::ResultUntimelyFunctionCall
)が返されます。
3.4.3. 通信処理の進行
Sender と Receiver の双方に用意されているメンバ関数 Process()
は、RDT ライブラリの通信処理を進行させるために呼び出します。データの送受信や確認応答などの送受信処理は Process()
内部で実行されますので、インスタンスの初期化に成功したあとのアプリケーションは、少なくともフレームごと(60 フレーム換算で約 16.667 ミリ秒ごと)に Process()
を呼び出すように設計してください。
nn::Result nn::rdt::Sender::Process(void); nn::Result nn::rdt::Receiver::Process(void);
Sender::Process()
が nn::uds::ResultBufferIsFull
を返す場合がありますが、このエラーは無視してもかまいません。到達が確認されていないデータは自動的に再送されます。
3.4.4. 接続の開始
Sender のメンバ関数 Open()
を呼び出すことで、Sender は初期化パラメータで指定した相手のノード ID、ポート番号で動作している Receiver への接続を試みます。このとき、Receiver の状態が WAITING(Receiver のメンバ関数 Wait()
の実行後)であれば接続要求は受諾され、Sender と Receiver はともにデータの送受信が可能な状態である OPENED に遷移します。
もし Receiver の状態が CLOSED であるときに Sender から接続を要求された場合、Receiver は接続を拒否するためにリセット信号を返し、これを受信した Sender の状態は CLOSED に遷移します。Open()
後に Sender の状態が CLOSED へ遷移しても、Receiver が Wait()
を実行する前である可能性を考えて、数回接続を試みてください。
nn::Result nn::rdt::Sender::Open(void); nn::Result nn::rdt::Receiver::Wait(void);
以下に、接続が開始されるまでの Sender と Receiver の状態遷移を図にしたものを示します。
3.4.5. データの送受信
Sender と Receiver の状態がともに OPENED に遷移しているときは、データの送受信が可能です。
送信には nn::rdt::Sender::Send()
を、受信には nn::rdt::Receiver::Receive()
を呼び出します。Send()
は、データを Sender 内部の送信バッファにデータを書き込む処理を、Receive()
は Receiver 内部の受信バッファからデータを取り出す処理を実行するだけです。データが実際に無線で送受信される処理は、Process()
を呼び出したときに行われます。
なお、RDT 通信で送受信されるデータには「バイト境界」のようなものは存在しません。そのため、仮に Sender がデータを 1024 バイトずつ送信したとしても、そのデータが Receiver に 1024 バイトずつ到着するとは限りません。
送信側が nn::rdt::Sender::Send()
で送信バッファに書き込んだ順序通りに、受信側では nn::rdt::Receiver::Receive()
でデータを読み取ることができます。
nn::Result nn::rdt::Sender::Send(const void* pBuf, size_t bufSize); nn::Result nn::rdt::Receiver::Receive( void* pBuf, size_t* pRecvSize, size_t bufSize);
3.4.6. 中断処理
ユーザーの指示により通信を中断する場合や UDS ライブラリでネットワーク切断を検知した場合は、Sender/Receiver の双方に用意されているメンバ関数 Cancel()
を呼び出して、RDT 通信を中断してください。Cancel()
は、状態を強制的に CLOSED に遷移させ、接続相手に向けてリセット信号を送出します。リセット信号を受信した接続相手の状態が CLOSED に遷移し、通信は中断されます。
void nn::rdt::Sender::Cancel(void); void nn::rdt::Receiver::Cancel(void);
3.4.7. 接続の終了
Sender がすべてのデータ送信し終えたときは、nn::rdt::Sender::Close()
を呼び出してください。Close()
は、これ以上送信すべきデータがないことを Sender に通知します。Close()
が呼び出されると、状態は CLOSE_REQUESTED に遷移します。この状態に遷移したあとに呼び出された Process()
の処理でクローズ指示が Receiver に送信され、Sender の状態は CLOSING に遷移します。
クローズ指示が Receiver に通知されると、Receiver の状態は WAITING_FINISHED に遷移します。この状態に遷移したあとに呼び出された Process()
の処理でクローズ指示の確認応答が Sender に送信され、Sender に届いたと判断できるだけの時間が経過したときに Receiver の状態は FINISHED に遷移します。受信側は、Receive()
で取得できるデータがなくなったことを確認してから nn::rdt::Receiver::Close()
を呼び出してください。Receiver の状態が CLOSED に遷移し、接続が閉じられます。
通信を中断する目的で Close()
を呼び出さないでください。nn::rdt::Sender::Close()
は、送信すべきデータをすべて正常に送信したことを指示するために用意されています。また、nn::rdt::Receiver::Close()
は、受信すべきデータをすべて正常に受信できたことを確認してから呼び出されることを想定しています。
nn::Result nn::rdt::Sender::Close(void); nn::Result nn::rdt::Receiver::Close(void);
以下の図は接続の終了までの状態の遷移を示したものです。