6. デバッグ用ライブラリ
注意:

これらのライブラリはノーサポート扱いであり、基本的に製品に含めることができません。製品に利用する場合は弊社窓口までご相談ください。

デバッグ用途に限定して、無線通信モジュールを利用したソケット通信などのネットワーク接続に関するライブラリを用意しています。

表 6-1. デバッグ用ライブラリ

機能

ライブラリ名

名前空間

説明

ソケット通信

SOCKET

nn::socket

ソケットを通じてリモートホストとのデータ送受信などを実現するライブラリです。

SSL 通信

SSL

nn::ssl

SSL による秘匿通信を補助するライブラリです。

HTTP 通信

HTTP

nn::http

HTTP による通信を行うライブラリです。

補足:

HTTP ライブラリは内部でソケット通信を行いますが、アプリケーションによる socket API の操作が HTTP ライブラリのソケット通信に影響を与えることはありません。

たとえば、HTTP ライブラリを使用している状態で nn::socket::Initialize()nn::socket::Finalize() を呼び出しても、HTTP ライブラリの処理に影響はありません。

6.1. ソケット通信

SOCKET(ソケット通信)ライブラリは、アドレスおよびポート番号と結び付けられたソケットを介してリモートホストとの通信を行うライブラリです。ソケット通信は、ローカル(マシン)とリモートホストとの間のデータの送受信やリモートホストの接続待ち受け、ローカルからリモートホストへの接続といった低位の通信機能を提供します。

6.1.1. 初期化

SOCKET ライブラリの初期化は nn::socket::Initialize() の呼び出しで行われます。

コード 6-1. SOCKET ライブラリの初期化
size_t nn::socket::GetRequiredMemorySize(size_t bufferSizeForSockets, 
                                         s32 maxSessions);
nn::Result nn::socket::Initialize(uptr bufferAddress, size_t bufferSize,
                                  s32 bufferSizeForSockets, s32 maxSessions);

bufferAddress bufferSize には、ライブラリが使用するワークメモリの先頭アドレスとそのサイズを指定します。ワークメモリは先頭アドレスのアライメントが nn::socket::BUFFER_ALIGNMENT ( 4096 Byte ) で、デバイスメモリ以外から確保 しなければなりません。ワークメモリに必要なサイズは、ソケット全体で確保する送受信バッファのサイズ(bufferSizeForSockets)と最大セッション数(maxSessions)を引数に、nn::socket::GetRequiredMemorySize() を呼び出して取得することができます。このとき指定する送受信バッファのサイズ(bufferSizeForSockets)は nn::socket::BUFFER_UNITSIZE_FOR_SOCKETS ( 4096 ) の倍数でなければなりません。

ソケットごとに割り当てる送受信バッファは nn::socket::SetSockOpt() で指定することができます。デフォルトでは 1 ソケットあたりの送受信バッファに、TCP の場合は 16 KByte(送信 8 KByte、受信 8 KByte)、UDP の場合は 32 KByte が割り当てられます。bufferSizeForSockets には、最大ソケット数が 1 であっても、最低でも 64 KByte 程度は確保してください。

maxSessions にはソケットを使用するスレッドの数(最大セッション数)を指定します。処理がブロックされるライブラリ関数の呼び出しを指定された数までのスレッドから受け付けることができます。厳密には、ブロックするライブラリ関数を同時に呼び出さなければ指定数以上のスレッドからライブラリ関数を呼び出すことができますが、そのような運用は推奨しません。ライブラリ関数の呼び出し中に最大セッション数を超えた場合、非同期呼び出し、同期呼び出しに関わらず、セッションが空くまで処理がブロックされます。また、データの着信や送信完了などでブロックが解除される条件を満たしていても、セッションが空くまで処理はブロックされます。非同期処理のはずが、同期処理と同じような挙動になる恐れがありますので注意してください。nn::socket::Poll() を利用すれば、ソケットの状態を一回の呼び出しで確認することができますので、必要なセッションの数を減らすことができます。

注意:

デフォルトで割り当てられる送受信バッファのサイズは、今後変更になる可能性があります。

6.1.2. ソケットの作成

リモートホストもそこに接続するローカル(マシン)も、まずソケットを作成しなければなりません。ソケットの作成は nn::socket::Socket() の呼び出しで行われます。アプリケーションで同時に使用することのできるソケットの数は 16 に制限されています。

補足:

同時使用可能ソケット数は、今後変更になる可能性があります。

コード 6-2. ソケットの作成
s32 nn::socket::Socket(s32 af, s32 type, s32 protocol);

af に指定することができるのは PF_INET のみです。

type には作成するソケットの種類を SOCK_STREAM(ストリーム)または SOCK_DGRAM(データグラム)から指定します。ストリームソケットは双方のソケットから接続を確立させる必要がありますが、データグラムソケットは一方的な送信と受信をデータブロック単位で行います。また、前者はデータブロックの到達順が保証されていますが、後者は保証されていない代わりに通信速度が高速になるという利点があります。

protocol にはソケットに用いるプロトコルを指定しますが、現時点では 0 を指定してください。protocol が 0 の場合、aftype で指定したプロトコルファミリとタイプに対するデフォルトのプロトコルが使われます。デフォルトのプロトコルは、ストリームソケットならば TCP、データグラムソケットならば UDP です。

返り値に 1 以上が返された場合、返り値はソケットを判別するためのソケット記述子です。0 以下が返された場合はエラーです。下表は発生するエラーの一覧です。

表 6-2. ソケット作成時に発生するエラー

返り値

エラーの詳細

ENETRESET

ライブラリが初期化されていません。

EAFNOSUPPORT

指定されたプロトコルファミリはサポートしていません。

EPROTONOSUPPORT

指定されたプロトコルはサポートされていません。

EMFILE

ソケット記述子をこれ以上作成することはできません。

ENOMEM

メモリ不足のため、ワークメモリを確保することができません。

EPROTOTYPE

指定されたソケットの種類はサポートしていません。

EINVAL

不正な呼び出しです。

ENETDOWN

ローカルのネットワークインタフェースがダウンしています。

EBUSY 内部処理がビジー状態です。Initialize() で指定するセッションが不足している可能性があります。

6.1.3. アドレスとポート番号の結び付け

ソケットは、どのアドレスでどのポート番号であるかを示すソケットアドレスが結び付けられて(バインドされて)いなければ通信を行うことができません。作成されただけのソケットには、まだソケットアドレスが結び付けられていません。ソケットアドレスをソケットに結び付けるには nn::socket::Bind() を呼び出します。

コード 6-3. アドレスおよびポート番号の結び付け
s32 nn::socket::Bind(s32 s, const nn::socket::SockAddrIn* sockAddr);

s には作成したソケットのソケット記述子を指定します。すでにソケットアドレスが結び付けられているソケットを指定した場合はエラーとなります。

sockAddr にはソケットアドレス(アドレスファミリ、ポート番号、アドレス)を設定した nn::socket::SockAddrIn 構造体へのポインタを渡します。アドレスの指定は IPv4 のみ対応しています。

処理に成功した場合は返り値に 0 が返されます。0 以外が返された場合はエラーです。下表は発生するエラーの一覧です。

表 6-3. アドレス、ポート番号の結び付け時に発生するエラー

返り値

エラーの詳細

ENETRESET

ライブラリが初期化されていません。

EBADF

指定されたソケット記述子が正しくありません。

ENETDOWN

ローカルのネットワークインタフェースがダウンしています。

EINVAL

無効な処理(ソケットがすでにバインド済みなど)です。

EAFNOSUPPORT

指定されたアドレスファミリはサポートしていません。

EADDRINUSE

指定されたソケットアドレスはすでに使用されています。

EBUSY 内部処理がビジー状態です。Initialize() で指定するセッションが不足している可能性があります。

6.1.4. 動作モード

ソケットの動作モードを nn::socket::Fcntl() の呼び出しで取得・設定することができます。

コード 6-4. 動作モードの設定・取得
s32 nn::socket::Fcntl(s32 s, s32 cmd, s32 val);

s には動作モードの設定・取得を行うソケットのソケット記述子を指定します。

cmd には動作モードを設定する(F_SETFL)のか、取得する(F_GETFL)のかを指定します。

val には 0 または動作モードに設定するフラグの論理和を指定します。cmdF_GETFL を渡している場合、この引数は無視されます。

設定可能なフラグは O_NONBLOCK(非封鎖モード)だけです。このフラグが設定されていないソケットの動作モードは封鎖モードとなります。ソケット作成時は封鎖モードに設定されています。

cmd F_GETFL を渡している場合の返り値は、設定されている動作モードを示すフラグの論理和です。cmdF_SETFL を渡している場合、返り値に 0 が返されたときは処理に成功しています。どちらの場合も、負の値が返されたときはエラーです。下表は発生するエラーの一覧です。

表 6-4. 動作モードの設定・取得時に発生するエラー

