前のページ次のページ上に戻るホーム BREW C++ ライブラリ & GUI フレームワーク & XML ミドルウェア : SophiaFramework 4.1

16.3. ソケット通信

ソケット通信を行うためのクラスは 3 種類あります。

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

クラス名 解説
SFXTCPSocket TCP ソケット通信のためのクラスです。
SFXSSLSocket SSL ソケット通信のためのクラスです。
SFXUDPSocket UDP ソケット通信のためのクラスです。
[Caution] mif ファイルの設定

mif ファイルの特権レベル設定で「ネットワーク」の項目をオンにします。

16.3.1. TCP ソケット通信

TCP ソケット通信は、SFXTCPSocket クラスを使って以下の手順で行います。

  1. SFXTCPSocket クラスのインスタンスを作成します。
  2. SFXTCPSocket::Open 関数を呼び出して、 TCP ソケットをオープンします。
  3. SFXTCPSocket::Connect 関数を呼び出して、サーバーへ接続します。 接続先のアドレスとポート番号 ( SFXSocketAddress クラスのインスタンス ) と、接続確立の通知を受け取るコールバック関数を登録します。 ドメイン名の解決は SFXTCPSocket クラス内で自動的に行われます。
  4. サーバーへの接続に関する通知を受け取ると、SFXTCPSocket::Connect 関数で登録したコールバック関数が呼び出されます。
  5. SFXTCPSocket::GetStreamReader 関数を使って入力ストリームを取得し、データを受信します。
  6. SFXTCPSocket::GetStreamWriter 関数を使って出力ストリームを取得し、データを送信します。
  7. 5. と 6. の送受信処理を繰り返します。
  8. 最後に、SFXTCPSocket::Close 関数を呼び出して、TCP ソケットをクローズします。

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

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

public:
    Void Start(Void); // NTP サーバーとの接続を開始する
    Void Stop(Void);  // 接続途中で NTP サーバーとの接続を終了する
    
    // コールバック関数の宣言
    CALLBACK_DECLARE_SFXTCPSOCKET(OnConnect)
    CALLBACK_DECLARE_SFXBINARYSTREAMREADER(OnFetch)
};

// NTP サーバーに接続を開始する
Void NetworkTime::Start(Void)
{
    SFCError error(SFERR_NO_ERROR);
    // NTP サーバーのアドレスを設定する
    SFXSocketAddress address("www.example.com:37");

    // NTP サーバーとの接続を初期化する
    if ((error = _tcp.Open()) == SFERR_NO_ERROR) {

        // NTP サーバーに接続する
        // ( 接続の完了は、OnConnect 関数に通知される )
        if ((error = _tcp.Connect(address,
                CALLBACK_FUNCTION(OnConnect))) == SFERR_NO_ERROR) {
            
            TRACE("connecting...");

        }
        else {
            // エラーが発生したとき
            _tcp.Close();
        }
    }
	
    if (error != SFERR_NO_ERROR) { 
        // エラーが発生したとき
        // エラー処理

        ...

    }
    return;
}

// 接続途中で NTP サーバーとの接続を終了する場合に呼び出す
Void NetworkTime::Stop(Void)
{
    // 終了処理
    _reader.Release();
    _tcp.Close();
    return;
}

// 接続の完了が通知されるコールバック関数
CALLBACK_IMPLEMENT_SFXTCPSOCKET(NetworkTime, OnConnect, error)
{
    if (error == SFERR_NO_ERROR) {

        // データ受信用ストリームを取得する
        if ((error = _tcp.GetStreamReader(64, &_reader)) == SFERR_NO_ERROR) {

            // 4 バイトのデータ受信をする ( データ受信の完了は、OnFetch 関数に通知される )
            if ((error = _reader.Fetch(4, CALLBACK_FUNCTION(OnFetch)))
                == SFERR_NO_ERROR) {
                TRACE("fetching...");
            }
            if (error != SFERR_NO_ERROR) { 
                 // エラーが発生したとき
                 _reader.Release();
            }
        }
    }
	
    if (error != SFERR_NO_ERROR) {  
        // エラーが発生したとき
        _tcp.Close();
    }
    return;
}

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

    if (error == SFERR_NO_ERROR) {

        // データをビッグ エンディアンとして読み込む設定をする
        _reader.SetEndian(SFXBinaryStreamReader::ENDIAN_BIG);
        
        // データを UInt32 型として読み込み、time 変数に格納する
        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();
    _tcp.Close();
    return;
}

16.3.2. SSL ソケット通信

