前のページ次のページ上に戻るホーム SophiaFramework UNIVERSE 5.3

16.3. ソケット通信

ソケット通信を行うためのクラスとして、以下の 3 種類のクラスがあります。

表 16.2. ソケット通信クラス

クラス名 解説
SFXTCPSocket TCP ソケット通信のためのクラスです。 TCP クライアント、またはTCP サーバーを実装するための機能を提供します。
SFXSSLSocket SSL ソケット通信のためのクラスです。 SSL クライアントを実装するための機能を提供します。
SFXUDPSocket UDP ソケット通信のためのクラスです。 UDP クライアント、またはUDP サーバーを実装するための機能を提供します。
[Note] ストリームを利用したデータ送受信

SFXTCPSocket / SFXSSLSocket クラスでは、 ストリームを利用したデータ送受信が可能です。 また、ストリームを利用せずに Write / Read 関数を使用してデータを送受信することも可能です。

SFXUDPSocket クラスでは、ストリームを利用してデータを送受信することができません。 SFXUDPSocket::Send / SFXUDPSocket::Receive 関数を使用してデータを送受信します。

[Caution] mif ファイルの設定

ソケット通信を行うには、 mif ファイルの特権レベル設定で「ネットワーク」の項目をオンにする必要があります。

16.3.1. TCP ソケット通信

SFXTCPSocket クラスは、 TCP クライアントと TCP サーバーを実装する機能を提供します。

このクラスでは、ストリームを利用したデータ送受信が可能です。

ストリームを利用しない場合は SFXTCPSocket::Write / SFXTCPSocket::Read 関数を使用してデータを送受信します。

16.3.1.1. TCP クライアントの実装

TCP クライアントは、 SFXTCPSocket クラスを使用して以下の手順で実装します。

  1. TCP ソケット(SFXTCPSocket インスタンス)を作成します。
  2. SFXTCPSocket::Open 関数を呼び出して、 TCP ソケットを開きます。
  3. 必要に応じて SFXTCPSocket::Bind 関数を呼び出して、ローカル の IP アドレスとポート番号を TCP ソケットにバインドします。 バインド処理を省略した場合は、デフォルトのポート番号がバインドされます。
  4. SFXTCPSocket::Connect 関数を呼び出して、TCP サーバーへ接続リクエストを送信します。
  5. TCP サーバーへの接続リクエストの結果は、SFXTCPSocket::Connect 関数で登録したコールバック関数に通知されます。
  6. SFXTCPSocket::GetStreamReader 関数を呼び出して入力ストリームを取得し、データを受信します (ストリームを利用しない場合は、SFXTCPSocket::Read 関数を使用してデータを受信します)。
  7. SFXTCPSocket::GetStreamWriter 関数を呼び出して出力ストリームを取得し、データを送信します (ストリームを利用しない場合は、SFXTCPSocket::Write 関数を使用してデータを送信します)。
  8. 6. と 7. のデータ送受信処理を繰り返します。
  9. SFXTCPSocket::Close 関数を呼び出して、TCP ソケットを閉じます。

例 16.12. NTP サーバーからの時刻取得

// コールバック関数で使用するので、_socket はクラスのメンバ変数として定義する
class NetworkTime {
private:
    SFXTCPSocket _socket;
    SFXBinaryStreamReader _reader;
    SFXTimer _timer;

public:
    Void Start(Void); // NTP サーバーとの接続を開始する
    Void Stop(Void);  // NTP サーバーとの接続を終了する
    
    // コールバック関数の宣言
    XALLBACK_DECLARE_SFXTCPSOCKET(OnConnect)
    XALLBACK_DECLARE_SFXBINARYSTREAMREADER(OnFetch)
    XALLBACK_DECLARE_SFXTIMER(OnTimer)
};

#define     NTP_SERVER              ("www.example.com:37")
#define     TIMER_INTERVAL          (5000)

