Webサーバーを使った分散アプリケーションの作り方

戻る

最近,LANを構築するための装置も大変安価となり家庭でも複数のパソコンを 持っている方は気軽にLANを構築することができるようになっています.当然仕事で パソコンを利用するときは,ネットワークに接続されていないパソコンは使い物に ならない状況になっているのではないでしょうか?
Windows95やWindowsNTでは,標準で「パーソナルWebサーバ」などのように標準で Webサーバが提供されるようになりました.Webサーバは,HTML (HyperText Makeup Language)ファイルをInternet ExploerなどのWebブラウザで 表示させるサーバとして利用しますが,単にテキストファイルを転送する機能として でなくWebサーバのCGIで,なにか処理を行わせて結果を受信することも可能です. 最近は,ASP(Active Server Page)でデータベースへのアクセスを行って, 結果をWebブラウザで参照するといったことがよく行われます.しかし,クライアントに Webブラウザを利用してサーバ側で処理をする以外に,ある処理を行う専用のCGIを 作成し,独自のクライアントアプリケーションを作成して,利用者にWebサーバの存在 を意識させずに処理を行わせることも可能です.そうすることによりアプリケーション の処理を分散化することが可能になります.サーバで動作するCGIを作成する方法に ついては多くの情報がありますが,Webブラウザの代わりをするクライアント アプリケーションを作成するための情報はあまりないようなので,クライアントでの 処理について考えてみることにしてみましょう.

4-1 Webサーバについて

一般的なWebサーバの役目は,Intrnet Explorer や Netscape Navigater のような Webブラウザに表示させる情報をHTTP(HyperText Transfer Protcol)と呼ばれる プロトコルで通信を行って受信します.このプロトコルは,TCP/IP上で動作するため, サーバ・クライアント共にソケットAPIを利用してプログラミングが行われます.
Webサーバでは,単純にテキストファイルやビットマップファイルを送信するだけでなく CGI(Common Gateway Interface)と呼ばれるアプリケーションを実行することにより, Webクライアント上に表示された入力項目を受信して処理を行うことも可能です.

●WindowsのWebサーバ
Windows95やWindowsNTで動作するMicrosoft製のWebサーバでは,Microsoftと Process Softwareによって開発されたISAPI を利用してCGIと同等な機能をDLLで 実現することができます.DLLで作成することによって,EXEファイルを実行するCGIと 比較するとシステムリソースの消費量や速度的には有利になります.しかし,DLLに した場合,DLLのクラッシュがWebサーバ全体に影響を与える恐れがあります.

●サーバマシンの選択を行うには
 インターネット上でアクセス量の大きいWebサイトを構築する場合は,まだまだUNIX の方が有利のようです.よく利用されるWebサーバはやはりNetscape社製のものでは ないかと思われます.WindowsNTで動作するIIS(Internet Infomation Server)では, 新しい機能を多く持っているというメリットがある反面,長時間システムの再起動を せずに運用を行うと原因不明のクラッシュなどが発生することがあるといったことも あるようです.このため,信頼性を重視する場合はUNIXのWebサーバを利用することが 多いようです.しかし,定期的(* 運用状態などによって変化する.)に OSの再起動を行える場合やイントラネットで利用する場合は,高機能なIISを利用する メリットが大きい場合もあります.

4-2 HTTP:HyperText Transfer Protocol

HTTPの説明に入るまえに,URLについて簡単な説明をしておきます.

●URLとは
URL(Unifom Resource Locations)は,以下の情報を1行で記述したサーバに存在する 情報の住所のようなもので,次の情報を持ちます.

スキーム: //ホスト :ポート /パス ;パラメーター ?クエリー

・スキーム(サーバーにアクセスするためのプロトコル)
http: ... HTTP(ポート番号80)
https: ... HTTPをSSL(Secure Sockets Layer)で暗号化(ポート番号443)
ftp: ... FTP(ポート番号20,21)
など
・ホスト(サーバのIPアドレス)とポート番号
xxx.xxx.xxx.xxx のようにIPアドレス(ポート番号)
www.xxx.co.jp のようDNS(Domain Name Server)に設定されたマシン名
www.xxx.co.jp:8080 ののようにアクセスするポート番号を指定することもある(8080番にアクセスする場合)
・パス(サーバに存在するファイルの位置)
http://server_name/~user/data/
これは,サーバのuserユーザーディレクトリ配下のdataディレクトリにある規定値 ファイルへのアクセス.ファイル名を指定しなければ index.html,index.htm, default.htm が利用される場合が多い
・パラメーター(オブジェクトに必要なパラメーター)
・クエリー(CGI用のクエリー文字列)