SSL ソケット通信は、SFXSSLSocket クラスを使って以下の手順で行います。

  1. SFXSSLSocket クラスのインスタンスを作成します。
  2. SFXSSLSocket::Open 関数を呼び出して、SSL ソケットをオープンします。
  3. SFXSSLSocket::SetTrustMode 関数を呼び出して、検証モードを設定します。(デフォルト: SSL_TRUST_MODE_FAIL )
  4. SFXSSLSocket::Connect 関数を呼び出して、サーバーへの接続します。 接続先のアドレスとポート番号 ( SFXSocketAddress クラスのインスタンス ) と、接続確立の通知を受け取るコールバック関数を登録します。 ドメイン名の解決は SFXSSLSocket クラス内で自動的に行われます。
  5. サーバーへの接続に関する通知を受け取ると、SFXSSLSocket::Connect 関数で登録したコールバック関数が呼び出されます。
  6. SFXSSLSocket::Negotiate 関数を呼び出して、サーバーとネゴシエートします。同時に、コールバック関数を登録します。
  7. サーバーとのネゴシエートに関する通知を受け取ると、SFXSSLSocket::Negotiate 関数で登録したコールバック関数が呼び出されます。
  8. SFXSSLSocket::GetStreamReader 関数を使って入力ストリームを取得し、データを受信します。
  9. SFXSSLSocket::GetStreamWriter 関数を使って出力ストリームを取得し、データを送信します。
  10. 8. と 9. の送受信処理を繰り返します。
  11. 最後に、SFXSSLSocket::Close 関数を呼び出して、SSL ソケットをクローズします。

例 16.11. SSL ソケット通信

// 太字が TCP と異なる所
// コールバック関数で使うので、SFXSSLSocket クラスのインスタンス _socket はクラスのメンバ変数として定義する
class MyClass {
private:
    SFXSSLSocket _socket;
    SFXAnsiStringStreamWriter _writer; // データ送信用ストリーム
    SFXAnsiStringStreamReader _reader; // データ受信用トリーム

public:
    Void Start(Void);

    // コールバック関数
    CALLBACK_DECLARE_SFXSSLSOCKET(OnConnect)
    CALLBACK_DECLARE_SFXSSLSOCKET(OnNegotiate)
    CALLBACK_DECLARE_SFXANSISTRINGSTREAMWRITER(OnFlush)
    CALLBACK_DECLARE_SFXANSISTRINGSTREAMREADER(OnFetch)
};

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

    // TCP サーバーとの接続を初期化する
    if ((error = _socket.Open()) == SFERR_NO_ERROR) {
 
        // ホスト名は自動的に解決される
        // TCP サーバーに接続する( 接続の完了は、OnConnect 関数に通知される )
        error = _socket.Connect(host, CALLBACK_FUNCTION(OnConnect));
    }
    if (error != SFERR_NO_ERROR) { 
        // エラーが発生したとき
        _socket.Close();
    }
    return;
}

// 接続の完了が通知されるコールバック関数
CALLBACK_IMPLEMENT_SFXSSLSOCKET(MyClass, OnConnect, error)
{
    if (error == SFERR_NO_ERROR) { 
        // エラーが発生していないとき
        // ネゴシエートする( サーバーとのネゴシエートに関する通知を受け取ると、OnNegotiate 関数が呼び出される )
        error = _socket.Negotiate(CALLBACK_FUNCTION(OnNegotiate));
    }
    if (error != SFERR_NO_ERROR) { 
        // エラーが発生したとき
        _socket.Close();
    }
    return;
}

// サーバーとのネゴシエートに関する通知を受け取ると呼び出されるコールバック関数
CALLBACK_IMPLEMENT_SFXSSLSOCKET(MyClass, OnNegotiate, error)
{
    static AChar sendingMessage[] = "GET / HTTP/1.0\r\n\r\n";

    if (error == SFERR_NO_ERROR) {

        // データ送信用ストリームを取得する ( バッファ サイズは 1024 )
        if ((error = _socket.GetStreamWriter(1024, &_writer))
            == SFERR_NO_ERROR) {

            // データを書き込む
            if ((error = _writer.Write(sendingMessage,
                   lengthof(sendingMessage))) == SFERR_NO_ERROR) {

                // 実際にデータを送信する ( データ送信の完了は、OnFlush 関数に通知される )
                error = _writer.Flush(CALLBACK_FUNCTION(OnFlush));
            }
            if (error != SFERR_NO_ERROR) { 
                // エラーが発生したとき
                 _writer.Release();
            }
        }
    }
    if (error != SFERR_NO_ERROR) { 
       // エラーが発生したとき
       _socket.Close();
    }
    return;
}