// NTP サーバーへの接続を開始する(アプリ開始時またはレジューム時に呼び出す)
Void NetworkTime::Start(Void)
{
    SFCError error(SFERR_NO_ERROR);
    // NTP サーバーのドメイン名とポート番号を設定する
    SFXSocketAddress address(NTP_SERVER);

    // タイマーコールバック関数を設定する
    _timer.Set(XALLBACK_INTERNAL(OnTimer));

    // ソケットを初期化する
    if ((error = _socket.Open()) == SFERR_NO_ERROR) {

        // NTP サーバーに接続する
        // ※1. 接続要求の結果は、OnConnect 関数に通知される
        // ※2. ドメイン名の解決は自動的に行われる
        if ((error = _socket.Connect(address, XALLBACK_INTERNAL(OnConnect))) == SFERR_NO_ERROR) {
            
            TRACE("> connecting...");
        }
        else {

            // エラーが発生したとき
            // ソケットを閉じる
            _socket.Close();
        }
    }
	
    if (error != SFERR_NO_ERROR) { 

        // エラーが発生したとき
        ...

        // タイマーをセットする。TIMER_INTERVAL ミリ秒後に OnTimer 関数が呼び出される
        _timer.Schedule(TIMER_INTERVAL);
    }
    return;
}

// NTP サーバーへの接続を終了する(アプリ終了時またはサスペンド時に呼び出す)
Void NetworkTime::Stop(Void)
{
    // 終了処理
    // ストリームを解放する
    _reader.Release();
    // ソケットを閉じる
    _socket.Close();
    return;
}

// 接続要求の結果が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXTCPSOCKET(NetworkTime, OnConnect, error)
{
    if (error == SFERR_NO_ERROR) {

        // データ受信用ストリームを取得する
        // ※ size 引数を指定しているのでストリームバッファは固定長(バッファサイズ: 64 バイト)
        if ((error = _socket.GetStreamReader(64, &_reader)) == SFERR_NO_ERROR) {

            // フェッチを行う: 実際にソケットからストリームバッファに 4 バイトのデータを受信する
            // ※ データ受信(フェッチ)の結果は、OnFetch 関数に通知される
            if ((error = _reader.Fetch(4, XALLBACK_INTERNAL(OnFetch))) == SFERR_NO_ERROR) {

                TRACE("> fetching...");
            }
            if (error != SFERR_NO_ERROR) { 

                 // エラーが発生したとき
                 // ストリームを解放する
                 _reader.Release();
            }
        }
    }
	
    if (error != SFERR_NO_ERROR) {  

        // エラーが発生したとき
        // ソケットを閉じる
        _socket.Close();
    }
    return;
}

// データ受信(フェッチ)の結果が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXBINARYSTREAMREADER(NetworkTime, OnFetch, error)
{
    SFXDate date; // 日付クラス
    UInt32 time;

    if (error == SFERR_NO_ERROR) {

        // ビッグエンディアンとしてデータを読み込む
        _reader.SetEndian(SFXBinaryStreamReader::ENDIAN_BIG);
        
        // ストリームバッファから time 変数に UInt32 型データを読み込む
        if ((error = _reader.ReadUInt32(&time)) == SFERR_NO_ERROR) {
            
            // time 変数の値を SFXDate インスタンスに設定する
            date.Set(time);
            
            // 受信したデータは 1900 年 1 月 1 日 からの時刻なので調整する
            date -= SFXDateDuration::Offset19000101();
            
            // 時刻をローカル時刻に変換する
            date += SFXDateDuration::LocalTimeOffset();

            // 時刻を書式に合わせて出力する
            TRACE("%s", date.Format("YYYY/MM/DD hh:mm:ss").GetCString());
        }
    }
	
    if (error != SFERR_NO_ERROR) {

        // エラーが発生したとき
        ...
    }

    // 終了処理

    // ストリームを解放する
    _reader.Release();

    // ソケットを閉じる
    _socket.Close();

    return;
}


// タイマーコールバック関数
XALLBACK_IMPLEMENT_SFXTIMER(NetworkTime, OnTimer)
{
    Start();
    return;
}
[Note] 注意

サスペンドとレジュームに対応した networktime アプリのサンプルコードが、 SophiaFramework UNIVERSE パッケージの Example/networktime フォルダにあります。

16.3.1.2. TCP サーバーの実装

