C#やJavaでは簡単にプログラムができますが、C言語ではソケットを作成するところから必要です。少しプログラムが長いですが、C言語によるURLのアクセスソフトを作ってみました。
URLが示す場所のファイルを取得するために必要なプロトコルがHTTPで、次のプログラムは、WebサーバのURLアドレス「http://192.168.0.50/adlogger/」に対して、HTTP要求メッセージ「HttpRequest」を送信するプログラムです。
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <winsock2.h> #include <ws2tcpip.h> #define BUF_LEN 1024 /* バッファのサイズ */ int main(int argc, char *argv[]){ /* IP アドレス、ポート番号、ソケット */ char destination[] = "192.168.0.50"; unsigned short port = 80; char httppath[] = "/adlogger/"; char httphost[] = "192.168.0.50"; int dstSocket; int result; //戻り値 /* sockaddr_in 構造体 */ struct sockaddr_in dstAddr; /* 各種パラメータ */ char toSendText[BUF_LEN]; char buf[BUF_LEN]; int read_size; /* Windows 独自の設定 */ WSADATA data; WSAStartup(MAKEWORD(2, 0), &data); /* sockaddr_in 構造体のセット */ memset(&dstAddr, 0, sizeof(dstAddr)); dstAddr.sin_port = htons(port); dstAddr.sin_family = AF_INET; dstAddr.sin_addr.s_addr = inet_addr(destination); /* ソケット生成 */ dstSocket = socket(AF_INET, SOCK_STREAM, 0); if (dstSocket < 0){ printf("%d\n", GetLastError()); printf("ソケット生成エラー\n"); } /* 接続 */ result = connect(dstSocket, (struct sockaddr *) &dstAddr, sizeof(dstAddr)); if (result < 0){ printf("%d\n", GetLastError()); printf("バインドエラー\n"); } /* HTTP プロトコル生成 & サーバに送信 */ sprintf(toSendText, "POST %s HTTP/1.1\r\n", httppath); send(dstSocket, toSendText, strlen(toSendText) , 0); sprintf(toSendText, "Host: %s:%d\r\n", httphost, port); send(dstSocket, toSendText, strlen(toSendText), 0); sprintf(toSendText, "Content-Length: 11\r\n", httphost, port); send(dstSocket, toSendText, strlen(toSendText), 0); sprintf(toSendText, "\r\n"); send(dstSocket, toSendText, strlen(toSendText), 0); // HTTP Body部の作成 memset(toSendText, 0, sizeof(toSendText)); for (int i = 0; i < 11; i++){ toSendText[i] = i+5 ; } send(dstSocket, toSendText, 11, 0); //Dump HTTP response while (1){ memset(buf, 0, sizeof(buf)); read_size = recv(dstSocket, buf, BUF_LEN, 0); if (read_size > 0){ printf("%s", buf); } else { break; } } /* Windows 独自の設定 */ closesocket(dstSocket); WSACleanup(); }
上記のプログラムは、最初にsocketを生成してWebサーバに接続し、テキスト形式でHTTPリクエストメッセージを作成してWebサーバに送信ます。
HTTPリクエストメッセージは、複数行から成り立つ一連のデータ列です。ここでいう1行とは、終端にCR(キャリッジリターン、16進の0x0d)とLF(ラインフィード、16進の0x0a)を持つデータの単位を示します。ほぼ、通常のテキスト・データの1行と等しくなります。メッセージ・ヘッダとメッセージボディ部に分かれ、両者は空行(単独のCR+LF)で分割されます。
- リクエストライン
-
リクエストラインは下記の書式で表します。
メソッド パス名 HTTP/バージョン
メソッドは、ホームページと情報の送信・取得を行う場合の、その「方法」の種類を示し、次のような種類があります。
メソッド メソッド 説明 GET 最も一般的に使用されるメソッドです。ブラウザはサーバに対してページの取得を要求します。 HEAD ヘッダのみの情報を要求します。サーバは GET メソッドと HEAD メソッドは必ずサポートしなければなりません。 POST CGI でよく使用されるメソッドです。method=”POST” を指定したフォームに入力したデータをサーバに転送する際に使用されます。 PUT ファイルをサーバにアップロードする際に用いられます。 DELETE 指定したリソースを削除することをサーバに要求します。
パス名は、通常、/aaa/bbb/ccc.html のような、スラッシュで始まるパス名や、http:// などで始まる URL が指定されます。バージョンは現在は 1.1 が主流です。 - ヘッダ
-
リクエスト・ラインに記載した情報以外にも,WebブラウザからWebサーバーに知らせたい情報があります。例えば,Webブラウザで利用可能な言語や認証情報などを知らせる場合です。このような情報は,リクエスト・ラインの次の行から「ヘッダー」として記載します。
リクエストに利用できるヘッダーには,リクエストにもレスポンスにも利用できる「汎用ヘッダー」と、WebブラウザからWebサーバーへのリクエストのみに利用可能な「リクエスト・ヘッダー」がある。
汎用ヘッダー ヘッダー 機能 Cache-Control キャッシュの制御 Connection 持続的な接続の制御 Date 現在の日時 Transfer-Encoding エンコード形式。「chunked」を指定するとチャンク形式でデータを転送する
リクエスト・ヘッダー ヘッダー 機能 Accept Webブラウザ画受信可能なデータ形式をMINEタイプで指定する Accept-Charset 優先する文字コード Host アクセスしているWebページのホスト名を指定する User-Agent Webブラウザの名称やバージョンを指定する If-Modified-Since Webブラウザがリクエストするページを最後にアクセスした日時を指定し、それ以降に更新されたページである場合、サーバに要求する。
サーバに置いた次のPHPソフトにより、受け取ったHttpRequestのHeaderの部分とbodyの部分(値:05 06 07 08 09 0a 0b 0c 0d 0e 0f)を今回作成したクライアントソフトに返す機能を持たせました。
<?php print_r(getallheaders() ); $post_body = bin2hex(file_get_contents('php://input')); echo $post_body; ?>
クライアントソフトを実行し、Webサーバにアクセスし、コマンドプロンプトに表示された実行した結果を次に示します。
HTTP/1.1 200 OK Date: Sat, 09 Aug 2014 Server: Apache/2.2.22 X-Powered-By: PHP/5.4. Vary: Accept-Encoding Content-Length: 89 Content-Type: text/htm Array ( [Host] => 192.168.0.50:80 [Content-Length] => 11 ) 05060708090a0b0c0d0e0f
WireSharkでHttpプロトコルを解析してみると次のようになっていました。HTTPリクエストのWiresharkのログです。フィルタの設定方法については「Wiresharkのフィルタ設定」を参照してください
HTTP応答のWiresharkのログです。