返り値

エラーの詳細

ENETRESET

ライブラリが初期化されていません。

EBADF

指定されたソケット記述子が正しくありません。

ENETDOWN

ローカルのネットワークインタフェースがダウンしています。

EINVAL

無効な処理(cmd の指定が不正)です。

EBUSY 内部処理がビジー状態です。Initialize() で指定するセッションが不足している可能性があります。

6.1.5. 接続の待ち受け

データグラムソケットはソケットアドレスのバインドが完了してすぐにデータの送受信を行うことができますが、ストリームソケットはデータの送受信を行う前に接続を確立していなければなりません。

サーバー側となるストリームソケットは、nn::socket::Listen() でクライアント側となるストリームソケットからの接続要求を待ち受けるキューを作成し、nn::socket::Accept() で着信(接続要求)を受け付けます。

コード 6-5. 接続の待ち受け
s32 nn::socket::Listen(s32 s, s32 backlog);
s32 nn::socket::Accept(s32 s, nn::socket::SockAddrIn* sockAddr);

どちらの関数でも、s には待ち受けに使用するソケットを指定します。このソケットはストリームソケットとして作成され、ソケットアドレスがバインドされていなければなりません。

backlog にはソケットのリッスンバックログとして使用するキューの最大数を指定します。0 または負の値を指定した場合は 1 を指定したものとみなされます。

sockAddr には受け付けた着信のソケットアドレスを格納する nn::socket::SockAddrIn 構造体へのポインタを指定します。

nn::socket::Listen() の返り値に 0 が返された場合は処理に成功しています。負の値が返された場合はエラーです。

nn::socket::Accept() は接続待ちのソケットがキューに存在しない場合、待ち受けに使用しているソケットが非封鎖モードでなければ処理をブロックします。返り値に 1 以上が返された場合、返り値は待ち受けに使用しているソケットと同じアドレスで新たに作成されたソケットのソケット記述子です。0 以下が返された場合はエラーです。

下表は発生するエラーの一覧です。

表 6-5. 接続の待ち受け時に発生するエラー

返り値

エラーの詳細

ENETRESET

ライブラリが初期化されていません。

EBADF

指定されたソケット記述子が正しくありません。

EOPNOTSUPP

サポートされていない処理です。

EINVAL

無効な処理です。

ENOBUFS

リソースが不足しています。

EAGAIN

接続待ちのソケットがありません。(非封鎖モード)

ECONNABORTED

接続がキャンセルされました。

EMFILE ソケット記述子をこれ以上作成することはできません。

ENOMEM

リッスンバックログ作成のためのメモリが不足しています。

EWOULDBLOCK

EAGAIN と同じです。

ENETDOWN ローカルのネットワークインタフェースがダウンしています。
EBUSY 内部処理がビジー状態です。Initialize() で指定するセッションが不足している可能性があります。

6.1.6. リモートホストへの接続

クライアント側となるストリームソケットはサーバー側のストリームソケットに nn::socket::Connect() で接続を試み、接続が確立されるまでデータの送受信を行うことはできません。データグラムソケットが nn::socket::Connect() を呼び出した場合は、送信先ソケットアドレスの書き換えだけが行われます。

コード 6-6. リモートホストへの接続
s32 nn::socket::Connect(s32 s, const nn::socket::SockAddrIn* sockAddr);

s には接続に使用するソケットのソケット記述子を指定します。ソケットにソケットアドレスがまだ結び付けられていない場合は、関数内で未使用のローカルソケットアドレスに結び付けられます。

sockAddr には接続先のソケットアドレスを設定した nn::socket::SockAddrIn 構造体へのポインタを指定します。

処理に成功した場合は返り値に 0 が返されます。0 以外が返された場合はエラーです。発生するエラーの一覧は以下のようになっています。

表 6-6. リモートホストへの接続時に発生するエラー

返り値

エラーの詳細

ENETRESET

ライブラリが初期化されていません。

EAFNOSUPPORT 指定されたプロトコルファミリはサポートしていません。

EBADF

指定されたソケット記述子が正しくありません。

EALREADY

既に非封鎖モードで接続を試みています。

ECONNREFUSED

接続する相手と同時オープンになった後、リセットされました。

ECONNRESET

接続がリセットされました。

EINPROGRESS

指定されたソケットは現在状態を変更しています。

EINVAL

無効な処理(sockAddr の指定が不正)です。

EISCONN

すでに使用されているソケットのため、処理に利用できません。

ENETDOWN

ローカルのネットワークインタフェースがダウンしています。

ENETUNREACH

接続先が見つかりません。

ENOBUFS

空いているソケットへのソケットアドレスの一時割り当てに失敗しました。

ETIMEDOUT

接続先の応答がタイムアウトしました。

EBUSY 内部処理がビジー状態です。Initialize() で指定するセッションが不足している可能性があります。

s に指定したソケットがデータグラムソケットの場合、ソケットの送信先ソケットアドレスが sockAddr に設定されているソケットアドレスに書き換えられるだけですので、動作モードによる処理の違いはありません。

s に指定したソケットがストリームソケットの場合、動作モードが封鎖モードならば接続が確立するまで処理がブロックされます。非封鎖モードならばすぐに制御が戻され、接続が確立しているかどうかは nn::socket::Poll() で確認することができます。

コード 6-7. リモートホストへの接続の確認
struct nn::socket::PollFd
{
    s32         fd;
    s32         events;
    s32         revents;
};
s32 nn::socket::Poll(nn::socket::PollFd fds[], u32 nfds, s32 timeout);

nn::socket::Poll() は複数のソケット記述子から、送受信が可能な状態のソケットがあるかどうかを調査します。

fds には調査対象のソケット記述子と調査条件を設定した nn::socket::PollFd 構造体の配列を指定します。構造体のメンバ変数 fd には調査対象のソケット記述子を、events には調査条件を設定します。revents には調査結果が格納されますので 0 を設定してください。

nn::socket::Connect() で接続が確立できなかった場合、revents には POLLRDNORMPOLLWRNORM のフラグが立てられ、そのあとのデータの送受信時にエラーが返されます。下表は、events に設定する調査条件と revents に格納される調査結果のフラグの一覧です。

表 6-7. 調査条件と調査結果に設定されるフラグ

フラグ

説明

POLLRDNORM

受信可能状態を示すフラグです。

POLLRDBAND

受信可能状態(優先データ)を示すフラグです。

POLLPRI

受信可能状態(最優先データ)を示すフラグです。

POLLWRNORM

送信可能状態を示すフラグです。

POLLWRBAND

送信可能状態(優先データ)を示すフラグです。

POLLERR

エラーが発生した状態を示すフラグです。調査結果でのみ設定されます。

POLLHUP

ソケットが切断された状態を示すフラグです。調査結果でのみ設定されます。

POLLNVAL

不正なソケット記述子が指定された状態を示すフラグです。調査結果でのみ設定されます。

POLLIN

受信可能状態を示すフラグです。POLLRDNORMPOLLRDBAND の論理和です。

POLLOUT

送信可能状態を示すフラグです。POLLWRNORM と同じです。

nfds には fds で指定した配列の要素数を指定します。

timeout には条件に一致するソケットが見つからなかったときのタイムアウト時間をミリ秒単位で指定します。0 以上の値または INFTIM(タイムアウトなし)で指定してください。

nn::socket::Poll() を呼び出すと、条件に一致するソケットが見つかるまでブロックは解除されません。ただし、ソケットが見つからないまま指定されたタイムアウト時間が経過するか、ソケットが切断されたりソケットに異常が発生したりしたときにはブロックが解除されます。

返り値に 1 以上が返された場合、返された値は条件に一致したソケットの数です。処理がタイムアウトした場合は 0 が返されます。負の値が返された場合はエラーです。下表は発生するエラーの一覧です。

表 6-8. リモートホストへの接続の確認時に発生するエラー

返り値

エラーの詳細

ENETRESET

ライブラリが初期化されていません。

ENETDOWN

ローカルのネットワークインタフェースがダウンしています。

EINVAL

無効な処理です。

EBUSY 内部処理がビジー状態です。Initialize() で指定するセッションが不足している可能性があります。

6.1.7. データの受信

データの受信は、nn::socket::RecvFrom()nn::socket::Recv()nn::socket::Read() のいずれかの関数で行うことができます。ストリームソケットの場合、サーバー側とクライアント側で接続が確立していなければデータの受信を行うことができません。

コード 6-8. データの受信
s32 nn::socket::RecvFrom(s32 s, void* buf, s32 len, s32 flags, 
                         nn::socket::SockAddrIn* sockFrom);
s32 nn::socket::Recv(s32 s, void* buf, s32 len, s32 flags);
s32 nn::socket::Read(s32 s, void* buf, s32 len);
s32 nn::socket::SockAtMark(s32 s);

s にはローカルソケットのソケット記述子を指定します。