TCP サーバーは、 SFXTCPSocket クラスを使用して以下の手順で実装します。

  1. TCP ソケット(SFXTCPSocket インスタンス)を作成します。
  2. SFXTCPSocket::Open 関数を呼び出して、 TCP ソケットを開きます。
  3. SFXTCPSocket::Bind 関数を呼び出して、ローカル IP アドレスとポート番号を関連付けます。
  4. SFXTCPSocket::Listen 関数を呼び出して、クライアントからの接続リクエストの受付を開始します。
  5. SFXTCPSocket::Accept 関数を呼び出して、接続リクエストを 1 つ選択し、接続を確立します。 このとき、以降のデータ送受信のためのソケットがこの関数の引数に返されます。
  6. SFXTCPSocket::GetStreamReader 関数を呼び出して入力ストリームを取得し、データを受信します (ストリームを利用しない場合は、SFXTCPSocket::Read 関数を使用してデータを受信します)。
  7. SFXTCPSocket::GetStreamWriter 関数を呼び出して出力ストリームを取得し、データを送信します (ストリームを利用しない場合は、SFXTCPSocket::Write 関数を使用してデータを送信します)。
  8. 6. と 7. のデータ送受信処理を繰り返します。
  9. SFXTCPSocket::Close 関数を呼び出して、TCP ソケットを閉じます。

以下は、ループバックで TCP クライアントと TCP サーバーが通信するコードです。

例 16.13. TCP サーバーの実装

// コールバック関数で使用するので、_socket* はクラスのメンバ変数として定義する
SFMTYPEDEFCLASS(MyClass)
class MyClass {
    SFMSEALCOPY(MyClass)
public:
    static SFCInvokerPtr Factory(Void);
private:
    SFXTCPSocket _socket1;  // TCP サーバー接続リクエスト受付ソケット
    SFXTCPSocket _socket2;  // TCP サーバー通信用ソケット
    SFXTCPSocket _socket3;  // TCP クライアント通信用ソケット

    Void ServerStart(Void);  // TCP サーバーの開始
    Void ClientStart(Void);  // TCP クライアントの開始
    Void Stop(Void);         // TCP サーバー/クライアントの終了

    // コールバック関数
    XALLBACK_DECLARE_SFXTCPSOCKET(/OnBind)
    XALLBACK_DECLARE_SFXTCPSOCKET(OnListen)
    XALLBACK_DECLARE_SFXTCPSOCKET(OnAccept)
    XALLBACK_DECLARE_SFXTCPSOCKET(OnConnect)
    XALLBACK_DECLARE_SFXTCPSOCKET(OnWrite)
    XALLBACK_DECLARE_SFXTCPSOCKET(OnRead)
};

// TCP サーバーを開始する
Void MyClass::ServerStart(Void)
{
    SFCError error(SFERR_NO_ERROR);

    TRACE("Start!");  

    // TCP サーバー接続リクエスト受付ソケットを開く
    if ((error = _socket1.Open()) == SFERR_NO_ERROR) {

        TRACE("Server Socket for Accepting the Client Requests Is Opened!"); 
        // Bind する
        OnBind(error);
    } else {
        
        // エラーが発生したとき
        Stop();
        TRACE("Fatal Open Error of the Server Socket for Accepting the Client Requests = %d", error);
    } 
}

// TCP クライアントを開始する
Void chelloworld::ClientStart(Void)
{
    SFCError error(SFERR_NO_ERROR);
    SFXSocketAddress address(SFXInetAddress::LoopbackInetAddress(), 1024);  // TCP サーバーのアドレス
    // ※外部のサーバーへ接続する場合は、実際の IP アドレスまたはドメイン名を指定する

    TRACE("Now, TCP Client Has Started.");  

    if ((error=_socket3.Open())== SFERR_NO_ERROR) {

        TRACE("Client Socket for Communicating with the Sever Has Opened!");

        // サーバーへ接続する 
        if ((error = _socket3.Connect(address, XALLBACK_INTERNAL(OnConnect))) == SFERR_NO_ERROR) {

            TRACE("connecting...");
        } else {

             // エラーが発生したとき
             TRACE("Fatal Connect Error = %d", error);
             Stop();
        }
    } else {

        // エラーが発生したとき
        Stop();
        TRACE("Fatal Client Socket Open Error = %d", error);
    }
    return;
}

