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のログです。