buf には受信データを格納するバッファへのポインタを、len にはバッファのバイトサイズをそれぞれ指定します。

flags には受信時に行う特殊な動作を指示するフラグを指定します。MSG_DONTWAIT を指定した場合、ソケットの動作モードが封鎖モードであってもブロック状態にはなりません。MSG_PEEK を指定した場合、データの受信は行われますが状態は変化せず、再度同じデータを受信することができます。MSG_OOB を指定した場合、帯域外のデータを受信することができます。受信可能なデータに帯域外のデータが含まれているかは nn::socket::SockAtMark() の返り値が 1 であるかどうかで判断することができ、この関数は TCP プロトコルで緊急データの最後の 1 バイトが受信可能データの先頭にあるかどうかの判断にも使用することができます。

sockFrom nn::socket::SockAddrIn 構造体へのポインタを指定した場合、データの送信元ソケットアドレスが格納されます。

ストリームソケットで受信している場合、動作モードが封鎖モードならばデータを受信するまで処理がブロックされます。動作モードが非封鎖モードもしくは flagsMSG_DONTWAIT を指定しているときは、関数の呼び出し時に受信可能なデータのみをバッファに格納するため、処理がブロックされません。

データグラムソケットで受信している場合、受信データのすべてを 1 回の呼び出しで受信しなければなりません。受信データがバッファ以上のサイズである場合、flagsMSG_PEEK が指定されていなければ、バッファを超過したデータが破棄されてしまいます。1 回の呼び出しで受信できるデータの最大サイズは 1500 バイトです。

返り値に 1 以上が返された場合、返された値は受信したデータのバイト数です。ストリームソケットで返り値に 0 が返された場合、リモートホストからのデータ送信が終了したことを示します。データグラムソケットでは 0 が返されることはありません。負の値が返された場合はエラーです。下表は発生するエラーの一覧です。

表 6-9. データの受信時に発生するエラー

返り値

エラーの詳細

ENETRESET

ライブラリが初期化されていません。

EBADF

指定されたソケット記述子が正しくありません。

EAGAIN