// TCP サーバー/クライアントの終了
Void MyClass::Stop(Void)
{
    _socket1.Close();
    _socket2.Close();
    _socket3.Close();
    return;
}

// Bind 可能が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnBind, error)
{
    TRACE("Error at the Start of OnBind() = %d", error);

    SFXSocketAddress address(SFXInetAddress::LoopbackInetAddress(), 1024); // TCP サーバーのアドレス
    // ※外部のクライアントと接続する場合は、SFXInetAddress::AnyInetAddress() を指定する

    if (error == SFERR_NO_ERROR) {

        switch (error = _socket1.Bind(address)) {

            case SFERR_NO_ERROR:

                // Listen する
                OnListen(error);
                break;

            case AEE_NET_WOULDBLOCK:

                // Bind がブロックされたとき

                // Bind をスケジュールする: OnBind コールバック関数を登録する
                // ※ OnBind コールバック関数は Bind が可能になれば BREW AEE により呼び出される
                TRACE("Schedule Bind.");
                _socket1.ScheduleBind(XALLBACK_INTERNAL(OnBind));
                break;

            default:

                // エラーが発生したとき
                Stop();
                TRACE("Fatal OnBind Error = %d", error);
                break;
        }
    } else {

        // エラーが発生したとき
        Stop();
        TRACE("Fatal OnBind Error = %d", error);
    }
    return;
}

// Listen 可能が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnListen, error)
{
    TRACE("Error at the Start of OnListen() = %d", error);

    SFXSocketAddress address(SFXInetAddress::LoopbackInetAddress(), 1024); // TCP サーバーのアドレス

    if (error == SFERR_NO_ERROR) {

        // Listen する
        switch (error = _socket1.Listen()) {

            case SFERR_NO_ERROR:

                // TCP クライアントを開始する
                StartClient();   
                // Accept する
                OnAccept(error);
                break;

            case AEE_NET_WOULDBLOCK:

                // Listen がブロックされたとき

                // Listen をスケジュールする: OnListen コールバック関数を登録する
                // ※ OnListen コールバック関数は Listen が可能になれば BREW AEE により呼び出される
                TRACE("Schedule Listen.");
                _socket1.ScheduleListen(XALLBACK_INTERNAL(OnListen));
                break;

            default:

                // エラーが発生したとき
                Stop();
                TRACE("Fatal OnListen Error = %d", error);
                break;
        }
    } else {

        // エラーが発生したとき
        Stop();
        TRACE("Fatal OnListen Error = %d", error);
    }
    return;
}

// Accept 可能が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnAccept, error)
{
    TRACE("Error at the Start of OnAccept() = %d", error);

    if (error == SFERR_NO_ERROR) {

        switch (error = _socket1.Accept(&_socket2)) {

            case SFERR_NO_ERROR:

                // クライアントからのデータを受信する
                OnRead(error); 
                break;

            case AEE_NET_WOULDBLOCK:

                // Accept がブロックされたとき

                // Accept をスケジュールする: OnAccept コールバック関数を登録する
                // ※ OnAccept コールバック関数は Accept が可能になれば BREW AEE により呼び出される
                TRACE("Schedule Accept.");
                _socket1.ScheduleAccept(XALLBACK_INTERNAL(OnAccept));
                break;

            default:

                // エラーが発生したとき
                Stop();
                TRACE("Fatal OnAccept Error = %d", error);
                break;
        }
    } else {

        // エラーが発生したとき
        Stop();
        TRACE("Fatal OnAccept Error = %d", error);
        }
    return;
}


// 接続要求の結果が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnConnect, error)
{
    TRACE("Error at the Start of OnConnect() = %d", error);

    switch (error) {

        case SFERR_NO_ERROR:

            // サーバー接続に成功した!
            TRACE("Connected!");

            // データを送信する
            OnWrite(error);
            break;

        default:

            // エラーが発生したとき
            Stop();
            TRACE("Fatal OnConnect Error = %d", error);
            break;
    }
    return;
}

