TCP / IP ネットワークプログラミング - 1 / 3 -
お客様のご要望にお応えして、" BREW プログラミング入門 " の連載を再開いたします。
早速 BREW での TCP/IP ネットワークプログラミングから始めます。
BREW ソケットプログラミング
「ソケットプログラミングは、ホスト名とポート番号を指定して接続すれば、あとはデータを送受信するだけ」という認識の方には、少々複雑かもしれません。
何故なら、DNS サーバーへの問い合わせからデータの送受信に至るまで、ほとんどの処理を非同期関数 ( コールバック関数 ) として記述しないといけないからです。
シングルスレッドな環境であるために、BREW プログラミングではいろんな非同期処理 ( 以下の コールバック と同義 ) に出くわしますが、非同期処理の基本的な考え方はソケットプログラミングの場合と同じです。
アプリの概要
- 最初に、"Push Select Key ..." を表示します。
- セレクトキーが押されると、通信が開始します。通信が終了すると、受け取ったデータを表示します。
MIF ファイルの設定
MIF ファイルには「ネットワーク」の特権レベルを設定します。
※ 1. MIF ファイルの特権レベルの設定により、アプリからアクセスできる API を明示的に指定することでセキュリティレベルを制御します。適切な特権レベルが設定されていないと、当該インターフェースのインスタンス生成関数 ISHELL_CreateInstance() でエラーが発生します。 通常、アプリ実行に必要最低限の特権レベルを設定します。特権レベルを追加すると QUALCOMM やキャリアで実施される検証で合格に必要なテスト項目が増えます。
※ 2. MIF ファイルの特権レベルと BREW インターフェースの関係
特権レベル | BREW インターフェース |
---|---|
ファイル | IFileMgr / IFile / IDatabase / IDBMgr / IDBRecord / ISound / ISoundplayer によるローカルファイルへの読み書き |
ネットワーク | INetMgr / ISocket による TCP / IP ネットワークアクセス |
位置情報取得 | IPosDet による位置情報取得 |
TAPI | ITAPI による電話機能 ( Telephony Application Programming Interface ) へのアクセス |
Web アクセス | IWeb による HTTP プロトコルを使った Web アクセス |
共用ディレクトリへの 書き込みアクセス |
IFileMgr / IFile による共用ディレクトリへの書き込み |
ベル音ディレクトリへの 書き込みアクセス |
IFileMgr / IFile によるベル音ディレクトリへの書き込み |
アドレス帳へのアクセス | IAddrBook によるアドレス帳へのアクセス |
セクタ情報へのアクセス | IPositionDet による携帯電話のセクタ情報へのアクセス |
詳細設定 | [詳細設定]のボタンを押すと、[ダウンロード]または[すべて]が選択可能です。これらはキャリア・端末メーカー専用の特権レベルです。 |
関連情報:" 第 2 回 HelloWorld プログラムを作ろう! − MIF ファイルとは − "
ネットワークを使うための準備
ISocket は ソケットプログラミングのためのBREW インターフェースです。
INetMgrは ISocket インターフェースを管理するためのBREW インターフェースです。通常、ISHELL_CreateInstance 関数を使って BREW インターフェースのインスタンスを生成しますが、ISocket インターフェースのインスタンスは INetMgr インターフェースを使って生成します。また、DNS への問い合わせも INetMgr インターフェースを使います。
アプレット構造体には INetMgr / ISocket インターフェースのインスタンス変数、メッセージ送受信バッファ変数、送受信データ量の変数、接続先ホスト IP アドレス用変数、コールバック用変数を追加します。
■アプレット構造体の宣言
// アプレット構造体 typedef struct SocketApp { AEEApplet a; // アプレット構造体の先頭メンバは必ず AEEApplet 型にすること。 Common common; // 画面描画用 // 通信用 INetMgr* netmgr; // INetMgr インターフェースのインスタンス変数 ISocket* socket; // ISocket インターフェースのインスタンス変数 uint16 index; // 送受信データ量を格納 AEEDNSResult dnsresult; // 接続先ホストの IP アドレスを格納 AEECallback callback; // コールバック関数とその引数を格納 } SocketApp;
■アプレット開始時に呼び出される OnAppStart 関数
static boolean OnAppStart(SocketApp* app) { IShell* shell = app->a.m_pIShell; IDisplay* display = app->a.m_pIDisplay; app->socket = NULL; ISHELL_CreateInstance(shell, AEECLSID_NET, (void**)&app->netmgr); // (中略) return TRUE; }
■アプレット終了時に呼び出される OnAppStop 関数
static boolean OnAppStop(NetworkApplet* app) { // 各種解放処理 if (app->socket != NULL) { ISOCKET_Cancel(app->socket, NULL, NULL); ISOCKET_Release(app->socket); } INETMGR_Release(app->netmgr); COMMON_Free(&app->common); return TRUE; }
※ その他 : " AEENet.h " ヘッダーファイルのインクルードが必要です。
コールバック
DNS サーバーへの問い合わせは携帯電話側では DNS サーバーからの返答待ちの状態が暫く続く処理です。
このような場合、BREW ではその処理が終了したときに自動的に呼び出される関数を登録します。
コールバック関数登録後、アプリは速やかに現在のイベント処理を完了して BREW に制御を戻し、次のイベントを待ちます。
この仕組みが、所謂「コールバック」です。登録された関数はコールバック関数と呼ばれます。コールバック関数には返答が返ってきた後にする処理を記述します。
コールバック関数を登録する関数は目的によって異なります。
ソケット通信関連の例で言うと、DNS サーバーへの問い合わせはINETMGR_GetHostByName 関数、ホスト接続は ISOCKET_Connect 関数、データ送信は ISOCKET_Writeable 関数、データ受信は ISOCKET_Readable 関数を使います。
ISOCKET_Cancel 関数を呼ぶと、ソケット通信関連のコールバックはキャンセルされます。
※ 1. BREW はイベント駆動型アーキテクチャで、しかもシングルスレッド環境のシステムです。 イベント駆動型モデルでは、イベントが発生するとシステムからアプリにイベントが渡され、 アプリ側でのイベント処理が終了するとシステムに制御が戻ります。 シングルスレッド環境では、アプリ側のイベント処理で待ちなどが発生して時間が掛かる場合は処理を打ち切って次の処理を実行し、 速やかにシステムに制御を戻す方が効率的です。コールバックはこのために考えられた、 シングルスレッド環境のシステムに共通する BREW のメカニズムです。
※ 2. アプリ側のイベント処理が長時間に及ぶと、BREW によってアプリは強制終了させられます。 強制終了までの時間は携帯電話によって異なりますが、だいたい 1 秒程度です。 BREW では、アプリの個々のイベント処理は少なくとも 1 秒以内に終了するようにプログラム設計しなければなりません。
関連情報:" 第 5 回 イベント処理って何だ? − イベントハンドラでは長時間の処理をしてはいけない − "
関連情報:" 第 18 回 BREW でカメラ (前編) − カメラ イベントとコールバック関数 − "
AEECallback 構造体
AEECallback は、コールバック関数とその引数を一つにまとめた構造体で、次のように定義されます。
typedef struct _AEECallback { AEECallback* pNext; void* pmc; PFNCBCANCEL pfnCancel; void* pCancelData; PFNNOTIFY pfnNotify; void* pNotifyData; void* pReserved; } AEECallback;
AEECallback 構造体への設定はフィールドに直接アクセスするよりも、CALLBACK_Init マクロ関数を使うのが便利です。
CALLBACK_Init(&pApp->callback, TCPDNSConnect, pApp);
のように第 1 引数を AEECallback 構造体、第 2 引数をコールバック関数、第 3 引数をコールバック関数への引数として、CALLBACK_Init マクロ関数を呼ぶだけで、コールバック関数の登録に必要な情報が AEECallback 構造体に設定されます。