●困ったURL テレビなどでURLを紹介しているのを見かけますが,
http://server_name/dir_name
のようにディレクトリ名の後ろに"/"がない場合があります. このようなアクセスを行うと,Webサーバはdir_nameがファイルであると認識して 検索を行います.しかし,検索してみるとディレクトリであることが分かり,
クライアントに
http://server_name/dir_name/index.html
(* index.htmlはサーバーの設定によって異なる.)
が実際のファイルを指すURLであること返信します.
クライアントは,
http://server_name/dir_name/index.html
で再度アクセスを行い,ファイルを受信します.
 つまり,1度のアクセスで済むところを2度のアクセスを行ってしまい,テレビの ようなメディアで紹介されたURLは多くの人がアクセスするために,そのサーバへの アクセスを行った人もアクセスに時間がかかるでしょうし,他の用途でインターネット を利用している人にもネットワークの負荷の増大によって迷惑がかかることが あります.

●Webサーバとのやり取りを確認するには
WebサーバはHTTPでクライアントとの通信を行います.UNIXの telnet コマンドでは, コンソールから手動でプロトコルをキーボードから入力して結果を得ることが できます.しかし残念ながら,Windows95やNTに標準で付属しているTELNET.EXEでは Webサーバに接続することができません.
<telnetコマンドでWebサーバにアクセスしてみる>
>telnet server_name 80 GET /data/ HTTP/1.0\n ← http://server_name/data/ と同等 (情報へのパスはサーバルートからのパス) \n
telnetコマンドでアクセスしてみると,HTTPも TELNETやSMTPなどと同様に文字列で 通信を行っていることが分かります.ソケットAPIを利用した経験のある方なら, send, recv などの関数を利用して実現することができます.
UNIXでは,ソケットAPIを使うしかありませんが,Windows95やWindowsNTでは, インターネットで利用する機能をAPIにしていることが多く,HTTP, FTP, Gopher クライアントを作成するためのInetAPI が用意されています.このAPIを利用すること により,複雑なプロトコルをあまり意識することなく,ファイル感覚でインターネット リソースにアクセスすることが可能になります.ここでは,InetAPIを利用することを 前提にしますので,HTTPプロトコルのうちクライアントに関連するものにとどめたい と思います.

●代理サーバー
企業内でインターネットに接続している方は,Webブラウザの設定に「プロキシサーバ」 の設定を行った経験があるのではないでしょうか.
プロキシサーバーとは,LANからインターネット上のサーバーへのアクセスを代行する ためのサーバーです.プロキシサーバーを利用することにより,インターネットへの アクセスに制限を設けることができます.また逆にインターネットからLAN内に進入 できないようにするために利用します.
代理サーバー経由でWebサーバにアクセスするには,代理サーバーに次のような形式で, フルURI(Unifom Resource Identifier)を指定してメソッドを実行します.
GET http://server_name/ HTTP/1.0

●HTTPのメソッド
HTTPでは,いくつかの「動詞」を利用してサーバへのリクエストを行います.
HTMLで<FORM>タグの記述のMETHODは,そのリクエスト方法を設定しており, たとえばリスト4-1のような記述をすると,Webサーバへのリクエストはリスト4-2の ように行われます。

リスト4-1:

<FORM NAME="main" METHOD="POST" ACTION="http://192.168.0.1/connect0.cgi"> <INPUT TYPE="hidden" NAME="remote 0 function" VALUE="connect"> <INPUT TYPE="submit" VALUE="connect"> </FORM>


リスト4-2:

POST /connect0.cgi HTTP/1.0 Accept: */* Context-Type: apprication/x-www-form-urlencoded Contents-Length: 40 remote 0 function=connect&submit=connect


表4-1:HTTP1.0のメソッド
GETドキュメントの取得
POSTサーバーにデータを送信
HEADヘッダー情報を取得
PUTエンティティボディーの内容を指定のURLに格納
LINKURIで同定される既存のリソースと他の既存リソースの間に1つ以上のLink関係を確立
UNLINKURIで同定される既存のリソースとの1つ以上のリンク関係を取り除く
DELETEURLを削除
a)HTTP1.0のメソッド
OPTIONS指定のURL対して許されるオプションの情報を取得
TRACEクライアントリクエストの伝送経路を追跡
b)HTTP1.1で追加されたメソッド

●ヘッダー情報
ヘッダー情報には,Webサーバーからの受信したコンテンツ(内容)を処理するために 必要な情報や,Webクライアントからサーバーに送信する情報を設定します.
ヘッダーには,ステータスコードやアクセスの方法によって必要なものと必要でない ものがあります.また,サーバーやクライアントによってサポートしているものと そうでないものがありますので,よく調査して利用しなければなりません.

例4-1:Windows95 のパーソナルWebサーバーから受信したヘッダー
HTTP/1.0 200 OK
Server: Microsoft-PWS-95/2.0
Date: Sat, 06 Jun 1998 13:07:39 GMT
Content-Type: text/html
Accept-Ranges: bytes
Last-Modified: Wed, 24 Dec 1997 12:43:08 GMT
Content-Length: 3016

●ステータスコード
ステータスコードは,Webサーバーにアクセスを行ったときの実行結果として 返却されます.通常,Webブラウザで見ることができるエラーコードは,HTMLで記述 された部分ですが,実際にはその前にステータス情報が設定されています.
クライアントアプリケーションは,ヘッダに設定された "HTTP/1.0"以降のステータス 番号と文字列を利用します.例えば,ステータスコードが200の場合は要求が正常に 受け入れられたことを意味します.
HTTPの説明をするとそれだけで本が1冊書けてしまうほどの量になってしまい, 本章ではとても全体を説明することができません.実際に,Webクライアント アプリケーションを作成するときには,参考文献を参照されることをお勧めします.

表4-2:ステータスコードの概要
コード概要
100〜199通知用
200〜299正常終了
300〜399リクエスト対象は移動した
400〜399クライアントのリクエスト処理エラー
500〜599サーバーエラー


例4-2:エラー発生時にサーバーから受信した情報
HTTP/1.0 404 オブジェクトが見つかりません.   ← ヘッダのステータス情報
Content-Type: text/html


<BODY>
<H1>
HTTP/1.0 404 オブジェクトが見つかりません.   ← Webブラウザに表示される情報
</H1>
</BODY>

4-3 WinInetAPIによるクライアントでの通信処理

ここからは,Windowsの世界でWebクライアントを作成する方法を説明します.

●インターネットAPI
インターネットAPIは,HTTP, FTP, Gopher クライアントを作成するためのAPIを 提供しています.APIの構成は,各プロトコル共通APIと,各プロトコル固有APIに 分かれており,共通APIは,関数名の頭に"Internet"が,各プロトコル固有APIには プロトコル名,たとえばHTTPなら"Http"が付加されています.
これらのAPIを利用することにより,通信手順をあまり意識せずに,プロトコル固有の 必要最小限の処理のみを記述すれば簡単にあとは,ファイル操作感覚でデータを 受信することが可能になります.ソケットAPIを利用してHTTPやFTPクライアントを 作成したことがある方なら,このAPIを利用すれば高機能なクライアント アプリケーションがたいへん簡単に作れるということをすぐに理解できるはずです. ここでは,Webサーバを利用することを前提にしていますのでHTTPの通信に特化 した説明をします.

表4-3:HTTPを利用するためのWinInetAPI
InternetAttemptConnect ダイアルアップダイアログボックスを表示する
InternetCheckConnection インターネットへの接続することができるか調べる
InternetCloseHandle インターネットハンドルをクローズする
InternetConfirmZoneCrossing 保全性が高い・保全性がないURLの間に変更を調べる
InternetConnect 指定サイトをFTP, Gopher あるいは HTTP で開く
InternetErrorDlg ダイアログボックスが存在するならInternetErrorDlg にパスするエラーの ためにダイアログボックスを表示する
InternetFindNextFile FtpFindFirstFile あるいは GopherFindFirstFile への前の呼び出しで 開始したファイル捜索を続ける
InternetGetLastResponseInfo この機能を呼び出しているスレッドの上に最後の Win32 INet API エラー記述あるいはサーバー終了コード取得する
InternetLockRequestFile ユーザーに使われているファイルのロックを行う
InternetOpenWin32 INet API を初期化する
InternetQueryDataAvailable 利用可能なデータの量を尋ねます.
InternetQueryOption 指定されたハンドルのインターネットオプションを問い合わせる
InternetReadFile InternetOpenUrl,FtpOpenFile,GopherOpenFile あるいは HttpOpenRequest によって開けられてハンドルからデータを読む
InternetReadFileEx InternetOpenUrl,FtpOpenFile,GopherOpenFile あるいは HttpOpenRequest によって開けられてハンドルからデータを読む
InternetSetFilePointer ファイルポジションをInternetReadFileのために設定
InternetSetOption インターネットオプションを設定する
InternetSetOptionEx インターネットオプションを設定する
InternetSetStatusCallback Win32 INet API でのオペレーションで呼び出されるコールバック関数を 設定する
InternetTimeFromSystemTime RFCフォーマットの時刻をSYSTEMTIMEの時刻に変換する
InternetTimeToSystemTime SYSTEMTIMEの時刻をRFCフォーマットの時刻に変換する
InternetUnlockRequestFile InternetLockRequestFile を使ってロックされていたファイルを アンロックする
InternetWriteFile 開いているインターネットファイルにデータを書込む
(a)Internet xxx API

IHttpAddRequestHeaders HTTPリクエストハンドルに1つ以上の HTTPリクエストヘッダーを追加する
IHttpEndRequest HTTPリクエストを終了する
IHttpOpenRequest HTTPリクエストハンドルを開く
IHttpQueryInfo HTTPのリクエストについての情報を問い合わせる
IHttpSendRequest HTTPサーバーに指定されたリクエストを送る
IHttpSendRequestEx HTTPサーバーに指定されたリクエストを送る
(b)Http xxx API

●インターネットAPIを利用できる環境
インターネットAPIは,通常Internet Explorer3.xx以降をインストールすると, 利用できるようになります.しかし,すべての人がそのような制限を受け入れるとは 限りません.もし,Internet Explorer をインストールせずにインターネットAPIを 利用しなければならない場合は,VisualC++のマスターディスクの,
\DevStudio\VC\Redist\REDISTRB.WRI
\DevStudio\VC\Redist\AXRedist\redist.txt
を参照してください.
(* これらのファイルはVisualStudio97, VisualC++ver5.0の 場合のみで,VisualStudio6.0には該当するファイルが存在しない.)
これらのファイルに,NT3.51やInternetExplorer3.xx以降をインストールしていない Windows95/NT4.xxでインターネットAPIを利用するためのパッケージについての 説明がありますので,参考にするとよいでしょう.

●インターネットリソースをオープンする
HTTPでサーバーにアクセスするには,必ず次の順番に処理を行う必要があります.
これらのAPIを実行すると,それぞれオープンハンドル・コネクトハンドル・ リクエストハンドルを取得することができます.HTTPの処理を行うための API(HttpXXXXX)を利用するには,リクエストハンドルが必要になります.
処理が終了すると,リクエストハンドル→コネクトハンドル→オープンハンドルの順 にInternetCloseHandle でクローズしなければなりません.

図4-1 HTTPでサーバにアクセスするときの処理手順

InternetOpen ↓ InternetConnect ↓ HttpOpenRequest ↓ HttpQueryInfo HttpSendRequest : :

●GETメソッドを送信する
 GETメソッドは,Webサーバーからデータを取得するときに利用します. WebサーバーからHTMLのテキストデータや画像データを取得する場合,
http://server_name/path/index.html
のようにファイルへのパスを指定し,特定をファイルを取得します.
また,
http://server_name/path/cgi-bin/program.cgi?data=1
のように,CGIを実行してパラメータdataを渡すこともできます.
クライアントからCGIを実行しパラメーターを渡す場合とファイルを取得する場合 に違いはなく,サーバーへのリクエストは文字列で行います.
GET /path/cgi-bin/program.cgi?data=1 HTTP/1.0 .... CGIを実行する
WinInetAPIでは,InternetConnect で,サーバーを指定して HttpOpenRequest に,リクエストを行うパスを,"/path/cgi-bin/program.cgi?data=1"のような形式 でアクセスを行うパスを設定します.

●POSTメソッドを送信する
POSTメソッドは,HTMLで入力フィールドを記述したとき,Webブラウザで設定された値を サーバーに通知するために利用します.
WinInet API でそれを実現するには,HttpSendRequest でヘッダーに,
Content-Type: application/x-www-form-urlencoded
を設定しフォームデータを設定します.
たとえば,
<FORM NAME="main" METHOD="POST" ACTION="http://server/program.cgi">
<INPUT TYPE="hidden" NAME="func1" VALUE="aaa bbb">
<INPUT TYPE="hidden" NAME="func2" VALUE="aaa&bbb">
</FORM>
と同じ処理を行う場合,フォームデータは,"func1=aaa+bbb&func2=aaa%26bbb"と なります.ここで,空白(0x20)は,"+", "&"は "%26"のように16進で指定します.

リスト4-3:POSTメソッドを送信する

                                :
                                :

    lstrcpy(szHeader, "Content-Type: application/x-www-form-urlencoded");
    lstrcpy(szOption, "func1=aaa+bbb&func2=aaa%26bbb");
    fSuccess = HttpSendRequest(hRequest,
            szHeader, lstrlen(szHeader),
            szOptional, lstrlen(szOptional));

                                :
                                :

●Cookieを使う
WinInet API でアクセスしたサーバーから"Set-Cookie:"ヘッダーを受信すると, 自動的に"C:\windows\Temporary Internet Files"へ Cookie を保存し,次回サーバー にアクセスするときに自動的にクライアントが送信するリクエストヘッダーに "Cookie:"を追加して送信します.このため,Cookie を利用するための特別な処理は 必要ありません.
プログラム上で,"Set-Cookie"ヘッダーを取得することができるのは次のような状態に なっているときです.

・HttpOpenRequest のフラグにINTERNET_FLAG_NO_COOKIES を指定した場合  → INTERNET_FLAG_NO_COOKIESを指定した場合,WinInet API は Cookie をファイル に保存しなくなり,HttpQueryInfoで取得したヘッダー情報から "Set-Cookie"ヘッダー を取得することができますが,クライアントは Cookieの値をメモリやディスクに 保存しておき,次回のアクセス時に必要に応じて HttpSendRequest で"Cookie:" ヘッダーを送信する必要があります.

・サーバーが送信する"Set-Cookie"ヘッダーに"expires"がないなど,Cookieをディスクに保存する必要がない場合
 → この場合でも自動的にCookieはメモリ中に保存され,アプリケーションが終了 するまで保存されるため,Cookieの処理について意識する必要はありません.
Internet Explorer を利用する場合,Cookie を受信しようとしていることを知らせる ダイアログボックスが表示されます.このダイアログボックスは,WinInet API で Cookie を受信するときにも表示されます.利用者が明示的にサーバーへのアクセスを 行うときには表示されても問題はありませんが,バックグランドで処理を行うなど, ダイアログボックスを表示させたくないときには,INTERNET_FLAG_NO_UI を指定 します.

例4-3 Cookieの書式


Set-Cookie: NAME=値; expires=値; domain=値; path=値; secure
(a)サーバーからクライアントへ送信

Cookie: NAME=値;
(b)クライアントからサーバーへ送信


リスト4-4:Cookieを送信する

                            :
                            :

strcpy(szCookie, "Cookie: data=1;");
fSuccess = HttpSendRequest(hRequest, szCookie, lstrlen(szCookie), NULL, 0);

                            :
                            :


●SSLで暗号化されたサイトにアクセスする
 WebブラウザでSSLリクエストを行うには,URLに "https://"を指定することで 行われます.例えば,Netscape Navigater でのSSL(Secure Socket Layer)の処理は, 米国RSA Data Security社からライセンスを受けるて作成してあります.
WinSock API でWebクライアントを作成してSSLを組み込む場合,独自のアルゴリズムの 暗号化を開発することも可能ですが,自分で作ることは考えないほうがよいでしょう. しかし,WinInet API を利用して "https://"をサポートする場合,非常に簡単です. InternetConnect のポート番号に,INTERNET_DEFAULT_HTTPS_PORT を設定し, HttpOpenRequest のフラグにINTERNET_FLAG_SECURE を指定するだけで実現できます.

リスト4-5:https で GETメソッドを発行する

                                :
                                :

    hConnect = InternetConnect(hOpen, "MyApp",
        INTERNET_DEFAULT_HTTPS_PORT, "", "", INTERNET_SERVICE_HTTP, 0, 0);

    hRequest = HttpOpenRequest(hConnet, "GET", "",
        HTTP_VERSION, "", NULL, INTERNET_FLAG_SECURE, 0);

                                :
                                :

●利用者認証をパスするには
InternetExpoler などでWebサーバーにアクセスすると,ユーザー名やパスワード の入力を促すダイアログボックスが表示されることがあります.
通常,このダイアログボックスが表示されるのは,Webサーバーからコード401を受信 したときです.このコードを受信すると,Webクライアントは,基本認証などの認証 ヘッダーをサーバーに返信します.また,WindowsNTで動作するIISやWerWebServer では,"Microsoft 暗号化認証"と呼ばれる独自の認証方式をサポートしています. WinInet API を利用することにより,これらの認証方式を意識することなく認証を 行うことができます.
ここで注意しなければならないのは,"Microsoft 暗号化認証"をサポートするとき には,HttpOpenRequest のフラグにINTERNET_FLAG_KEEP_CONNECTIONを設定しなければ ならないことです.このフラグがない場合は,基本認証しかパスできなくなります.

図4-2:認証処理
                HttpSendRequest でリクエストを発行
                               |
 +---------------------------→+
 |                             ↓
 |            HttpQueryInfo でステータスコードを取得
 |                             |
 |                             ↓
 |          ステータスコードに HTTP_STATUS_DENIED,
 |           HTTP_STATUS_PROXY_AUTH_REQ などが設定されている  →  終了
 |                             |Yes                          No
 |                             ↓
 |        ユーザー名とパスワードをInternetSetOption で設定
 |                             |
 +-----------------------------+

●キャッシュを制御する
WinInet API を利用することにより,ローカルディスク (C:\windows\Temporary Internet Files)に保存されるキャッシュファイルの制御や 「オフライン作業」を実現することができます.
実際には, InternetOpen や HttpOpenRequest のフラグに次のような設定を追加 することで動作を制御できます.


表4-4:キャッシュ関連フラグ
INTERNET_FLAG_ASYNC キャッシュに書きこむ
INTERNET_FLAG_FROM_CACHE キャッシュから読みこむ
INTERNET_FLAG_OFFLINE INTERNET_FLAG_FROM_CACHEと同様
(a)InternetOpen
INTERNET_FLAG_PRAGMA_NOCACHE プロキシサーバーのキャッシュを抑制する
INTERNET_FLAG_NO_CACHE_WRITE キャッシュに書きこまない
(b)HttpOpenRequest


4-4 プログラムについて

●ソースファイル
HDUMP.EXE(HTTPヘッダー表示ツール)
HDUMP.C(ソースファイル)
HDUMP.H(ヘッダーファイル)

指定されたURLのサーバーにアクセスして,受信したHTTPヘッダーと内容を表示 します.プロトコルは"http"と"https"に対応しています. プロキシサーバーを利用する環境で利用するときは,Internet Explorer のオプション で,プロキシサーバーの設定をあらかじめ行っておく必要があります.  認証エラーが発生したときは,ユーザー名・パスワードの入力を求められます.入力 をキャンセルするには,ユーザー名・パスワードに改行のみを入力してください.

●実行例
コマンドラインで "HDUMP [-c] 参照するURL" とします.

>HDUMP http://www.cqpub.co.jp/ ... 指定されたURLのHTTPヘッダーを表示する
>HDUMP -c http://www.cqpub.co.jp/ ... 指定されたURLのコンテンツを表示する