プロが教える店舗&オフィスのセキュリティ対策術

VC++2010でHTTP通信のサンプルプログラムを走らせてみたのですが、どうも上手く行きません。

ソースは以下の通りです。

#include <stdio.h>
#include <winsock2.h>

int main()
{
WSADATA wsaData;
LPHOSTENT lpHost;
SOCKET s;
int nRtn;
SOCKADDR_IN sockadd;
char szServer[256], szURL[256], szStrRcv[1024], szPort[8];
char szStr[256], szYN[4];
u_short port;
unsigned int addr;

while (1) {
// WinSockを初期化
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
perror("WSAStartupエラーです\n");
return -1;
}

// サーバ名、ポート番号、ファイル名を取得
printf("Webサーバ名----");
gets_s(szServer);
printf("ポート番号----");
gets_s(szPort);
if (strcmp(szPort, "") == 0)
strcpy_s(szPort, "80");
port = (u_short)atoi(szPort);
printf("ファイル名----");
gets_s(szURL);
if (strcmp(szURL, "") == 0)
strcpy_s(szURL, "/");

// ソケットをオープン
s = socket(PF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET) {
perror("ソケットをオープンできません\n");
WSACleanup();
return -2;
}

// ホスト名からホスト情報を取得
lpHost = gethostbyname(szServer);
if (lpHost == NULL) {
// アドレスを表す文字列を数値表現に変換
addr = inet_addr(szServer);
// ネットワークアドレスからホスト情報を取得
lpHost = gethostbyaddr((char *)&addr, 4, AF_INET);
if (lpHost == NULL) {
wsprintf(szStr, "%sが見つかりません\n", szServer);
perror(szStr);
WSACleanup();
return -3;
}
}

// SOCKADDR_IN構造体に必要事項をセット
memset(&sockadd, 0, sizeof(sockadd));
sockadd.sin_family = AF_INET;
sockadd.sin_port = htons(port);
sockadd.sin_addr = *((LPIN_ADDR)*lpHost->h_addr_list);

// ソケットに接続
if (connect(s, (PSOCKADDR)&sockadd, sizeof(sockadd)) != 0) {
perror("サーバソケットに接続失敗\n");
closesocket(s);
WSACleanup();
return -4;
}

// GETリクエストを送信
wsprintf(szStr, "GET %s HTTP/1.0\r\n\r\n", szURL);
nRtn = send(s, szStr, (int)strlen(szStr), 0);

while(1) {
// 受信用バッファ(szStrRcv)をゼロクリア
memset(szStrRcv, '\0', sizeof(szStrRcv));
// データを受信
nRtn = recv(s, szStrRcv, (int)sizeof(szStrRcv) - 1, 0);
// 受信したデータを標準出力に書き出す
printf("%s", szStrRcv);

if (nRtn == 0)
break;
if (nRtn == SOCKET_ERROR) {
perror("recvエラーです\n");
break;
}
}

if (shutdown(s, SD_BOTH) != 0) { // シャットダウン
perror("シャットダウンに失敗しました\n");
}
closesocket(s); // ソケットをクローズ
WSACleanup(); // WinSockのリソースを解放

printf("\nもう一度実行しますか(Y/N)----");
gets_s(szYN);
if (strcmp(szYN, "n") == 0 || strcmp(szYN, "N") == 0)
break;
}

return 0;
}

例えば、ttp://kumei.jp/c_lang/を開こうしとて、
kumei.jp
80
/c_lang/index.html
と入力した場合は上手くレスポンスが帰ってきますが、

googleに接続しようと思い、
www.google.co.jp
80
/
と入力すると 302 Moved と帰ってきます。
しかし
www.google.co.jp
80
www.google.co.jp
と入力した場合は問題なくgoogleのトップページが帰ってきます。

また、ttp://hako.gob.jp/にアクセスしようとして、
hako.gob.jp
80
/index.html
と入力した場合 DOMAIN ERROR が帰ってきます。
さらに、
hako.gob.jp
80
hako.gob.jp
と入力すると 400 エラーが帰って来ました。


私はwindowsプログラムに関しては全くの素人で分からない事だらけの状態です。できるだけ分り易く書いて頂けると嬉しいです。私が教えて頂きたいことは以下のことです。

・サイト(通信相手?)による違いはどこで生まれるのでしょうか?
・また、正しくはどのように入力すれば良いのでしょうか?それはどうすれば分かるのでしょうか?
・アドレスを指定しただけであとはプログラムに自動で判断してもらうにはどうすれば良いでしょうか?
・そもそも、このプログラムでは根本的に不可能な事をやろうとしているのでしょうか?もしそうであった場合どうすれば良いでしょうか?

質問だらけで申し訳ありません。誰か分かる方がいらっしゃれば教えて下さい。

A 回答 (4件)

すみません、質問に対する直接の回答ではないですがアドバイスを。


VC++でHTTP通信しようと思ったら直接WINSOCKでやるより、
WININETライブラリを使った方が便利です。
WININETはIEの下層となっているライブラリであり、クッキーの処理や
SSL通信(https)もサポートしているので後々いろいろやりたいなら
こちらのほうが便利です。そうではなくプログラミングの勉強目的
ならWINSOCKでもいいですが。
    • good
    • 0
この回答へのお礼

winsock以外にも方法があるんですね。
調べてみます。ありがとうございました。

お礼日時:2012/10/04 18:26

302について


 302は、指定されたページが移動していることを示します。
 新しいページは、レスポンスヘッダのLocation:が示すURLです。
 このURLからサーバ名とURIに分解して再度要求をだします。

