2010-08-01 35 views
7

Gần đây tôi bắt đầu chụp this guide để bắt đầu tải xuống tệp từ internet. Tôi đọc nó và đưa ra đoạn mã sau để tải xuống phần thân HTTP của một trang web. Vấn đề duy nhất là, nó không hoạt động. Mã dừng lại khi gọi đến lệnh gọi recv(). Nó không sụp đổ, nó chỉ tiếp tục chạy. Đây có phải là lỗi của tôi không? Tôi có sử dụng sai ý kiến ​​không? Tôi có ý định sử dụng mã để không chỉ tải xuống nội dung của tệp .html mà còn tải xuống các tệp khác (zip, png, jpg, dmg ...). Tôi hy vọng có ai đó có thể giúp tôi. Đây là mã của tôi:Tải xuống các ổ cắm HTTP thông qua (C)

#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); 
} 
+2

Nếu ý định tải xuống tệp, không triển khai HTTP, bạn nên sử dụng thư viện chẳng hạn như cURL: http://curl.haxx.se/ – You

Trả lời

12

Nếu bạn muốn lấy tệp bằng HTTP, thì libcURL có lẽ là đặt cược tốt nhất của bạn trong C. Tuy nhiên, nếu bạn đang sử dụng cách này để học lập trình mạng, bạn sẽ phải tìm hiểu thêm một chút về HTTP trước khi bạn có thể truy xuất tệp.

Những gì bạn thấy trong chương trình hiện tại của mình là bạn cần gửi yêu cầu rõ ràng cho tệp trước khi bạn có thể truy xuất tệp đó. Tôi sẽ bắt đầu bằng cách đọc qua số RFC2616. Đừng cố gắng để hiểu tất cả - nó là rất nhiều để đọc cho ví dụ này. Đọc số first section để hiểu cách hoạt động của HTTP, sau đó đọc phần 4, 5, and 6 để hiểu định dạng thông báo cơ bản.

Dưới đây là một ví dụ về một yêu cầu HTTP cho trang stackoverflow câu hỏi trông giống như:

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 

Tôi tin rằng đó là một yêu cầu tối thiểu. Tôi đã thêm CRLF một cách rõ ràng để cho thấy rằng một dòng trống được sử dụng để chấm dứt khối tiêu đề yêu cầu as described in RFC2616. Nếu bạn loại bỏ tiêu đề Accept-Encoding, thì tài liệu kết quả có thể được chuyển thành luồng nén gzip vì HTTP cho phép điều này rõ ràng trừ khi bạn nói với máy chủ mà bạn không muốn.

Phản hồi của máy chủ cũng chứa tiêu đề HTTP cho siêu dữ liệu mô tả phản hồi. Dưới đây là ví dụ về phản hồi từ yêu cầu trước đó:

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 

Ví dụ đơn giản này sẽ cho bạn biết bạn đang thực hiện điều gì nếu bạn muốn lấy tệp bằng HTTP. Đây là trường hợp tốt nhất, ví dụ đơn giản nhất. Đây không phải là điều tôi sẽ thực hiện một cách nhẹ nhàng, nhưng đây có lẽ là cách tốt nhất để học hỏi và đánh giá cao HTTP.

Nếu bạn đang tìm kiếm một cách đơn giản để học lập trình mạng, đây là một cách tốt để bắt đầu. Tôi khuyên bạn nên chọn một bản sao của TCP/IP Illustrated, Volume 1UNIX Network Programming, Volume 1. Đây có lẽ là cách tốt nhất để thực sự tìm hiểu cách viết các ứng dụng dựa trên mạng. Tôi có thể bắt đầu bằng cách viết một FTP client từ FTP là một giao thức đơn giản hơn nhiều để bắt đầu.

Nếu bạn đang cố gắng tìm hiểu các chi tiết liên quan đến HTTP, sau đó:

  1. Mua HTTP: the Definitive Guide và đọc nó
  2. đọc RFC2616 cho đến khi bạn hiểu nó
    • Hãy thử ví dụ sử dụng telnet server 80 và gõ vào yêu cầu bằng tay
    • Tải xuống ứng dụng khách cURL và sử dụng các tùy chọn dòng --verbose--include để bạn có thể xem điều gì đang xảy ra
  3. Đọc Fielding's dissertation cho đến khi HTTP thực sự hợp lý.

Chỉ cần không lập kế hoạch viết ứng dụng khách HTTP của riêng bạn cho sử dụng doanh nghiệp. Bạn không muốn làm điều đó, hãy tin tôi như một người đã duy trì sai lầm như vậy trong một thời gian ngắn ...

+0

Tôi thực sự, thực sự, thực sự muốn cảm ơn tất cả các bạn vì phản ứng nhanh, đặc biệt là D.Shawley. Tôi đoán các tập tin tải xuống sẽ không dễ dàng như tôi nghĩ, nhưng tôi chắc chắn sẽ làm việc này. Tôi muốn điều này làm việc vì tôi muốn độc lập với thư viện curl, và nếu nó không hoạt động ... cURL sẽ luôn ở đó. Cảm ơn, ief2 – v1Axvw

+0

@ lef2. Bạn đang khá hoan nghênh. Tôi sẽ cung cấp một số lời khuyên mặc dù. Sử dụng việc triển khai các giao thức phức tạp mà các giao thức khác cung cấp là một phần quan trọng trong việc phát triển phần mềm. Tôi sẽ nắm lấy các thư viện như cURL, Apache Portable Runtime, Boost và các thư viện phổ biến khác. Viết tất cả mọi thứ cho mình là một công thức cho thảm họa. Đây là một cách rất hay để tìm hiểu cách thức một giao thức hoạt động nhưng một cách rất xấu để sử dụng HTTP ở tầng ứng dụng. –

+0

Tôi đã đồng ý với bạn cho đến khi bạn đề cập đến APR, đó là abomination lớn nhất mà tôi từng thấy trong C ... –

3

Bạn phải gửi yêu cầu HTTP trước khi mong đợi phản hồi. Hiện tại, mã của bạn chỉ chờ phản hồi không bao giờ xuất hiện.

Ngoài ra, không viết nhận xét ở tất cả các chữ cái.

7

Vấn đề là bạn phải triển khai giao thức HTTP. Tải xuống tệp không chỉ là vấn đề kết nối với máy chủ, bạn phải gửi yêu cầu HTTP (cùng với tiêu đề HTTP thích hợp) trước khi bạn nhận được phản hồi. Sau đó, bạn vẫn cần phải phân tích cú pháp dữ liệu trả về để loại bỏ các tiêu đề HTTP khác.

Nếu bạn chỉ đang cố gắng tải xuống tệp bằng C, tôi đề xuất số cURL library, HTTP hoạt động cho bạn.

Các vấn đề liên quan