// データ送信の完了が通知されるコールバック関数
CALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMWRITER(MyClass, OnFlush, error)
{
    // データ送信を終えると、データ送信用ストリームを解放する
    _writer.Release();

    if (error == SFERR_NO_ERROR) {

        // データ受信用ストリームを取得する ( バッファ サイズは 1024 )
        if ((error = _socket.GetStreamReader(1024, &_reader))
            == SFERR_NO_ERROR) {

            // データを受信する ( データ受信の完了は、OnFetch 関数に通知される )
            if ((error = _reader.Fetch(CALLBACK_FUNCTION(OnFetch)))
                != SFERR_NO_ERROR) {
                // エラーが発生したとき
               _reader.Release(); 
            }
        }
    }
    if (error != SFERR_NO_ERROR) { 
        // エラーが発生したとき
        _socket.Close();
    }
    return;
}

// データ受信の完了が通知されるコールバック関数
CALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMREADER(MyClass, OnFetch, error)
{
    SFXAnsiString receivedString;

    if (error == SFERR_NO_ERROR) {

        // ストリームからデータを読み込む
        _reader >> receivedString;

        // 読み込んだデータを表示する
        TRACE("%s", receivedString.GetCString());
    }
    // データ受信を終えると、データ受信用ストリームを解放する
    _reader.Release();

    // ソケットをクローズする
    _socket.Close();
    return;
}

16.3.3. UDP ソケット通信

UDP ソケット通信は、SFXUDPSocket クラスを使って以下の手順で行います。

  1. SFXUDPSocket クラスのインスタンスを作成します。
  2. SFXUDPSocket::Open 関数を呼び出して、 UDP ソケットをオープンします。
  3. SFXUDPSocket::Bind 関数を呼び出して、ローカル の IP アドレスとポート番号を UDP ソケットにバインドします。 SFXUDPSocket::Bind 関数の戻り値が AEE_NET_WOULDBLOCK でブロックされたときは、 SFXUDPSocket::ScheduleBind 関数でコールバック関数を登録し、コールバック関数の中でバインドのために再度 SFXUDPSocket::Bind 関数を呼び出します。
  4. SFXUDPSocket::Send 関数を呼び出して、非同期的にデータを送信します。このとき、送信先の IP アドレスとポート番号も SFXUDPSocket::Send 関数で設定します。
  5. SFXUDPSocket::Receive 関数を呼び出して、非同期的にデータを受信します。送信元の IP アドレスとポート番号も SFXUDPSocket::Receive 関数で受信できます。
  6. 4. と 5. の送受信処理を繰り返します。
  7. 最後に、SFXUDPSocket::Close 関数を呼び出して、UDP ソケットをクローズします。

例 16.12. UDP ソケット通信

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

public:
    Void Start(Void);

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

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

    // UDP ソケットをオープンする
    if ((error = _socket.Open()) == SFERR_NO_ERROR) {

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

// バインドの完了が通知されるコールバック関数
CALLBACK_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:

                // バインドの完了が通知されるコールバック関数を登録する
                _socket.ScheduleBind(CALLBACK_FUNCTION(OnBind));
                break;
        }
    }
    return;
}

// データ送信の完了が通知されるコールバック関数
CALLBACK_IMPLEMENT_SFXUDPSOCKET(MyClass, OnSend, error)
{
    static ACharConst data[] = "udp!";
    SFXSocketAddress address(SFXInetAddress::LoopbackInetAddress(), 1024);
    UInt32 size;

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

        // データを非同期的に送信する
        error = _socket.Send(address, data, &size);

        switch (error) {
            case SFERR_NO_ERROR:

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

                    // データを非同期に読み込む
                    OnReceive(SFERR_NO_ERROR);
                }
                else {
                    TRACE("...send failed...");
                }
                break;
            case AEE_NET_WOULDBLOCK:

                // データ送信の完了が通知されるコールバック関数を登録する
                _socket.ScheduleSend(CALLBACK_FUNCTION(OnSend));
                break;
        }
    }
    return;
}

// データ受信の完了が通知されるコールバック関数
CALLBACK_IMPLEMENT_SFXUDPSOCKET(MyClass, OnReceive, error)
{
    SFXSocketAddress socket;
    SFXBuffer buffer;
    UInt32 size;

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

        size = static_cast<UInt16>(buffer.GetSize());

        // 非同期的にデータを受信する
        switch (_socket.Receive(&socket, buffer.GetBuffer(), &size)) {
            case SFERR_NO_ERROR:

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

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

                    // ソケットをクローズする
                    _socket.Close();
                }
                else {
                    TRACE("...receive failed...");
                }
                break;
            case AEE_NET_WOULDBLOCK:

                // データデータ受信の完了が通知されるコールバック関数をスケジュールする
                _socket.ScheduleReceive(CALLBACK_FUNCTION(OnReceive));
                break;
        }
    }
    return;
}