HTTP ネットワークプログラミング - 1 / 3 -
IWeb
IWeb は HTTP プロトコルを処理する BREW インターフェースです。URL などの HTTP 通信パラメーターを設定し、HTTP 通信をするコールバック関数を登録して、応答を待ちます。
アプリの概要
- 最初に、"Push Select Key..."と表示します。
- セレクトキーが押されると、通信が開始します。HTTP 通信が終了すると、受け取った HTML データを表示します。
MIF ファイルの設定
MIF ファイルには「ネットワーク」と「 Web アクセス」の特権レベルを設定します。
関連情報:" 第 11 回 TCP / IP ネットワークプログラミング − MIF ファイルの設定 − "
準備
IWeb インターフェースを利用する際は、パラメーターが多いので構造体を利用して簡潔にコードを記述します。
■ アプレット構造体の宣言
typedef struct WebApp WebApp; struct WebApp { AEEApplet a; // アプレット構造体の先頭メンバは必ず AEEApplet 型にすること。 Common common; // 画面描画用 IWeb* web; // IWeb インターフェースのインスタンス変数 WebAction webaction; // コールバック関数 WebAction_GotResp の引数 };
■ 通信用構造体の宣言
typedef struct WebAction { WebApp* parent; AEECallback callback; IWebResp* webresp; // IWebResp インターフェースのインスタンス変数 IGetLine* getline; // IGetLine インターフェースのインスタンス変数 } WebAction;
※ その他 : " AEEWeb.h " ヘッダーファイルのインクルードが必要です。
IWeb インターフェースの初期化
ISHELL_CreateInstance 関数を使って IWeb インターフェースのインスタンスを生成します。
次に、IWEB_AddOpt 関数を使って、WebOpt 構造体の配列 webopts に HTTP 通信に必要なパラメータを設定します。( 参考 : IWeb インターフェースのオプション )
WebOpt 構造体はキーとその値のペアのリストで構成されます。以下のコードでは、タイムアウトのキー WEBOPT_CONNECTTIMEOUT を 10000 ミリ秒に設定しています。パラメーターの最後は WEBOPT_END で閉じます。
IWeb インターフェースを使って HTTP 通信した結果は、IWebResp インターフェースのインスタンス変数 webresp に格納されます。
// アプレットが開始したときに呼び出される。 static boolean OnAppStart(WebApp* app) { IShell* shell = app->a.m_pIShell; IDisplay* display = app->a.m_pIDisplay; WebOpt webopts[2]; app->web = NULL; ISHELL_CreateInstance(shell, AEECLSID_WEB, (void**)&app->web); // 接続のタイムアウト時間の設定 (ms) webopts[0].nId = WEBOPT_CONNECTTIMEOUT; webopts[0].pVal = (void*)10000; // WebOpt 終了マーク webopts[1].nId = WEBOPT_END; // オプション設定 IWEB_AddOpt(app->web, webopts); app->webaction.parent = app; //(略) return TRUE; }
HTTP 通信の開始
CALLBACK_Init マクロ関数を呼んで、コールバック関数 WebAction_GotResp とその引数を AEECallback 構造体 の変数 webaction->callback に設定します。
次に、IWEB_GetResponse マクロ関数を使って WebAction_GotResp 関数をコールバック関数として登録します。
※ IWEB_GetResponse マクロ関数の 2 つ目の引数はリスト構造になっています。マクロ関数なので、任意の数の引数が持てます。リスト構造の 2 つ目の引数のうち、url 以降が、IWeb インターフェースに渡すパラメータです。キーと値をセットにして、最後は WEBOPT_END とします。
// HTTP 通信開始 static void WebAction_Start(WebAction* webaction, char* url) { WebApp* app = webaction->parent; CALLBACK_Init(&webaction->callback, WebAction_GotResp, webaction); IWEB_GetResponse(app->web, (app->web, &webaction->webresp, &webaction->callback, url, WEBOPT_HANDLERDATA, webaction, WEBOPT_HEADER, "X-Method: GET\r\n", WEBOPT_HEADERHANDLER, WebAction_Header, WEBOPT_STATUSHANDLER, WebAction_Status, WEBOPT_END)); return; }
■ IWeb インターフェースのオプション
IWeb インターフェースのオプション | 意味 |
---|---|
WEOPT_ACTIVEXACTIONS | 同時 HTTP 通信の数 |
WEOPT_CONNECTTIMEOUT | ソケット接続のタイムアウト ( 単位 : ミリ秒 ) |
WEOPT_CONTENTLENGTH | HTTP リクエストとレスポンスの長さ |
WEOPT_HANDLERDATA | HTTP 通信のコールバック処理のためのデータ |
WEOPT_HEADER | HTTP リクエストのヘッダー ( 各フィールドは " CR " と " LF " で区切る ) |
WEOPT_HEADERHANDLER | HTTP 通信のヘッダー情報を取得する関数 |
WEOPT_IDLECONNTIMEOUT | 返答が無いときのタイムアウト ( 単位 : ミリ秒 ) |
WEOPT_METHOD | HTTP リクエストのタイプ ( デフォルトは " GET " ) |
WEOPT_PROXYSPEC | HTTP プロキシの指定 |
WEOPT_STATUSHANDLER | HTTP 通信の状態を取得する関数 |
WEOPT_USERAGENT | ユーザーエージェント |
関連情報:" 第 11 回 TCP / IP ネットワークプログラミング − AEECallback 構造体 − "
WEBOPT_HEADERHANDLER
WEBOPT_HEADERHANDLER に設定された WebAction_Header 関数は、WEBOPT_HANDLERDATA の値 webaction、HTTP ヘッダーにあるパラメーターの名前と値を引数として呼び出されます。
WebAction_Header 関数は、HTTP リクエストヘッダーの名前と値を表示します。
// ヘッダー処理 static void WebAction_Header(void* p, const char* name, GetLine* val) { WebAction* webaction = (WebAction*)p; WebApp* app = webaction->parent; if (name != NULL) { COMMON_WriteString(&app->common, name); COMMON_WriteString(&app->common, ":"); COMMON_WriteString(&app->common, val->psz); COMMON_WriteString(&app->common, "\n"); } else { COMMON_WriteString(&app->common, val->psz); COMMON_WriteString(&app->common, "\n"); } COMMON_Draw(&app->common); return; }
WEBOPT_STATUSHANDLER
WEBOPT_STATUSHANDLER に設定された WebAction_Status 関数は、WEBOPT_HANDLERDATA の値 webaction、HTTP 通信の状態を表す WebStatus、void* 型の値を引数として呼び出されます。
WebAction_Status 関数は、HTTP 通信の状態を表示します。
// 状態取得 static void WebAction_Status(void* p, WebStatus webstatus, void* val) { char* string = NULL; WebAction* webaction = (WebAction*)p; (void)val; switch (webstatus) { case WEBS_CANCELLED: string = "** cancelled...\n"; break; case WEBS_GETHOSTBYNAME: string = "** finding host...\n"; break; case WEBS_CONNECT: string = "** connecting...\n"; break; case WEBS_SENDREQUEST: string = "** sending...\n"; break; case WEBS_READRESPONSE: string = "** receiving...\n"; break; case WEBS_GOTREDIRECT: string = "** redirect...\n"; break; case WEBS_CACHEHIT: string = "** cache hit...\n"; break; } if (string != NULL) { COMMON_WriteString(&webaction->parent->common, string); COMMON_Draw(&webaction->parent->common); } return; }
■ WebStatus の種類とその内容
ステータス コード | 意味 |
---|---|
WEBS_STARTING | 接続が開始している |
WEBS_CANCELLED | 接続がキャンセルされた |
WEBS_GETHOSTBYNAME | リモートホストの IP アドレスが取得している |
WEBS_CONNECT | IWeb インターフェースが接続した |
WEBS_SENDREQUEST | IWeb インターフェースがリクエストを送信中である |
WEBS_READRESPONSE | IWeb インターフェースがレスポンスを受信中である |
WEBS_GOTREDIRECT | IWeb インターフェースが別の URL へのリダイレクトを受信した |
WEBS_CACHEHIT | IWeb インターフェースがキャッシュヒットした |
メッセージの受信
次はIWeb インターフェースによる HTTP 通信が終了した時に呼び出される、コールバック関数 WebAction_GotResp です。
IWEBRESP_GetInfo 関数を呼び出した時に設定される WebRespInfo 構造体のデータは次のような情報で構成されています。
typedef struct { int nCode; // HTTP 通信の戻り値 ISource* pisMessage; // 応答メッセージの内容 long lContentLenght; // 応答メッセージの長さ const char* cpszContentType; // content-type の内容 const char* cpszCharset; // charset の内容 int32 tExpires; // 有効期限 int32 tModified; // 更新日時 } WebRespInfo;
応答メッセージを取得するために、 WebRespInfo 構造体の持つ ISource インターフェース のインスタンス pisMessage を ISourceUtil インターフェースで IGetLine インターフェースのインスタンスに変換します。
// メッセージの取得開始 static void WebAction_GotResp(void* p) { WebAction* webaction = (WebAction*)p; WebApp* app = webaction->parent; WebRespInfo* info; char buffer[80]; info = IWEBRESP_GetInfo(webaction->webresp); SPRINTF(buffer, "** got response...\n** info code: %d\n", info->nCode); COMMON_WriteString(&app->common, buffer); COMMON_Draw(&app->common); if (info->pisMessage != NULL) { ISourceUtil* util = NULL; ISHELL_CreateInstance(app->a.m_pIShell, AEECLSID_SOURCEUTIL, (void**)&util); ISOURCEUTIL_GetLineFromSource(util, info->pisMessage, 1100, &webaction->getline); ISOURCEUTIL_Release(util); } if (webaction->getline != NULL) { WebAction_ReadLines(webaction); } else { COMMON_WriteString(&app->common, "** no response\n"); COMMON_Draw(&app->common); WebAction_Stop(webaction); } return; }
IGetLine インターフェースからの読み込み
IGetLine インターフェースはデータの構文解析を行うクラスで、読み込みの際に、ブロッキングが発生することもあります。
「今は読み込めないからまた後で」というような状況もありえます。
従って、WebAction_ReadLines 関数では自分自身をコールバック関数として登録します。
// メッセージ取得 static void WebAction_ReadLines(void* p) { int value; GetLine getline; WebAction* webaction = (WebAction*)p; WebApp* app = webaction->parent; // 次の LF までを一行として、getline 構造体に読み込む。 value = IGETLINE_GetLine(webaction->getline, &getline, IGETLINE_LF); if (value == IGETLINE_WAIT) { // ブロックされた場合は、コールバック関数として自分自身を再度登録する。 CALLBACK_Init(&webaction->callback, WebAction_ReadLines, webaction); IGETLINE_Peekable(webaction->getline, &webaction->callback); } else { COMMON_WriteString(&app->common, getline.psz); COMMON_WriteString(&app->common, "\n"); if (!IGETLINE_Exhausted(value)) { // 読み込みが完了していない場合も、コールバック登録 CALLBACK_Init(&webaction->callback, WebAction_ReadLines, webaction); IGETLINE_Readable(webaction->getline, &webaction->callback); } else { // 読み込み完了 COMMON_WriteString(&app->common, (value == IGETLINE_ERROR) ? ("\n** error\n") : ("\n** end\n")); COMMON_Draw(&app->common); WebAction_Stop(webaction); } } return; }
最後に、コールバック関数 WebAction_ReadLines、WebAction_GotResp をキャンセルし、IWebResp インターフェースと IGetLine インターフェースを解放します。
// 通信終了 static void WebAction_Stop(WebAction* webaction) { CALLBACK_Cancel(&webaction->callback); // 各種解放処理 if (webaction->getline != NULL) { IGETLINE_Release(webaction->getline); webaction->getline = NULL; } if (webaction->webresp != NULL) { IWEBRESP_Release(webaction->webresp); webaction->webresp = NULL; } return; }
※ WebAction_Stop 関数は、HTTP 通信の処理途中でエラーが発生してもサスペンドしても強制終了されても、この関数が呼ばれれば、コールバックはキャンセルされ、インターフェースも解放されるように設計されています。
関連情報:"第 17 回 サスペンドとレジューム"