// Write(データ送信)可能が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnWrite, error)
{
    TRACE("Error at the Start of OnWrite() = %d", error);

    static ACharConst data[] = "hello world!";
    UInt32 size = sizeof(data) - 1;
 
    if (error == SFERR_NO_ERROR) {

       // サーバーへデータを送信する
       switch (error = _socket3.Write(data, &size)) {

            case SFERR_NO_ERROR:

                // データ送信に成功した!
                TRACE("Data has been sent!");

                break;

            case AEE_NET_WOULDBLOCK:

                // データ送信がブロックされたとき

                // Write をスケジュールする: OnWrite コールバック関数を登録する
                // ※ OnWrite コールバック関数は Write 可能になれば BREW AEE により呼び出される
                TRACE("Schedule Write.");
                _socket3.ScheduleWrite(XALLBACK_INTERNAL(OnWrite));
                break;

            default:

                // エラーが発生したとき
                Stop();
                TRACE("Fatal OnWrite Error = %d", error);
                break;
        }
    }
    return;
}

// Read(データ受信)可能が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnRead, error)
{
    TRACE("Error at the Start of OnRead() = %d", error);

    SFXBuffer buffer;
    UInt32 size(1024);
 
    buffer.SetSize(size);

    if (error == SFERR_NO_ERROR) {

        // クライアントからデータを受信する
        switch (error = _socket2.Read(buffer.GetBuffer(), &size)) {

            case SFERR_NO_ERROR:

                // データ受信に成功した!
                TRACE("Data has been reveived!");

                // 受信したデータを表示する
                buffer.SetSize(size + 1);
                buffer[buffer.GetSize() - 1] = '\0';
                TRACE("Received Data: %s", SFXAnsiString(buffer).GetCString());
                Stop();
                break;

            case AEE_NET_WOULDBLOCK:

                // データ受信がブロックされたとき

                // Read をスケジュールする: OnRead コールバック関数を登録する
                // ※ OnRead コールバック関数は Read 可能になれば BREW AEE により呼び出される
                TRACE("Schedule Read.");
                _socket2.ScheduleRead(XALLBACK_INTERNAL(OnRead));
                break;

            default:

                // エラーが発生したとき
                Stop();
                TRACE("Fatal OnRead Error = %d", error);
                break;
        }
    }
    return;
}
[Note] クライアントとサーバーが異なる端末のとき

クライアントとサーバーが異なる端末のときは、 下記の変更により TCP サーバーと TCP クライアントを実装します。

  1. 上記コードから、TCP クライアント通信用ソケット(_socket3)、 StartClient 関数、OnConnect 関数、OnWrite 関数を抜き出して TCP クライアントを実装します。 残りは TCP サーバーになります。
  2. TCP サーバーがバインドする IP アドレスは、 SFXInetAddress::LoopbackInetAddress() の代わりに SFXInetAddress::AnyInetAddress() を指定します。
  3. TCP クライアントが接続する IP アドレスは、 SFXInetAddress::LoopbackInetAddress() の代わりに TCP サーバーの IP アドレスまたはドメイン名を指定します。

16.3.2. SSL ソケット通信

SFXSSLSocket クラスは、 SSL クライアントを実装する機能だけを提供します。 SSL サーバーを実装する機能は提供しません。

このクラスでは、ストリームを利用したデータ送受信が可能です。

ストリームを利用しない場合は SFXSSLSocket::Write / SFXSSLSocket::Read 関数を使用してデータを送受信します。

16.3.2.1. SSL クライアントの実装

SSL クライアントは、 SFXSSLSocket クラスを使用して以下の手順で実装します。

  1. SSL ソケット(SFXSSLSocket インスタンス)を作成します。
  2. SFXSSLSocket::Open 関数を呼び出して、SSL ソケットを開きます。
  3. 必要に応じて SFXSSLSocket::Bind 関数を呼び出して、ローカル の IP アドレスとポート番号を SSL ソケットにバインドします。 バインド処理を省略した場合は、デフォルトのポート番号がバインドされます。
  4. SFXSSLSocket::SetTrustMode 関数を呼び出して、SSL 認証モードを設定します。(デフォルト: SSL_TRUST_MODE_FAIL )
  5. SFXSSLSocket::Connect 関数を呼び出して、サーバーへ接続リクエストを送信します。
  6. サーバーへの接続リクエストの結果は、SFXTCPSocket::Connect 関数で登録したコールバック関数に通知されます。
  7. SFXSSLSocket::Negotiate 関数を呼び出して、サーバーとネゴシエートします。
  8. ネゴシエートの結果は、SFXSSLSocket::Negotiate 関数で登録したコールバック関数に通知されます。
  9. SFXSSLSocket::GetStreamReader 関数を呼び出して入力ストリームを取得し、データを受信します (ストリームを利用しない場合は、SFXSSLSocket::Read 関数を使用してデータを受信します)。
  10. SFXSSLSocket::GetStreamWriter 関数を呼び出して出力ストリームを取得し、データを送信します (ストリームを利用しない場合は、SFXSSLSocket::Write 関数を使用してデータを送信します)。
  11. 9. と 10. の送受信処理を繰り返します。
  12. SFXSSLSocket::Close 関数を呼び出して、SSL ソケットを閉じます。