受信待ちのデータがありません。(非封鎖モードまたは flagsMSG_DONTWAIT

EINVAL

無効な処理です。

EOPNOTSUPP

サポートされていない処理です。

ENOTCONN

指定されたソケットはリモートホストに接続されていません。

ECONNRESET

接続がリセットされました。

EINTR

処理が中止されました。

ETIMEDOUT

処理がタイムアウトしました。

ENETDOWN

ローカルのネットワークインタフェースがダウンしています。

EWOULDBLOCK

EAGAIN と同じです。

EBUSY 内部処理がビジー状態です。Initialize() で指定するセッションが不足している可能性があります。

6.1.8. データの送信

データの送信は、nn::socket::SendTo()nn::socket::Send()nn::socket::Write() のいずれかの関数で行うことができます。ストリームソケットの場合、サーバー側とクライアント側で接続が確立していなければデータの送信を行うことができません。

コード 6-9. データの送信
s32 nn::socket::SendTo(s32 s, const void* buf, s32 len, s32 flags, 
                       const nn::socket::SockAddrIn* sockTo);
s32 nn::socket::Send(s32 s, const void* buf, s32 len, s32 flags);
s32 nn::socket::Write(s32 s, const void* buf, s32 len);

s にはローカルソケットのソケット記述子を指定します。

buf には送信データが格納されているバッファへのポインタを、len にはバッファのバイトサイズをそれぞれ指定します。

flags には送信時に行う特殊な動作を指示するフラグを指定します。

sockTo nn::socket::SockAddrIn 構造体へのポインタを指定した場合、構造体に設定されているソケットアドレスにデータを送信します。

データの送信が完了するまで処理がブロックされます。

データグラムソケットで送信している場合、1 回の呼び出しで送信できるデータの最大サイズは 1500 バイトです。

処理に成功した場合は返り値に 0 が返されます。負の値が返された場合はエラーです。下表は発生するエラーの一覧です。

表 6-10. データの送信時に発生するエラー

返り値

エラーの詳細

ENETRESET

ライブラリが初期化されていません。

EAFNOSUPPORT 指定されたプロトコルファミリはサポートしていません。

EBADF

指定されたソケット記述子が正しくありません。

EAGAIN

封鎖モードの場合、すでに送信を試みましたがブロックされています。

非封鎖モードのストリームソケットの場合、帯域外に送信しようとするデータを内部送信バッファに送信予約できません。

ECONNRESET

接続がリセットされました。

EINTR

処理が中止されました。

EINVAL

無効な処理です。

EOPNOTSUPP

サポートされていない処理です。

EDESTADDRREQ

ストリームソケットの場合、ソケットは接続要求を受け付けるために使用されています。

データグラムソケットの場合、データを送信しようとする通信先のソケットアドレスが決まっていません。

EMSGSIZE

データが内部送信バッファのサイズを超えています。(データグラムソケットのみ)

ENETDOWN

ローカルのネットワークインタフェースがダウンしています。

ENETUNREACH

接続先が見つかりません。

ENOBUFS

空いているソケットへのソケットアドレスの一時割り当てに失敗しました。または、一時送信バッファの確保に失敗しました。

ENOTCONN

指定されたソケットはリモートホストに接続されていません。

ETIMEDOUT

処理がタイムアウトしました。

EWOULDBLOCK

EAGAIN と同じです。

EBUSY 内部処理がビジー状態です。Initialize() で指定するセッションが不足している可能性があります。

6.1.9. オプション設定

nn::socket::SetSockOpt()nn::socket::GetSockOpt() の呼び出しで、ソケットの内部設定値や内部状態情報など、オプション設定の変更と取得を行うことができます。

コード 6-10. オプション設定
s32 nn::socket::SetSockOpt(s32 s, s32 level, s32 optname, const void* optval, 
                           s32 optlen);
s32 nn::socket::GetSockOpt(s32 s, s32 level, int optname, void* optval, 
                           int* optlen)

引数に指定可能な値についてはヘッダファイル(nn/socket/socket_User.autogen.h)を参照してください。

処理に成功した場合は返り値に 0 が返されます。負の値が返された場合はエラーです。下表は発生するエラーの一覧です。

表 6-11. オプション設定時に発生するエラー

返り値

エラーの詳細

ENETRESET

ライブラリが初期化されていません。

EBADF

指定されたソケット記述子が正しくありません。

EINVAL

無効な処理です。

ENETDOWN

ローカルのネットワークインタフェースがダウンしています。

ENOMEM

メモリ不足のため、ワークメモリを確保することができません。

ENOPROTOOPT

サポートされていないオプションです。

EBUSY 内部処理がビジー状態です。Initialize() で指定するセッションが不足している可能性があります。

6.1.10. ソケットの切断

nn::socket::Shutdown() の呼び出しで、ソケット通信を遮断してソケットを切断することができます。

コード 6-11. ソケットの切断
s32 nn::socket::Shutdown(s32 s, s32 how);

s にはソケット通信を遮断するソケットのソケット記述子を指定します。

how には遮断方法を指定します。以降のデータの受信を遮断する場合は SHUT_RD を、送信を遮断する場合は SHUT_WR を、送受信ともに遮断する場合は SHUT_RDWR を指定してください。

処理に成功した場合は返り値に 0 が返されます。負の値が返された場合はエラーです。下表は発生するエラーの一覧です。

表 6-12. ソケットの切断時に発生するエラー

返り値

エラーの詳細

ENETRESET

ライブラリが初期化されていません。

EBADF

指定されたソケット記述子が正しくありません。

ENOTCONN

指定されたソケットはリモートホストに接続されていません。

EINVAL

無効な処理です。

EBUSY 内部処理がビジー状態です。Initialize() で指定するセッションが不足している可能性があります。

6.1.11. ソケットの破棄

同時に使用することのできるソケットの数は制限されています。不要になったソケットは nn::socket::Close() を呼び出して破棄してください。

コード 6-12. ソケットの破棄
s32 nn::socket::Close(s32 s);

s には破棄する(閉じる)ソケットのソケット記述子を指定します。

ソケットが閉じられると、そのソケットは以後使用できなくなります。閉じられるソケットを使用して呼び出された関数の処理がブロックされている場合、それぞれの関数のブロックが解除されてエラーが返されます。非封鎖モードに設定されていないストリームソケットが閉じられる場合、Linger オプションの設定に従って接続が閉じられます。

デフォルトでは、この関数はブロックせずに処理をすぐに返します。そして、バックグランドで残りの送信データを自動的に転送し、そのあとでソケットが使用していたリソースを解放します。

処理に成功した場合は返り値に 0 が返されます。負の値が返された場合はエラーです。下表は発生するエラーの一覧です。

表 6-13. ソケットの破棄時に発生するエラー

返り値

エラーの詳細

ENETRESET

ライブラリが初期化されていません。

EBADF

指定されたソケット記述子が正しくありません。

ENOTCONN

指定されたソケットはリモートホストに接続されていません。

EINVAL

無効な処理です。

EBUSY 内部処理がビジー状態です。Initialize() で指定するセッションが不足している可能性があります。

6.1.12. 終了

SOCKET ライブラリの使用を終了するときは nn::socket::Finalize() を呼び出して終了処理を行ってください。

コード 6-13. SOCKET ライブラリの終了
nn::Result nn::socket::Finalize(void);

ライブラリが使用していたリソースはすべて解放され、使用中のソケット記述子もすべて破棄されます。

6.1.13. ユーティリティ関数

SOCKET ライブラリには、ソケットアドレスの取得や、ネットワークアダプタ情報の取得、DNS を介したリモートホスト情報の取得、数値と文字列間でのアドレスの相互変換、数値のホストバイトオーダー変換とネットワークバイトオーダー変換などのユーティリティ関数が用意されています。

コード 6-14. ソケットアドレスの取得
s32 nn::socket::GetSockName(s32 s, nn::socket::SockAddrIn* sockAddr);
s32 nn::socket::GetPeerName(s32 s, nn::socket::SockAddrIn* sockAddr);

nn::socket::GetSockName()s に指定されたソケット記述子で示されるソケットのローカルソケットアドレスを sockAddr に取得します。ローカルソケットアドレスは通信元のソケットアドレスです。nn::socket::Bind()nn::socket::Connect() でローカルソケットアドレスを確定していない状態で、UDP プロトコルのソケット(データグラムソケット)に対してこの関数を呼び出した場合は 0.0.0.0 のアドレスが返され、TCP プロトコルのソケット(ストリームソケット)に対してこの関数を呼び出すとエラーが返されます。

nn::socket::GetPeerName()s に指定されたソケット記述子で示されるソケットのリモートソケットアドレスを sockAddr に取得します。リモートソケットアドレスは通信先のソケットアドレスです。

どちらの関数も処理に成功した場合は返り値に 0 が返されます。負の値が返された場合はエラーです。下表は発生するエラーの一覧です。

表 6-14. ソケットアドレスの取得時に発生するエラー

返り値

エラーの詳細

ENETRESET

ライブラリが初期化されていません。

EBADF

指定されたソケット記述子が正しくありません。

ENOTCONN

リモートホストに接続されていないか通信先がいません。(GetPeerName() のみ)

EINVAL

無効な処理です。

ENETDOWN

ローカルのネットワークインタフェースがダウンしています。

EBUSY 内部処理がビジー状態です。Initialize() で指定するセッションが不足している可能性があります。
コード 6-15. ネットワークアダプタ情報の取得
u32 nn::socket::GetHostId(void);

nn::socket::GetHostId() はローカルホストの IP アドレス(IPv4)を返します。0 が返されたときはネットワークを使用することができない状態です。返り値は IP アドレスを 32 ビット数値(ネットワークバイトオーダー)にしたものです。

コード 6-16. DNS を介したリモートホスト情報の取得
nn::socket::HostEnt* nn::socket::GetHostByName(const char8* name);
nn::socket::HostEnt* nn::socket::GetHostByAddr(const void* addr, s32 len, 
                                               s32 type);
s32 nn::socket::GetAddrInfo(const char8* nodeName, const char8* servName, 
                const nn::socket::AddrInfo* hints, nn::socket::AddrInfo** res);
void nn::socket::FreeAddrInfo(nn::socket::AddrInfo* head);
s32 nn::socket::GetNameInfo(const void* sa, char8* node, s32 nodeLen, 
                            char8* service, s32 serviceLen, s32 flags);

nn::socket::GetHostByName()nn::socket::GetHostByAddr() はどちらも指定されたホストの情報を取得します。前者はホスト名またはドット十進記法のアドレスの文字列から、後者はアドレスからホストを検索します。検索中は処理がブロックされ、DNS サーバーへの問い合わせを行う場合があります。返り値が示す構造体の実体はライブラリ内部のバッファですので、これらの関数はスレッドセーフではありません。

nn::socket::GetAddrInfo() はホスト名とサービス名で検索したホストの情報を取得します。検索動作の設定は hints に指定された nn::socket::AddrInfo 構造体のメンバ変数 flags で行います。flags には nn::socket::AddrInfoFlag 列挙子に定義されているフラグの論理和を設定します。

表 6-15. nn::socket::AddrInfoFlag 列挙子

フラグ

説明

AI_PASSIVE

nodeName NULL を指定した場合、0.0.0.0 を使用します。

AI_CANONNAME

nodeName に対応する正規名を取得します。

AI_NUMERICHOST

nodeName にドット十進記法のアドレスを指定している。

AI_NUMERICSERV

servName にポート番号を文字列で指定している。

検索中は処理がブロックされ、DNS サーバーへの問い合わせを行う場合があります。res に返される検索結果の構造体は nn::socket::Initialize() で指定したアロケータからライブラリが確保したメモリに格納されます。検索結果が不要になったときは nn::socket::FreeAddrInfo() でライブラリが確保したメモリを解放してください。

nn::socket::GetAddrInfo() は処理に成功した場合、返り値に 0 を返します。負の値を返した場合はエラーです。下表は発生するエラーの一覧です。

表 6-16. nn::socket::GetAddrInfo() によるホスト情報の取得時に発生するエラー

返り値

エラーの詳細

EAI_BADFLAGS hints AI_CANONNAME が指定されているにも関わらず nodeNameNULL または空文字列です。

EAI_FAIL

修復不可能なエラーが発生しました。

EAI_FAMILY hints に指定したアドレスファミリがサポートされていません。

EAI_MEMORY

メモリの確保に失敗しました。

EAI_NONAME

検索対象のホストが見つかりません。

EAI_SOCKTYPE

hints に指定したソケットの種類、ソケットのプロトコル、またはそれらの組み合わせがサポートされていません。

EAI_SYSTEM システムエラーが発生しました。Initialize を呼び出さずに本関数を呼び出した場合などに発生します。

nn::socket::GetNameInfo() はアドレスからホスト名とサービス名を検索します。検索動作の設定は flagsnn::socket::NameInfoFlag 列挙子に定義されているフラグの論理和を指定することで行います。

表 6-17. nn::socket::NameInfoFlag 列挙子

フラグ

説明

NI_NOFQDN

完全修飾ドメイン名(FQDN)のうちノード名の部分だけをホスト名として取得します。

NI_NUMERICHOST

node にホストアドレスをドット十進記法による文字列に変換したものを格納します。

NI_NAMEREQD

検索対象のホストが見つからない場合に、数値形式のホストアドレスを文字列に変換することで代替せずにエラー(EAI_NONAME)として扱います。

NI_NUMERICSERV

service にサービス(ポート番号)を数値記法による文字列に変換したものを格納します。

検索中は処理がブロックされ、DNS サーバーへの問い合わせを行う場合があります。

処理に成功した場合は返り値に 0 が返されます。負の値が返された場合はエラーです。下表は発生するエラーの一覧です。

表 6-18. nn::socket::GetNameInfo() によるホスト情報の取得時に発生するエラー

返り値

エラーの詳細

EAI_FAMILY

sa に指定したソケットアドレスに含まれるプロトコルファミリはサポートされていません。

EAI_NONAME

nodeLen または serviceLen に指定したバッファの大きさが不正です。

または、flagsNI_NAMEREQD を指定した呼び出しで検索対象のホストが見つかりません。

EAI_SYSTEM システムエラーが発生しました。Initialize を呼び出さずに本関数を呼び出した場合などに発生します。
コード 6-17. アドレス変換
s32 nn::socket::InetAtoN(const char* cp, nn::socket::InAddr* inp);
char* nn::socket::InetNtoA(nn::socket::InAddr in);
s32 nn::socket::InetPtoN(int af, const char* src, void* dst);
const char* nn::socket::InetNtoP(int af, const void* src, char* dst, 
                                 unsigned len);

nn::socket::InetAtoN()nn::socket::InetNtoA() を呼び出すことで、アドレスを十進記法の文字列と nn::socket::InAddr 構造体の数値形式間で相互変換することができます。変換することのできるアドレスは IPv4 のホストアドレスで、本来ならば nn::socket::InetPtoN()nn::socket::InetNtoP() はアドレスファミリを指定することができますが、CTR-SDK では AF_INET のみが指定可能です。

これらの関数は nn::socket::Initialize() を呼び出していなくても使用することができます。

コード 6-18. バイトオーダー変換
bit32 nn::socket::HtoNl(bit32 v);
bit32 nn::socket::NtoHl(bit32 v);
bit16 nn::socket::HtoNs(bit16 v);
bit16 nn::socket::NtoHs(bit16 v);

数値を、ホストバイトオーダーからネットワークバイトオーダーへ(HtoN)またはネットワークバイトオーダーからホストバイトオーダーへ(NtoH)変換します。関数名の末尾が "l" ならば 32 ビット数値、"s" ならば 16 ビット数値のバイトオーダー変換を行うことができます。

6.2. SSL 通信

SSL(SSL 通信)ライブラリは、ソケット通信をラッピングして通信の秘匿化を補助します。

6.2.1. 初期化

SSL ライブラリの初期化は nn::ssl::Initialize() の呼び出しで行われます。

コード 6-19. SSL ライブラリの初期化
nn::Result nn::ssl::Initialize(void);

6.2.2. 接続クラスの生成

SSL 通信は接続クラス(nn::ssl::Connection)にラッピングされたソケットを介して行われます。

コード 6-20. 接続クラスのコンストラクタとソケットのアサイン
class nn::ssl::Connection
{
    explicit Connection(s32 socket);
    explicit Connection();
    bool AssignSocket(s32 socket);
}

コンストラクタにはソケット記述子を引数に持つものと、引数なしのものがあります。引数なしで生成された接続クラスは、メンバ関数の AssignSocket() でソケットを結び付ける必要があります。

6.2.3. 通信先の設定

メンバ関数の Initialize() で、通信先のサーバーを接続クラスに設定します。

コード 6-21. 通信先の設定
class nn::ssl::Connection
{
    nn::Result Initialize(const char* pServerName, 
                          u32 verifyOpt = VERIFY_NONE);
}

pServerName には通信先サーバーのホスト名を指定します。SSL 通信時には、このホスト名とサーバー証明書の CommonName もしくは subjectAltName 拡張領域の dnsName/ipAddress を比較し、一致していなかった場合は接続エラーを返します。ホスト名として指定できる文字列の長さは、終端文字を含めて nn::socket::MAXDNAME バイトまでです。

verifyOpt には SSL 通信のサーバー検証に関するオプションを指定します。デフォルトのサーバー検証を用いる場合はこの引数を省略することができます。デフォルトでは実施されないサーバー検証のオプションを実施するときは、実施する検証オプションを表す nn::ssl::VerifyOption 列挙子を verifyOpt に指定します。複数の検証オプションを同時に設定したい場合は、その論理和を指定します。引数には、以下の値を設定することができます。

表 6-19. サーバー検証オプションに設定可能な値

説明

VERIFY_DATE

証明書の期限切れ検証を実施します。

USE_SESSION_CACHE

resumption を利用し、同じホストに連続で接続する際にセッションの再利用を試みます。

VERIFY_EV

EV 証明書検証を実施します。サーバー証明書が EV 証明書に紐づいたものでない限り、検証は失敗します。

信頼する EV 証明書は AddEVPolicyID() で設定することができます。

VERIFY_IGNORE

サーバー証明書検証を行いますが、検証結果は無視して接続します。検証結果は、GetCertVerifyErrors() で取得することができます。

GET_ALL_SERVER_CERT_CHAIN

SetServerCertBuffer() で指定したバッファにサーバー証明書を保存する際、証明書チェーン内の全証明書データを保存します。このオプションを指定しない場合は、サーバー証明書のみが保存されます。

nn::ssl::VerifyOption に定義されている上記以外のオプションはデフォルトで実施されます。

補足:

証明書チェーン内の全証明書データを保存する場合、データは "[証明書データ長(4 Byte)]+[証明書データ]" が並ぶ形で保存されます。データの並び順は、チェーン先頭の証明書(=サーバー証明書)から、チェーン末尾(CA 証明書)の順です。ただし CA 検証が NG の場合、CA 証明書が特定できなため、CA 証明書データは含まれません。

6.2.4. 証明書と CRL の設定

サーバーとのハンドシェイクに使用する、証明書と CRL の 2 つのストア、クライアント証明書を接続クラスに設定します。

コード 6-22. 証明書と CRL の設定
class nn::ssl::Connection
{
    nn::Result SetServerCertStore(nn::ssl::CertStore& certStore);
    nn::Result SetClientCert(nn::ssl::ClientCert& clientCert);
    nn::Result SetCRLStore(nn::ssl::CrlStore& crlStore);
}

証明書ストアの登録は SetServerCertStore() で、 CRL ストアの登録は SetCRLStore() で、クライアント証明書の登録は SetClientCert() で行います。

6.2.4.1. 証明書ストアのクラス

証明書ストアのクラスは nn::ssl::CertStore で定義されています。

コード 6-23. 証明書ストアのクラス
class nn::ssl::CertStore
{
    explicit CertStore();
    virtual ~CertStore(void);

    nn::Result Initialize(void);
    nn::Result Finalize(void);

    nn::Result RegisterCert(const u8* pCertData, size_t certDataSize, 
                            nn::ssl::CertId* pCertIdCourier=NULL);
    nn::Result RegisterCert(nn::ssl::InternalCaCert inCaCertName, 
                            nn::ssl::CertId* pCertIdCourier=NULL);
    nn::Result UnRegisterCert(nn::ssl::CertId certId);
}

コンストラクタに引数はありません。インスタンスを用意し、Initialize() で初期化を行ってください。

メンバ関数の RegisterCert() で証明書を証明書ストアに登録します。引数 pCertDatacertDataSize には、証明書データとそのバイトサイズを指定してください。機器に内蔵された証明書を証明書ストアに登録するためには、代わりに nn::ssl::InternalCaCert を引数に指定します。個別に証明書の登録を解除する必要がなければ、引数 pCertIdCourier を指定しなくてもかまいません。登録を解除していなかった証明書は、Finalize() を呼び出したときに登録が解除されます。証明書ストアに複数の証明書を登録する場合は、続けて RegisterCert() メンバ関数を呼び出してください。

メンバ関数の UnRegisterCert() で証明書ストアから個別に証明書の登録を解除することができます。RegisterCert() で証明書を登録したときに pCertIdCourier で受け取った証明書 ID を certId に指定してください。

証明書ストアが不要になったときには、必ず Finalize() を呼び出してください。

6.2.4.2. CRL ストアのクラス

CRL ストアのクラスは nn::ssl::CrlStore で定義されています。

コード 6-24. CRL ストアのクラス
class nn::ssl::CrlStore
{
    explicit CrlStore();
    virtual ~CrlStore(void);

    nn::Result Initialize(void);
    nn::Result Finalize(void);

    nn::Result RegisterCrl(const u8* pCrlData, size_t crlDatasize, 
                           nn::ssl::CrlId* pCrlIdCourier=NULL);
    nn::Result RegisterCrl(nn::ssl::InternalCrl inCrlName, 
                           nn::ssl::CrlId* pCrlIdCourier=NULL);
    nn::Result UnRegisterCrl(nn::ssl::CrlId crlId);
}

コンストラクタに引数はありません。インスタンスを用意し、Initialize() で初期化を行ってください。

メンバ関数の RegisterCrl() で CRL を CRL ストアに登録します。引数 pCrlDatacrlDatasize には、CRL データとそのバイトサイズを指定してください。機器に内蔵された CRL を CRL ストアに登録するためには、代わりに nn::ssl::InternalCrl を引数に指定します。個別に CRL の登録を解除する必要がなければ、引数 pCrlIdCourier を指定しなくてもかまいません。登録を解除していなかった CRL は、Finalize() を呼び出したときに登録が解除されます。CRL ストアに複数の CRL を登録する場合は、続けて RegisterCrl() メンバ関数を呼び出してください。

メンバ関数の UnRegisterCrl() で CRL ストアから個別に CRL の登録を解除することができます。RegisterCrl() で CRL を登録したときに pCrlIdCourier で受け取った CRL の ID を crlId に指定してください。

CRL ストアが不要になったときには、必ず Finalize() を呼び出してください。

6.2.4.3. クライアント証明書のクラス

クライアント証明書のクラスは nn::ssl::ClientCert で定義されています。

コード 6-25. クライアント証明書のクラス
class nn::ssl::ClientCert
{
    explicit ClientCert();
    virtual ~ClientCert(void);

    nn::Result Initialize(const u8* pCertData, size_t certDataSize, 
                          const u8* pPrivateKeyData, size_t privateKeyDataSize);
    nn::Result Initialize(nn::ssl::InternalClientCert inClientCertName);
    nn::Result Finalize(void);
}

コンストラクタに引数はありません。インスタンスを用意し、Initialize() で初期化を行います。

引数 pCertDatacertDataSize には、クライアント証明書の証明書データとそのバイトサイズを指定してください。引数 pPrivateKeyDataprivateKeyDataSize には、秘密鍵のデータとそのバイトサイズを指定してください。内蔵のクライアント証明書を使用する場合には、代わりに nn::ssl::InternalClientCert を指定します。

クライアント証明書が不要になったときには、必ず Finalize() を呼び出してください。

6.2.5. ハンドシェイク

メンバ関数の DoHandshake() で、接続クラスに設定された証明書ストア、CRL ストア、クライアント証明書を使って、接続先のサーバーとのハンドシェイクを行います。

コード 6-26. ハンドシェイク
class nn::ssl::Connection
{
    nn::Result SetServerCertBuffer(uptr bufferAddress, size_t bufferSize);
    nn::Result DoHandshake(void);
    nn::Result DoHandshake(size_t* pServerCertSize, 
                           u32*    pServerCertNum = NULL);
}

サーバーから通知されたサーバー証明書についての情報が必要なときは、引数ありの DoHandshake() を呼び出してください。サーバー証明書のバイトサイズは pServerCertSize に指定された変数のポインタに格納されます。

サーバー証明書を格納するバッファは SetServerCertBuffer() で指定します。指定するバッファはデバイスメモリ以外から確保しなければなりません。また、指定するバッファの先頭アドレスは nn::ssl::Connection::BUFFER_ALIGNMENT ( 4096 Byte ) のアライメント、サイズは nn::ssl::Connection::BUFFER_UNITSIZE ( 4096 ) の倍数でなければなりません。SetServerCertBuffer() でバッファを指定せずに引数ありの DoHandshake() を呼び出した場合は、引数なしの DoHandshake() を呼び出したときと同じ動作になります。

6.2.6. データの送受信

ハンドシェイクに成功したことを確認できたら、SSL 接続経由でのデータの送受信を開始することができます。

コード 6-27. SSL 接続経由でのデータの送受信
class nn::ssl::Connection
{
    nn::Result Read(u8* pDataBuf, size_t dataBufSize, 
                    size_t* pReadSizeCourier = NULL);
    nn::Result Peek(u8* pDataBuf, size_t dataBufSize, 
                    size_t* pReadSizeCourier = NULL);
    nn::Result Write(const u8* pDataBuf, size_t dataBufSize, 
                     size_t* pWrittenDataSizeCourier = NULL);
}

Read()Peek() はともにデータの受信を行うメンバ関数ですが、Peek() で受信した場合は状態を変化させずに受信済みのデータを受け取ることができます。引数 pDataBufdataBufSize には、受信データを受け取るバッファとそのバイトサイズを指定してください。引数 pReadSizeCourier には、受け取ったデータのサイズが必要な場合にサイズを受け取る変数のポインタを指定してください。

Write() はデータの送信を行うメンバ関数です。引数 pDataBufdataBufSize には、送信するデータが格納されているバッファとそのバイトサイズを指定してください。引数 pWrittenDataSizeCourier には、送信されたデータのサイズが必要な場合にサイズを受け取る変数のポインタを指定してください。

6.2.7. 接続の切断

同時に使用することのできる SSL 接続の数は制限されています。そのため、不要になった接続クラスに対してメンバ関数 Shutdown() を呼び出し、SSL 接続の切断と終了処理を行ってください。

コード 6-28. SSL 接続の切断
class nn::ssl::Connection
{
    nn::Result Shutdown(void);
}

6.2.8. 終了

SSL ライブラリの使用を終了するときは nn::ssl::Finalize() を呼び出して終了処理を行ってください。

コード 6-29. SSL ライブラリの終了
nn::Result nn::ssl::Finalize(void); 

6.3. HTTP 通信

HTTP(HTTP 通信)ライブラリは、指定された URL へ接続して HTTP プロトコルでのネットワーク通信を行うライブラリです。HTTPS 通信にも対応しています。

6.3.1. 初期化

HTTP ライブラリの初期化は nn::http::Initialize() の呼び出しで行われます。

コード 6-30. HTTP ライブラリの初期化
nn::Result nn::http::Initialize(uptr bufferAddress = 0, size_t bufferSize = 0);

HTTP ライブラリを利用するときは、事前にこの関数を呼び出しておかなければなりません。すでに初期化が行われている場合は、返り値に nn::http::ResultAlreadyInitializedErr が返されます。

bufferAddress bufferSize を指定します。通信用バッファとして渡すバッファは デバイスメモリ以外から確保 しなければなりません。また、バッファの先頭アドレスのアライメントは nn::http::BUFFER_ALIGNMENT ( 4096 Byte )、バッファのサイズは nn::http::BUFFER_UNITSIZE ( 4096 ) の倍数でなければなりません。

6.3.2. 接続クラスの生成

HTTP 接続には nn::http::Connection クラスを使用します。このクラスのインスタンスは、1 URL への接続と通信を単位に生成しなければなりません。システム全体およびアプリケーションで同時に使用することのできる通信リソース(接続数)には制限がありますので、接続数の管理には注意してください。

コンストラクタには、引数ありとなしの 2 種類が用意されています。引数なしのコンストラクタでインスタンスを生成した場合は、メンバ関数の Initialize() を呼び出して、接続先の URL とメソッド、デフォルトプロキシの使用についての設定を行う必要があります。

コード 6-31. HTTP 接続クラスのコンストラクタと初期化
class nn::http::Connection
{
    explicit Connection(void);
    explicit Connection(const char* pUrl, 
                        RequestMethod method = REQUEST_METHOD_GET, 
                        bool isUseDefaultProxy = true);
    nn::Result Initialize(const char* pUrl, 
                          RequestMethod method = REQUEST_METHOD_GET, 
                          bool isUseDefaultProxy = true);
}

pUrl には接続先の URL を指定します。

method には HTTP リクエストのメソッドを指定します。メソッドは以下の 3 種類から選択します。

表 6-20. メソッドの一覧

定義

メソッドの種類

REQUEST_METHOD_GET

GET メソッド。送信するデータは接続先の URL に埋め込みます。

REQUEST_METHOD_POST

POST メソッド。送信するデータは POST データとして追加します。

REQUEST_METHOD_HEAD

HEAD メソッド。接続先の URL からメッセージヘッダだけを受信します。

isUseDefaultProxy true を指定した場合は、接続時にデフォルトのプロキシ設定を利用します。デフォルト以外のプロキシ設定を利用する場合はこの引数に false を指定し、別途プロキシの設定を行ってください。

Initialize() で返される nn::Result クラスがエラーのときは、Description でエラーの原因を判断することができます。

表 6-21. 初期化時に発生するエラー

Description の値

エラーの原因

ER_CONN_ADD

システム全体の通信リソースに空きがありません。

ER_REQ_URL

指定された URL が不正です。

ER_ALREADY_ASSIGN_HOST

すでに接続先の URL が指定されています。

ER_CONN_PROCESS_MAX

アプリケーションに割り当てられている通信リソースに空きがありません。

6.3.3. 通信設定

プロキシ設定やベーシック認証の設定、通信時に使用するソケットの受信バッファサイズの設定など、通信に関する設定を行います。これらの設定は接続を開始する前に行わなければなりません。

コード 6-32. 通信設定
class nn::http::Connection
{
    nn::Result SetProxy(const char* pProxyName, u16 port, 
                        const char* pUserName, const char* pPassword);
    nn::Result SetBasicAuthorization(const char* pUserName, 
                                     const char* pPassword);
}

デフォルトのプロキシ設定を利用しない場合は、SetProxy() でプロキシ設定を行います。pProxyNameport にはプロキシの URL とポート番号を、pUserNamepPassword には認証に使用するユーザーとパスワードを指定します。

接続時にベーシック認証が必要な場合は、SetBasicAuthorization() でベーシック認証の設定を行います。pUserNamepPassword には認証に使用するユーザーとパスワードを指定します。

6.3.3.1. HTTPS 通信を行う場合の設定

HTTPS 通信を行う場合は、CA 証明書や CRL 、クライアント証明書など、SSL 通信に必要な設定を行わなければなりません。

コード 6-33. HTTPS 通信の設定
class nn::http::Connection
{
    nn::Result SetRootCa(const u8* pCertData, size_t certDataSize);
    nn::Result SetRootCa(nn::http::InternalCaCertId inCaCertName);
    nn::Result SetRootCaStore(nn::http::CertStore& certStore);
    nn::Result SetCrl(const u8* pCrlData, size_t crlDataSize);
    nn::Result SetCrl(nn::http::InternalCrlId inCrlName);
    nn::Result SetCrlStore(CrlStore& crlStore);
    nn::Result SetClientCert(const u8* pCertData, size_t certDataSize, 
                             const u8* pPrivateKeyData, size_t privateKeyDataSize);
    nn::Result SetClientCert(nn::http::InternalClientCertId inClientCertName);
    nn::Result SetClientCert(nn::http::ClientCert& clientCert);
    nn::Result GetSslError(s32* pResultCodeBuf) const;
    nn::Result SetVerifyOption(u32 verifyOption);
    nn::Result DisableVerifyOptionForDebug(u32 excludeVerifyOptions);
}

CA 証明書の登録は SetRootCa() で行います。証明書は証明書データを直接登録することも、機器に内蔵された CA 証明書を nn::http::InternalCaCertId で指定して登録することもできます。

複数の CA 証明書を設定する必要がある場合は、SetRootCa() を複数回呼び出して設定する以外にも、nn::http::CertStore クラスのインスタンスを引数に SetRootCaStore() を呼び出して設定することもできます。この場合、複数の接続で同じ CA 証明書セットを利用することができます。

CA 証明書セットを利用して通信しているときは、nn::http::CertStore クラスのインスタンスを破棄しないでください。

コード 6-34. nn::http::CertStore クラスの定義
class nn::http::CertStore
{
    explicit CertStore();
    virtual ~CertStore (void);

    nn::Result Initialize(void);
    nn::Result Finalize(void);
    nn::Result RegisterCert(const u8* pCertData, size_t certDataSize, 
                            nn::http::CertId* pCertIdCourier=NULL);
    nn::Result RegisterCert(nn::http::InternalCaCertId inCaCertName, 
                            nn::http::CertId* pCertIdCourier=NULL);
    nn::Result UnRegisterCert(nn::http::CertId certId);
}

nn::http::CertStore クラスを利用するときは、必ず Initialize() で初期化を行ってください。CA 証明書の登録は RegisterCert() で行います。複数回呼び出すことで、複数の CA 証明書が登録された CA 証明書セットになります。

個別に CA 証明書の登録を解除する場合は、登録時に pCertIdCourier に返される値を引数に UnRegisterCert() を呼び出してください。インスタンスそのものが不要になったときは、必ず Finalize() で終了処理を行ってから破棄してください。

CRL の登録は SetCrl() で行います。 CRL データを直接登録することも、機器に内蔵された CA 証明書を nn::http::InternalCrlId で指定して登録することもできます。ただし、現時点では内蔵 CRL は存在しません。

CA 証明書の登録と同じように、複数の CRL を設定する必要がある場合は、 SetCrl() を複数回呼び出して設定する以外にも、 nn::http::CrlStore クラスのインスタンスを引数に SetCrlStore() を呼び出して設定することができます。 nn::http::CrlStore クラスの使い方は nn::http::CertStore クラスと同様です。

コード 6-35. nn::http::CrlStore クラスの定義
class nn::http::CrlStore
{
    explicit CrlStore();
    virtual ~CrlStore (void);

    nn::Result Initialize(void);
    nn::Result Finalize(void);
    nn::Result RegisterCrl(const u8* pCrlData, size_t crlDataSize, 
                            nn::http::CrlId* pCrlIdCourier=NULL);
    nn::Result RegisterCrl(nn::http::InternalCrlId inCrlName, 
                            nn::http::CrlId* pCrlIdCourier=NULL);
    nn::Result UnRegisterCrl(nn::http::CrlId crlId);
}

クライアント証明書の設定は SetClientCert() で行います。設定の方法には、証明書と秘密鍵を指定する方法と機器に内蔵されているクライアント証明書を指定する方法、nn::http::ClientCert クラスのインスタンスを利用して複数の接続で同じクライアント証明書を利用する方法があります。

nn::http::ClientCert クラスを複数の接続で利用する場合は、通信中にインスタンスを破棄しないでください。

コード 6-36. nn::http::ClientCert クラスの定義
class nn::http::ClientCert
{
    explicit ClientCert();
    virtual ~ClientCert (void);

    nn::Result Initialize(const u8* pCertData, size_t certDataSize, 
                          const u8* pPrivateKeyData, size_t privateKeyDataSize);
    nn::Result Initialize(nn::http::InternalClientCertId inClientCertName);
    nn::Result Finalize(void);
}

nn::http:ClientCert クラスの初期化関数 Initialize() には、証明書と秘密鍵を指定するものと、機器に内蔵されているクライアント証明書を指定するものがあります。インスタンスが不要になったときは、必ず Finalize() で終了処理を行ってから破棄してください。

デフォルトでは有効となっていないサーバーの検証オプションを SetVerifyOption() で有効にすることができます。デフォルトでは、VERIFY_COMMON_NAME(CommonName 検証)、VERIFY_ROOT_CA(RootCA 検証)、VERIFY_SUBJECT_ALT_NAME(SubjectAlternativeName 検証)が有効となっています。

現在、追加で有効にすることができる検証オプションは、VERIFY_DATE(証明書の期限切れ検証)、USE_SESSION_CACHE(resumption の利用)、VERIFY_EV(EV 証明書検証)の 3 つです。

DisableVerifyOptionForDebug() で検証オプションを無効にすることができますが、デバッグ目的以外での使用はなるべく控えてください。

HTTPS 通信中に発生したエラーは GetSslError() の引数 pResultCodeBuf に格納される値で判断することができます。格納されるエラーコードは nn::ssl::ResultCode で定義されている値です。

6.3.4. 送信データの設定

接続を開始してデータを送信する前に、メッセージフィールドのヘッダの追加や、POST データの追加を行います。後述する POST データ遅延設定モードを利用すると、接続を開始したあとに POST データを追加することができます。

コード 6-37. 送信前に行うヘッダ・データの追加
class nn::http::Connection
{
    nn::Result AddHeaderField(const char* pLabel, const char* pValue);
    nn::Result SetPostDataEncoding(nn::http::EncodingType type);
    nn::Result AddPostDataAscii(const char* pLabel, const char* pValue);
    nn::Result AddPostDataBinary(const char* pLabel, const void* pValue, 
                                 size_t valueSize);
    nn::Result AddPostDataRaw(const void* pValue, size_t valueSize);
}

メッセージフィールドにヘッダを追加するには AddHeaderField() を呼び出してください。たとえば、pLabelpValue"Connection""keep-alive" を指定すると、Connection ラベルに "keep-alive" を指定したヘッダが追加されます。

POST データを追加するには、最初に SetPostDataEncoding() でエンコードの方法を指定し、AddPostDataAscii() などで POST データを追加します。

type に渡すエンコードの方法は、nn::http::EncodingType 列挙子に定義された値から選択します。

表 6-22. POST データのエンコード方法

定義

説明

ENCODING_TYPE_AUTO

すべて AddPostDataAscii() で追加した場合は URL エンコード、1 つでも AddPostDataBinary() で追加した場合はマルチパートで送信します。(デフォルト)

ENCODING_TYPE_URL

データを URL エンコードして送信します。POST データを追加するときは、AddPostDataAscii() を呼び出します。

ENCODING_TYPE_MULTIPART

データをエンコードせずに送信します。AddPostDataAscii() で追加された POST データはエンコードされません。

AddPostDataAscii() で追加された POST データ(ASCII 文字列)は、URL エンコードされて送信される場合にはラベル、データともにエンコードされます。POST データにバイナリが含まれる場合や、エンコード方法にマルチパートを指定した場合はラベルもデータもエンコードされません。

AddPostDataBinary() で追加された POST データ(バイナリデータ)は、エンコードされずにそのまま送信されます。

AddPostDataRaw() は上記 2 つの関数とは異なり、複数の POST データを一括して追加する場合に呼び出します。POST データにはバイナリ形式のみ指定可能です。この関数のあとに AddPostDataAscii()AddPostDataBinary() を呼び出した場合は Description に ER_POST_ADDED_ANOTHER が設定されたエラーが返されます。また、再度 AddPostDataRaw() を呼び出した場合は前に追加したデータが破棄され、新たに追加したデータが有効になります。

6.3.4.1. POST データ遅延設定モード

POST データ遅延設定モードを利用すると、接続を開始したあとに POST データを送信することができます。このモードで POST データを送信する場合は、SendPostData*() が同期処理のため、送信が完了するまで処理がブロックされることに注意してください。ただし、引数 timeout を持つオーバーロードは指定された時間で処理をタイムアウトさせることができます。タイムアウトした場合、内部で Cancel() が呼び出され、接続はキャンセル状態になります。

コード 6-38. POST データ遅延設定モードでの送信
class nn::http::Connection
{
    nn::Result SetLazyPostDataSetting(nn::http::PostDataType dataType);
    nn::Result NotifyFinishSendPostData(void);
    nn::Result SendPostDataAscii(const char* pLabel, const char* pValue);
    nn::Result SendPostDataAscii(const char* pLabel, const char* pValue, 
                                 const nn::fnd::TimeSpan& timeout);
    nn::Result SendPostDataBinary(
                const char* pLabel, const void* pValue, size_t valueSize);
    nn::Result SendPostDataBinary(
                const char* pLabel, const void* pValue, size_t valueSize, 
                const nn::fnd::TimeSpan& timeout);
    nn::Result SendPostDataRaw(const void* pValue, size_t valueSize);
    nn::Result SendPostDataRaw(const void* pValue, size_t valueSize, 
                               const nn::fnd::TimeSpan& timeout);
}

POST データ遅延設定モードは SetLazyPostDataSetting() の呼び出しで行います。引数 dataType で指定する送信データのタイプによって、POST データ送信のために呼び出すことのできる関数とデータの送信方法が異なります。送信データのタイプは nn::http::PostDataType 列挙子に定義された値から選択します。

表 6-23. POST データ遅延設定モードの送信データのタイプ

定義

説明

POST_DATA_TYPE_URLENCODE

URL エンコード形式で送信します。

POST_DATA_TYPE_MULTIPART

マルチパート形式で送信します。

POST_DATA_TYPE_RAW

RAW 形式で送信します。

URL エンコード形式で送信する場合、送信のために呼び出すことのできる関数は SendPostDataAscii()SendPostDataBinary() です。SendPostDataAscii() を呼び出した場合は URL エンコードされた「(ラベル名)=(データ内容)」という内容のデータが送信され、SendPostDataBinary() を呼び出した場合はエンコードされていない同じ内容のデータが送信されます。

マルチパート形式で送信する場合、送信のために呼び出すことのできる関数は SendPostDataAscii()SendPostDataBinary() です。1 回の呼び出しで送信されるデータは、boundary データとヘッダフィールドを先頭に付与した Chunked データとしてまとめて送信されます。ラベル名が "Content-Disposition: form-data; name=(ラベル名)" というヘッダフィールドとして boundary データのあとに付与されるのはどちらの関数でも同じですが、SendPostDataBinary() を呼び出した場合は "Content-Type: application/octet-stream\r\nContent-Transfer-Encoding: binary\r\n" というヘッダフィールドがその前に追加で付与されます。

RAW 形式で送信する場合。送信のために呼び出すことのできる関数は SendPostDataRaw() のみです。1 回の呼び出しで送信されるデータは、そのまま Chunked データとしてまとめて送信されます。

すべての POST データを送信し終えたあとは必ず、NotifyFinishSendPostData() を呼び出して、POST データの送信完了を通知してください。呼び出したあとは、処理が HTTP レスポンスの受信へと移行します。

6.3.5. 接続の開始

初期化で指定した URL に対して HTTP 接続を開始します。接続のために呼び出す関数には、接続が開始されるまで処理をブロックする Connect() と、すぐに処理を返す ConnectAsync() の 2 種類があります。また、接続をキャンセル状態にする Cancel() も用意されています。

コード 6-39. 接続の開始とキャンセル
class nn::http::Connection
{
    nn::Result Connect(void);
    nn::Result ConnectAsync(void);
    nn::Result Cancel(void);
}

Connect() はシステム全体で同時に接続可能な数の HTTP 接続がすでに行われていた場合に処理をブロックしますが、ConnectAsync() はすぐにエラーとして処理を返します。

Cancel() を呼び出すと HTTP 接続はキャンセル状態となり、以降その接続クラスでは通信が行えなくなります。

注意:

nn::http::Connection::Connect() もしくは nn::http::Connection::ConnectAsync() を使用して接続を開始した際、ヘッダ受信中にアクセスポイントの WAN 側のケーブルを抜いた場合無期限に受信を待ち続けてしまいます。

これを防ぐには、一定時間経過後に通信を切断(nn::http::Connection::Cancel())する処理を実装する必要があります。

6.3.6. レスポンスの受信

Connect() などで接続が完了したあと、HTTP 通信の結果(レスポンス)を受信するには、メッセージボディならば Read() を、レスポンスステータスならば GetStatusCode() を、メッセージヘッダならば GetHeaderField() または GetHeaderAll() を呼び出します。引数 timeout を持つオーバーロードは指定された時間で処理をタイムアウトさせることができます。タイムアウトした場合、内部で Cancel() が呼び出され、接続はキャンセル状態になります。

コード 6-40. レスポンスの受信
class nn::http::Connection
{
    nn::Result Read(u8* pBodyBuf, size_t bufLen);
    nn::Result Read(u8* pBodyBuf, size_t bufLen, 
                    const nn::fnd::TimeSpan& timeout);
    nn::Result GetStatusCode(s32* pStatusCodeCourier) const;
    nn::Result GetStatusCode(s32* pStatusCodeCourier, 
                             const nn::fnd::TimeSpan& timeout) const;
    nn::Result GetHeaderField(
                const char* pLabel, char* pFieldBuf, size_t bufSize, 
                size_t* pFieldLengthCourier = NULL) const;
    nn::Result GetHeaderField(
                const char* pLabel, char* pFieldBuf, size_t bufSize, 
                const nn::fnd::TimeSpan& timeout, 
                size_t* pFieldLengthCourier = NULL) const;
    nn::Result GetHeaderAll(
                char* pHeaderBuf, size_t bufSize, 
                size_t* pLengthCourier = NULL) const;
    nn::Result GetHeaderAll(
                char* pHeaderBuf, size_t bufSize, 
                const nn::fnd::TimeSpan& timeout, 
                size_t* pLengthCourier = NULL) const;
}

Read() は、pBodyBuf に指定された bufLen バイトのバッファにメッセージボディのデータを格納します。指定されたバッファサイズよりもメッセージボディのサイズが大きい場合は、バッファにできる限りのデータを格納して、バッファ不足を表す返り値(nn::http::ResultBodyBufShortage)を返しますが、再度 Read() を呼び出すことで続くデータの読み取りを継続することができます。それ以外でエラーが返された場合は、レスポンスの受信自体に失敗しています。ただし認証失敗などの HTTP ステータスエラーの場合は、エラーメッセージを示すメッセージボディの受信に成功しているため、通信成功時と同様の動作になります(つまりバッファ不足のときは nn::http::ResultBodyBufShortage が、受信完了ならば IsSuccess()true を返すインスタンスが返ります)。

GetStatusCode() は、引数 pStatusCodeCourier に指定された s32 型の変数にレスポンスステータスを格納します。通信エラーが発生したかどうかは、この関数で取得するレスポンスステータスで確認することができます。メッセージヘッダの受信が完了していない状態でこの関数の呼び出したときは、ヘッダ部の受信が完了するまで処理がブロックされます。先に Read() を呼び出していた場合はヘッダ部の受信は完了していますので、この関数は処理をすぐに返します。

GetHeaderField() は、引数 pFieldBuf に指定された bufSize バイトのバッファに pLabel で指定されたラベルのフィールドデータを格納します。pFieldBufNULLbufSize に 0、pFieldLengthCourierNULL 以外を指定した場合は、指定されたラベルのフィールドデータのバイトサイズを pFieldLengthCourier に格納します。

GetHeaderAll() はすべてのメッセージヘッダを引数 pHeaderBuf に指定された bufSize バイトのバッファに格納します。pHeaderBufNULLbufSize に 0、pLengthCourierNULL 以外を指定した場合は、pLengthCourier にメッセージヘッダの取得に必要なバッファサイズをバイト単位で格納します。メッセージヘッダの受信が完了していない状態でこれらの関数の呼び出したときは、ヘッダ部の受信が完了するまで処理がブロックされます。先に Read() を呼び出していた場合はヘッダ部の受信は完了していますので、これらの関数は処理をすぐに返します。

6.3.7. 接続状況などの取得

HTTP 接続クラス自身のステータスや発生したエラーのエラーコード、レスポンス受信の進捗を取得するための関数が用意されています。

コード 6-41. 接続状況などの取得
class nn::http::Connection
{
    nn::Result GetStatus(nn::http::Status* pStatusBuf) const;
    nn::Result GetError(nn::http::ResultCode* pResultCodeBuf) const;
    nn::Result GetProgress(size_t* pReceivedLen, size_t* pContentLen) const;
}

GetStatus() は、引数 pStatusBuf に指定されたバッファに接続状況を格納します。格納される接続状況は nn::http::Status 列挙子の値です。

GetError() は、引数 pResultCodeBuf に指定されたバッファにエラーコードを格納します。格納されるエラーコードは nn::http::ResultCode 列挙子の値です。

GetProgress() は、引数 pReceivedLenpContentLen に指定された変数に受信済みメッセージボディのバイトサイズと予定されるメッセージボディの総サイズを格納します。総サイズは HTTP レスポンスのメッセージヘッダ "Content-Length" の値ですので、メッセージヘッダに "Content-Length" が存在しなかった場合、pContentLen には 0 が格納されます。

6.3.8. 接続の終了

レスポンスの受信がすべて完了し、インスタンスが不要になったときは必ず Finalize() を呼び出してから破棄してください。接続先 URL を指定していない(Initialize() を呼び出していない)インスタンスに対して呼び出す必要はありません。

コード 6-42. 接続の終了
class nn::http::Connection
{
    nn::Result Finalize(void);
}

6.3.9. 終了

HTTP ライブラリの使用を終了するときは nn::http::Finalize() を呼び出して終了処理を行ってください。初期化が行われていない状態でこの関数を呼び出したときは nn::http::ResultNotInitializedErr が返されます。

コード 6-43. HTTP ライブラリの終了
nn::Result nn::http::Finalize(void);