サイトの違い
 ブラウザでhttp:~/hoge/の様に/で終わるURLを指定すると、ページが見れますが、
 実際には一度302が帰ってきて、ページ名を省略された場合のデフォルトページ名が
 追加されたURLに誘導されています。

自動化
 サーバから返されるレスポンスヘッダの一行目は、必ず HTTP/1.X NNN Description
 というフォーマットになっています。
 XはHTTPのバージョンで1.0か1.1が帰ってきます。最近はほとんど1.1です。
 NNNは三桁の数字で、リクエストに対する結果が入ります。
 100番台は処理中であることを示します。サーバの処理に時間がかかる場合などに帰ってきますが、
 1.0で要求するとこれは帰ってきません。
 200番台は成功を示します。300番台は302の様に指定された場所にページは無いけと、他の場所に
 存在する、みたいな情報通知みたいなもの。
 400番台は失敗を示します。401(認証失敗)や404(ページがない)。
 500番台はサーバ側の要因による失敗。
 Descriptionは、前記のコードの簡単な説明。
 レスポンスコードは、ググれば出てきます。

 このレスポンスコードの番号によって、処理をある程度自動化できます。

 結構面倒なので、HTTPを使ってサーバにアクセスするのが目的ならば、
 ANo1さんのやつを使う方が簡単です。
 通信をやる勉強ならば、レスポンスの百の位の値で分類して、それから302みたいに必要な処理を
 個別に処理すれば、取り合えず自動化できます。
 400番台(400を除く),500番台はクライアント側では対処できませんし、300番台も302以外は継続できませんから
 これら以外の300,400,500番台は全て通信終了とみなす、見たいに。
    • good
    • 0
この回答へのお礼

サーバーからの返答はかなり種類があるんですね・・・。
このやり方は少し難しいようなので、アドバイスの通りNo1さんのやり方を調べて見たいと思います。
ありがとうございました。

お礼日時:2012/10/04 18:35

>・サイト(通信相手?)による違いはどこで生まれるのでしょうか?



相手方の設定などによる…ということかと。

>・また、正しくはどのように入力すれば良いのでしょうか?それはどうすれば分かるのでしょうか?

ホストの想定している形式で……ということになるかと。
後述する名前ベースのバーチャルホストの場合はHTTP/1.0ではどうにもできないかと。
# Hostヘッダ付けたらいけましたが。

>googleに接続しようと思い、
>www.google.co.jp
>80
>/
>と入力すると 302 Moved と帰ってきます。

サーバが複数あったかと思いますので、ロードバランサなどにより別のサーバへ飛んで欲しい…ということでしょう。

>しかし
>www.google.co.jp
>80
>www.google.co.jp
>と入力した場合は問題なくgoogleのトップページが帰ってきます。

HTTPリクエストとしては…ちょっと変かもしれませんが、サーバ側でよろしく処理してくれた…のでしょう。
プロキシ使った場合のGETリクエストではサーバ名も入ったかと思いますけど……

>また、ttp://hako.gob.jp/にアクセスしようとして、
>hako.gob.jp
>80
>/index.html
>と入力した場合 DOMAIN ERROR が帰ってきます。

名前ベースのバーチャルホストなのかも知れません。
なので、「HTTP/1.1」で「Hostヘッダ」が必要なのでしょう。
そこにHTTP/1.0でHostヘッダ無しのアクセスだった為、どのバーチャルホストへのアクセスなのか不明な為エラーを返答してきたのでしょう。
# レスポンスは「HTTP/1.1 200 OK」ですし…。

>さらに、
>hako.gob.jp
>80
>hako.gob.jp
>と入力すると 400 エラーが帰って来ました。

GETリクエストとして不正だったから…でしょうかねぇ。
googleのようにサーバ内部でよろしく処理してくれなかったのでしょう。
# Hostヘッダが無いのでよろしくしようにも無理なんでしょうが……。

>・アドレスを指定しただけであとはプログラムに自動で判断してもらうにはどうすれば良いでしょうか?

サーバからのレスポンスを見て処理する。
ってところでしょうか。
googleの場合は移動先を指定されている筈ですし。

>・そもそも、このプログラムでは根本的に不可能な事をやろうとしているのでしょうか?もしそうであった場合どうすれば良いでしょうか?

期待する応答とは違うかも知れませんが、『HTTPリクエストを投げてHTTPレスポンスを受ける。』という部分についてはとりあえず動作しているとみていいでしょう。
あとは、『HTTPプロトコル』の理解を深める必要はあるでしょう。
    • good
    • 0
この回答へのお礼

そうですね。私はこの辺りの知識がほぼ皆無のようですね・・・。
プログラムのミスなどではなくそういう仕組なんですね。
詳しく教えでくださり、ありがとうございます。

お礼日時:2012/10/04 18:31

> www.google.co.jp


> 80
> /
> と入力すると 302 Moved と帰ってきます。

ページがリダイレクトされる場合の正常な応答だと思いますが。

> しかし
> www.google.co.jp
> 80
> www.google.co.jp
> と入力した場合は問題なくgoogleのトップページが帰ってきます。

存在しないページを指定した場合に、強制的にトップページを表示するようになっているのでしょう。

> hako.gob.jp
> 80
> hako.gob.jp
> と入力すると 400 エラーが帰って来ました。

こちらのサイトでは、存在しないページを指定した場合はエラーを返すようになっているのでしょう。

何が問題なのかわかりません。
    • good
    • 0
この回答へのお礼

エラーとは言っても正常なレスポンスなんですね。
ありがとうございました。

お礼日時:2012/10/04 18:28

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!