例 16.14. SSL クライアントの実装

// コールバック関数で使用するので、_socket はクラスのメンバ変数として定義する
class MyClass {
private:
    SFXSSLSocket_socket;               // SSL ソケット
    SFXAnsiStringStreamWriter _writer; // データ送信用ストリーム
    SFXAnsiStringStreamReader _reader; // データ受信用ストリーム

public:
    Void Start(Void);

    // コールバック関数
    XALLBACK_DECLARE_SFXSSLSOCKET(OnConnect)
    XALLBACK_DECLARE_SFXSSLSOCKET(OnNegotiate)
    XALLBACK_DECLARE_SFXANSISTRINGSTREAMWRITER(OnFlush)
    XALLBACK_DECLARE_SFXANSISTRINGSTREAMREADER(OnFetch)
};

Void MyClass::Start(Void)
{
    SFCError error;
    SFXSocketAddress host("www.example.com:995");

    // ソケットを開く
    if ((error = _socket.Open()) == SFERR_NO_ERROR) {
 
        // サーバーに接続する
        // ※1. 接続要求の結果は、OnConnect 関数に通知される
        // ※2. ホスト名(host)は自動的に解決される
        error = _socket.Connect(host, XALLBACK_INTERNAL(OnConnect));
    }
    if (error != SFERR_NO_ERROR) { 

        // エラーが発生したとき
        // ソケットを閉じる
        _socket.Close();
    }
    return;
}

// 接続要求の結果が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXSSLSOCKET(MyClass, OnConnect, error)
{
    if (error == SFERR_NO_ERROR) { 

        // エラーが発生していないとき
        // サーバーとの SSL ネゴシエーションを行う
        // ※ SSL ネゴシエーションの結果は OnNegotiate 関数に通知される
        error = _socket.Negotiate(XALLBACK_INTERNAL(OnNegotiate));
    }
    if (error != SFERR_NO_ERROR) { 

        // エラーが発生したとき
        // ソケットを閉じる
        _socket.Close();
    }
    return;
}

// SSL ネゴシエーションの結果が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXSSLSOCKET(MyClass, OnNegotiate, error)
{
    // 送信する文字列
    static AChar message[] = "GET / HTTP/1.0\r\n\r\n";

    if (error == SFERR_NO_ERROR) {

        // データ送信用ストリームを取得する
        // ※ size 引数を指定しているのでストリームバッファは固定長(バッファサイズ: 1024 バイト)
        if ((error = _socket.GetStreamWriter(1024, &_writer)) == SFERR_NO_ERROR) {

            // message 変数からストリームバッファにデータを書き込む
            if ((error = _writer.Write(message, lengthof(message))) == SFERR_NO_ERROR) {

                // フラッシュを行う: 実際にストリームバッファからソケットにデータを送信する
                // ※ データ送信(フラッシュ)の結果は、OnFlush 関数に通知される
                error = _writer.Flush(XALLBACK_INTERNAL(OnFlush));
            }
            if (error != SFERR_NO_ERROR) { 

                // エラーが発生したとき
                // データ送信用ストリームを解放する
                 _writer.Release();
            }
        }
    }
    if (error != SFERR_NO_ERROR) { 

        // エラーが発生したとき
        // ソケットを閉じる
        _socket.Close();
    }
    return;
}

