7

最近、インターネットからファイルをダウンロードし始めるために、このガイドを使い始めました。私はそれを読んで、Web サイトの HTTP 本文をダウンロードする次のコードを思いつきました。唯一の問題は、それが機能していないことです。recv() 呼び出しを呼び出すと、コードが停止します。クラッシュすることはありません。ただ走り続けます。これは私のせいですか?間違ったアプローチを使用していますか? コードを使用して、.html ファイルのコンテンツをダウンロードするだけでなく、他のファイル (zip、png、jpg、dmg ...) もダウンロードするつもりです。私を助けてくれる人がいることを願っています。これは私のコードです:

#include <stdio.h>
#include <sys/socket.h> /* SOCKET */
#include <netdb.h> /* struct addrinfo */
#include <stdlib.h> /* exit() */
#include <string.h> /* memset() */
#include <errno.h> /* errno */
#include <unistd.h> /* close() */
#include <arpa/inet.h> /* IP Conversion */

#include <stdarg.h> /* va_list */

#define SERVERNAME "developerief2.site11.com"
#define PROTOCOL "80"
#define MAXDATASIZE 1024*1024

void errorOut(int status, const char *format, ...);
void *get_in_addr(struct sockaddr *sa);

int main (int argc, const char * argv[]) {
    int status;

    // GET ADDRESS INFO
    struct addrinfo *infos; 
    struct addrinfo hints;

    // fill hints
    memset(&hints, 0, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = AF_UNSPEC;

    // get address info
    status = getaddrinfo(SERVERNAME, 
                         PROTOCOL, 
                         &hints, 
                         &infos);
    if(status != 0)
        errorOut(-1, "Couldn't get addres information: %s\n", gai_strerror(status));

    // MAKE SOCKET
    int sockfd;

    // loop, use first valid
    struct addrinfo *p;
    for(p = infos; p != NULL; p = p->ai_next) {
        // CREATE SOCKET
        sockfd = socket(p->ai_family, 
                        p->ai_socktype, 
                        p->ai_protocol);
        if(sockfd == -1)
            continue;

        // TRY TO CONNECT
        status = connect(sockfd, 
                         p->ai_addr, 
                         p->ai_addrlen);
        if(status == -1) {
            close(sockfd);
            continue;
        }

        break;
    }

    if(p == NULL) {
        fprintf(stderr, "Failed to connect\n");
        return 1;
    }

    // LET USER KNOW
    char printableIP[INET6_ADDRSTRLEN];
    inet_ntop(p->ai_family,
              get_in_addr((struct sockaddr *)p->ai_addr),
              printableIP,
              sizeof(printableIP));
    printf("Connection to %s\n", printableIP);

    // GET RID OF INFOS
    freeaddrinfo(infos);

    // RECEIVE DATA
    ssize_t receivedBytes;
    char buf[MAXDATASIZE];
    printf("Start receiving\n");
    receivedBytes = recv(sockfd, 
                         buf, 
                         MAXDATASIZE-1, 
                         0);
    printf("Received %d bytes\n", (int)receivedBytes);
    if(receivedBytes == -1)
        errorOut(1, "Error while receiving\n");

    // null terminate
    buf[receivedBytes] = '\0';

    // PRINT
    printf("Received Data:\n\n%s\n", buf);

    // CLOSE
    close(sockfd);

    return 0;
}

void *get_in_addr(struct sockaddr *sa) {
    // IP4
    if(sa->sa_family == AF_INET)
        return &(((struct sockaddr_in *) sa)->sin_addr);

    return &(((struct sockaddr_in6 *) sa)->sin6_addr);
}

void errorOut(int status, const char *format, ...) {
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    exit(status);
}
4

3 に答える 3

12

HTTP を使用してファイルを取得する場合は、おそらく C でlibcURLを使用するのが最善の策です。しかし、これをネットワーク プログラミングの学習方法として使用する場合は、その前に HTTP についてもう少し学習する必要があります。ファイルを取得します。

現在のプログラムで見られるのは、ファイルを取得する前に、ファイルに対する明示的な要求を送信する必要があるということです。RFC2616を読むことから始めます。すべてを理解しようとしないでください。この例を読むのは大変です。最初のセクションを読んで HTTP の仕組みを理解してから、セクション4、5、および 6を読んで基本的なメッセージ形式を理解してください。

以下は、stackoverflow の質問ページに対する HTTP リクエストの例です。

GET http://stackoverflow.com/questions HTTP/1.1\r\n
Host: stackoverflow.com:80\r\n
Connection: close\r\n
Accept-Encoding: identity, *;q=0\r\n
\r\n

最低限のお願いだと思います。RFC2616 で説明されているように、空白行を使用してリクエスト ヘッダー ブロックを終了することを示すために、CRLF を明示的に追加しました。ヘッダーを省略した場合Accept-Encoding、結果のドキュメントはおそらく gzip で圧縮されたストリームとして転送されます。これは、サーバーに不要であることを伝えない限り、HTTP で明示的に許可されているためです。

サーバー応答には、応答を説明するメタデータの HTTP ヘッダーも含まれています。前のリクエストからのレスポンスの例を次に示します。

HTTP/1.1 200 OK\r\n
Server: nginx\r\n
Date: Sun, 01 Aug 2010 13:54:56 GMT\r\n
Content-Type: text/html; charset=utf-8\r\n
Connection: close\r\n
Cache-Control: private\r\n
Content-Length: 49731\r\n
\r\n
\r\n
\r\n
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" ... 49,667 bytes follow

この簡単な例は、HTTP を使用してファイルを取得したい場合に、何を実装しようとしているのかを理解できるはずです。これは最良のケースであり、最も単純な例です。これは私が軽々しく着手できるものではありませんが、おそらく HTTP を学び、理解するための最良の方法です。

ネットワーク プログラミングを学ぶための簡単な方法を探しているなら、これは適切な開始方法です。TCP/IP Illustrated, Volume 1UNIX Network Programming, Volume 1のコピーを入手することをお勧めします。これらは、ネットワーク ベースのアプリケーションの作成方法を実際に学習するためのおそらく最良の方法です。FTPは開始するのにはるかに単純なプロトコルであるため、おそらくFTP クライアントを作成することから始めるでしょう。

HTTP に関連する詳細を知りたい場合は、次のようにします。

  1. HTTP: The Definitive Guideを購入して読む
  2. 理解できるまで RFC2616を読む
    • telnet server 80手でリクエストを使用して入力する例を試してください
    • cURL クライアントをダウンロードし、コマンド ライン オプション--verbose--includeコマンド ライン オプションを使用して、何が起こっているかを確認します。
  3. HTTP が本当に理にかなっているまで、Fielding の論文を読んでください。

企業で使用する独自の HTTP クライアントを作成する予定はありません。あなたはそれをしたくありません、私を信頼してください.

于 2010-08-01T14:36:27.600 に答える
7

問題は、HTTP プロトコルを実装する必要があることです。ファイルのダウンロードは、サーバーに接続するだけの問題ではありません。応答を取得する前に、(適切な HTTP ヘッダーと共に) HTTP 要求を送信する必要があります。この後、返されたデータを解析して、さらに HTTP ヘッダーを削除する必要があります。

C を使用してファイルをダウンロードしようとしているだけの場合は、HTTP が機能するcURL ライブラリをお勧めします。

于 2010-08-01T13:25:56.267 に答える
3

応答を期待する前に、HTTP 要求を送信する必要があります。あなたのコードは現在、決して来ない応答を待つだけです。

また、コメントはすべて大文字で書かないでください。

于 2010-08-01T13:24:53.590 に答える