HTTPクライアントの作成(2)
ここでは、前述したインチキHTTPクライアントをもうちょっと改造してみます。 前述したインチキHTTPクライアントは何も考えずにサーバからの結果を表示していましたが、今回はHTTPリクエストが成功したのか失敗したのか、までの解析をしたいと思います。 ただし、かなり解析(パース)部分はインチキです。 真面目にHTTPクライアントを書きたい場合には、そのまま使わないことをお勧めいたします。
前回サンプルとの違い
今回は、前回と違って最初の一行を解析しています。 最初の1行とは'\n'があるまでです。
HTTPでは、リクエストに対しての結果が成功なのか失敗なのかは、最初の一行に書いてあります。 具体的には、例えば以下のように帰ってきます。
HTTP/1.0 200 OK
これは、HTTPバージョン1.0による回答で、結果は200(成功)で、「OK」というのは詳細な結果メッセージです。 最後の「OK」の部分はメッセージなので何が入っていても大丈夫です。 例えば、以下のようなメッセージでも大丈夫です。
HTTP/1.0 200 成功だよーーん。
HTTPでリクエストしたURLが無い場合には、以下のようになる場合があります。
HTTP/1.0 404 NOT FOUND
今回は真ん中のステータスコードが404であると書いてあります。 このステータスコードには色々なものがあります。
HTTPクライアントサンプル
以下が、改造版インチキHTTPクライアントです。 改造箇所の説明はソースコードにコメントとして書き込んでいます。
#include <stdio.h>
#include <winsock2.h>
int
main(int argc, char *argv[])
{
WSADATA wsaData;
struct sockaddr_in server;
SOCKET sock;
char buf[4096];
char *ptr;
char *deststr;
unsigned int **addrptr;
if (argc != 2) {
printf("Usage : %s dest\n", argv[0]);
return 1;
}
deststr = argv[1];
if (WSAStartup(MAKEWORD(2,0), &wsaData) != 0) {
printf("WSAStartup failed\n");
return 1;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
printf("socket : %d\n", WSAGetLastError());
return 1;
}
server.sin_family = AF_INET;
server.sin_port = htons(80); // HTTPのポートは80番です
server.sin_addr.S_un.S_addr = inet_addr(deststr);
if (server.sin_addr.S_un.S_addr == 0xffffffff) {
struct hostent *host;
host = gethostbyname(deststr);
if (host == NULL) {
if (WSAGetLastError() == WSAHOST_NOT_FOUND) {
printf("host not found : %s\n", deststr);
}
return 1;
}
addrptr = (unsigned int **)host->h_addr_list;
while (*addrptr != NULL) {
server.sin_addr.S_un.S_addr = *(*addrptr);
// connect()が成功したらloopを抜けます
if (connect(sock,
(struct sockaddr *)&server,
sizeof(server)) == 0) {
break;
}
addrptr++;
// connectが失敗したら次のアドレスで試します
}
// connectが全て失敗した場合
if (*addrptr == NULL) {
printf("connect : %d\n", WSAGetLastError());
return 1;
}
} else {
if (connect(sock,
(struct sockaddr *)&server,
sizeof(server)) != 0) {
printf("connect : %d\n", WSAGetLastError());
return 1;
}
}
// HTTPで「/」をリクエストする文字列を生成
memset(buf, 0, sizeof(buf));
_snprintf(buf, sizeof(buf), "GET / HTTP/1.0\r\n\r\n");
// HTTPリクエスト送信
int n = send(sock, buf, (int)strlen(buf), 0);
if (n < 0) {
printf("send : %d\n", WSAGetLastError());
return 1;
}
// サーバからのHTTPメッセージ受信
// まずは結果(status line)を取得
//
// 注意)説明のためかなり手抜きで書いています
// 本来ならばもっと真面目にパースすべきです
//
memset(buf, 0, sizeof(buf));
n = recv(sock, buf, sizeof(buf), 0);
if (n < 0) {
printf("recv : %d\n", WSAGetLastError());
return 1;
}
// 最初が "HTTP/" ではじまるかどうかを確認
if (strncmp("HTTP/", buf, 5) != 0) {
printf("status line error !\n");
fwrite(buf, n, 1, stdout);
return 1;
}
if (n < 5) {
// エラー処理
// 本当はもっと真面目に書くべき
return 1;
}
// HTTP versionを確認
ptr = buf + 5;
if (strncmp("1.0 ", ptr, 4) == 0) {
printf("is HTTP/1.0\n");
ptr += 4;
} else if (strncmp("1.1 ", ptr, 4) == 0) {
printf("is HTTP/1.1\n");
ptr += 4;
} else {
printf("unknown HTTP version\n");
return 1;
}
// 次の項目までスペースをスキップ
while (ptr < buf + n && *ptr == ' ') {
ptr++;
}
// HTTPの結果を解析。
// 本当はもっと真面目に数値を取得して
// switchとかすべきです
if (strncmp("200 ", ptr, 4) == 0) {
printf("is 200 OK\n");
} else if (strncmp("302 ", ptr, 4) == 0) {
printf("is 302 moved\n");
} else if (strncmp("404 ", ptr, 4) == 0) {
printf("is 404 not found\n");
} else {
printf("other status code\n");
}
// status lineの行末まで移動
while (ptr < buf + n && *ptr != '\n') {
ptr++;
}
// 残りの部分を表示
if (ptr < buf + n) {
fwrite(ptr, n - (ptr - buf), 1, stdout);
}
// さらに残りの部分を表示
while (n > 0) {
memset(buf, 0, sizeof(buf));
n = recv(sock, buf, sizeof(buf), 0);
if (n < 0) {
printf("recv : %d\n", WSAGetLastError());
return 1;
}
// 受信結果を表示
fwrite(buf, n, 1, stdout);
}
closesocket(sock);
WSACleanup();
return 0;
}
折角なので、作成したHTTPクライアントを使ってみたいと思います。 www.google.co.jpに接続すると以下のようになります。 (ただし、下記例では表示スペースに押し込むために一部結果を削ってあります。何となくこんな感じかなぁ程度に見てください。) 前回との違いは強調してあります。
|