// データ送信(フラッシュ)の結果が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMWRITER(MyClass, OnFlush, error)
{
    // 送信が終わったのでデータ送信用ストリームを解放する
    _writer.Release();

    if (error == SFERR_NO_ERROR) {

        // データ受信用ストリームを取得する
        // ※ size 引数を指定しているのでストリームバッファは固定長(バッファサイズ: 1024 バイト)
        if ((error = _socket.GetStreamReader(1024, &_reader)) == SFERR_NO_ERROR) {

            // フェッチを行う: 実際にソケットからストリームバッファにデータを受信する
            // ※ データ受信(フェッチ)の結果は、OnFetch 関数に通知される
            if ((error = _reader.Fetch(XALLBACK_INTERNAL(OnFetch))) != SFERR_NO_ERROR) {

                // エラーが発生したとき
                // データ受信用ストリームを解放する
               _reader.Release(); 
            }
        }
    }
    if (error != SFERR_NO_ERROR) { 

        // エラーが発生したとき
        // ソケットを閉じる
        _socket.Close();
    }
    return;
}

// データ受信(フェッチ)の結果が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMREADER(MyClass, OnFetch, error)
{
    SFXAnsiString string;

    if (error == SFERR_NO_ERROR) {

        // ストリームバッファから string 変数にデータを読み込む
        _reader > string;

        // string 変数の内容をデバッグウィンドウに表示する
        TRACE("%s", string.GetCString());
    }

    // 受信が終わったのでデータ受信用ストリームを解放する
    _reader.Release();

    // ソケットを閉じる
    _socket.Close();
    return;
}

16.3.3. UDP ソケット通信

SFXUDPSocket クラスは、 UDP クライアント、またはUDP サーバーを実装するための機能を提供します。

このクラスでは、ストリームを利用してデータ送受信することはできません。 SFXUDPSocket::Send / SFXUDPSocket::Receive 関数を呼び出してデータを送受信します。

16.3.3.1. UDP クライアントの実装

UDP クライアントは、 SFXUDPSocket クラスを使用して以下の手順で実装します。

  1. UDP ソケット(SFXUDPSocket インスタンス)を作成します。
  2. SFXUDPSocket::Open 関数を呼び出して、 UDP ソケットを開きます。
  3. 必要に応じて SFXUDPSocket::Bind 関数を呼び出して、ローカル の IP アドレスとポート番号を UDP ソケットにバインドします。 省略時は、デフォルトのポート番号がバインドされます。
  4. SFXUDPSocket::Send 関数を呼び出して、指定した送信先(UDP サーバー)の IP アドレスとポート番号へ UDP ソケット経由でデータを送信します。
  5. SFXUDPSocket::Close 関数を呼び出して、UDP ソケットを閉じます。
[Note] 注意

UDP ソケットからデータを受信するには、 ローカル の IP アドレスとポート番号を UDP ソケットにバインドして UDP サーバーを実装する必要があります。

16.3.3.2. UDP サーバーの実装

UDP サーバーは、 SFXUDPSocket クラスを使用して以下の手順で実装します。

  1. UDP ソケット(SFXUDPSocket インスタンス)を作成します。
  2. SFXUDPSocket::Open 関数を呼び出して、 UDP ソケットを開きます。
  3. SFXUDPSocket::Bind 関数を呼び出して、ローカル の IP アドレスとポート番号を UDP ソケットにバインドします。
  4. SFXUDPSocket::Receive 関数を呼び出して、 UDP ソケットから送信元(UDP クライアント)の IP アドレスとポート番号も含めてデータを受信します。
  5. SFXUDPSocket::Send 関数を呼び出して、指定した送信先の IP アドレスとポート番号へ UDP ソケット経由でデータを送信します。
  6. 4. と 5. の送受信処理を繰り返します。
  7. SFXUDPSocket::Close 関数を呼び出して、UDP ソケットを閉じます。
[Note] 注意

UDP サーバーは、 UDP クライアントとして別の UDP サーバーにデータを送信することも可能です。

例 16.15. UDP クライアント兼サーバーの実装

// コールバック関数で使用するので、_socket はクラスのメンバ変数として定義する
class MyClass {
private:
    SFXUDPSocket _socket;  // UDP ソケット

public:
    Void Start(Void);

    // コールバック関数
    XALLBACK_DECLARE_SFXUDPSOCKET(OnBind)
    XALLBACK_DECLARE_SFXUDPSOCKET(OnSend)
    XALLBACK_DECLARE_SFXUDPSOCKET(OnReceive)
};

Void MyClass::Start(Void)
{
    SFCError error;

    // ソケットを開く
    if ((error = _socket.Open()) == SFERR_NO_ERROR) {

        // ソケットをローカル の IP アドレスとポート番号にバインドする
        OnBind(SFERR_NO_ERROR);
    }
    return;
}

// Bind 可能が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXUDPSOCKET(MyClass, OnBind, error)
{
    SFXSocketAddress address(SFXInetAddress::LoopbackInetAddress(), 1024);

    // エラーが発生したかどうかチェックする
    if (error == SFERR_NO_ERROR) {

        error = _socket.Bind(address);

        switch (error) {

            case SFERR_NO_ERROR:

                // データを送信する
                OnSend(SFERR_NO_ERROR);
                break;

            case AEE_NET_WOULDBLOCK:
                // バインドがブロックされたとき

                // Bind をスケジュールする: OnBind コールバック関数を登録する
                // ※ OnBind コールバック関数は Bind 可能になれば BREW AEE により呼び出される
                _socket.ScheduleBind(XALLBACK_INTERNAL(OnBind));
                break;
        }
    }
    return;
}

// Send(データ送信)可能が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXUDPSOCKET(MyClass, OnSend, error)
{
    static ACharConst data[] = "udp!";
    SFXSocketAddress address(SFXInetAddress::LoopbackInetAddress(), 1024);
    UInt32 send_size;
    UInt32 data_size = sizeof(data) - 1;

    send_size = data_size;

    // エラーが発生したかどうかチェックする
    if (error == SFERR_NO_ERROR) {

        // データを送信する
        error = _socket.Send(address, data, &send_size);

        switch (error) {

            case SFERR_NO_ERROR:

                // 指定したサイズのデータが書き込まれたかチェックする
                // Send 関数は指定したサイズのデータを一度に書き込めないことがある
                // その場合、ここでは簡易化のためエラーとしている
                if (send_size == data_size) {

                    // データを受信する
                    OnReceive(SFERR_NO_ERROR);
                }
                else {

                    TRACE("...send failed...");
                }
                break;

            case AEE_NET_WOULDBLOCK:
                // データ送信がブロックされたとき

                // Send をスケジュールする: OnSend コールバック関数を登録する
                // ※ OnSend コールバック関数は Send 可能になれば BREW AEE により呼び出される
                _socket.ScheduleSend(XALLBACK_INTERNAL(OnSend));
                break;
        }
    }
    return;
}

// Receive(データ受信)可能が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXUDPSOCKET(MyClass, OnReceive, error)
{
    SFXSocketAddress socket;
    SFXBuffer buffer;
    UInt32 receive_size;
    UInt32 buffer_size(4);

    // エラーが発生したかどうかチェックする
    if (error == SFERR_NO_ERROR) {

        buffer.SetSize(buffer_size);
        receive_size = buffer_size;

        // データを受信する
        error = _socket.Receive(&socket, buffer.GetBuffer(), &receive_size);

        switch (error) {

            case SFERR_NO_ERROR:

                // 指定したサイズのデータが読み込まれたかチェックする
                // Receive 関数は指定したサイズのデータを一度に読み込めないことがある
                // その場合、ここでは簡易化のためエラーとしている
                if (receive_size == buffer_size) {

                    // 読み込んだデータを表示する
                    buffer.SetSize(buffer_size + 1);
                    buffer[buffer_size - 1] = '\0';
                    TRACE(":%s", SFXAnsiString(buffer).GetCString());

                    // ソケットを閉じる
                    _socket.Close();
                }
                else {

                    TRACE("...receive failed...");
                }
                break;

            case AEE_NET_WOULDBLOCK:
                // データ受信がブロックされたとき

                // Receive をスケジュールする: OnReceive コールバック関数を登録する
                // ※ OnReceive コールバック関数は Receive 可能になれば BREW AEE により呼び出される
                _socket.ScheduleReceive(XALLBACK_INTERNAL(OnReceive));
                break;
        }
    }
    return;
}