2011-12-11 36 views
1

我正在學習小HTTP客戶端的開發,特別是使用http/1.1客戶端的NONBLOCKING tcp套接字的整體複雜性。C http O_NONBLOCK模式問題?

下面的小代碼似乎在Linux上工作,但不是在我的Mac OS X 10.6.8 ..調試後似乎循環發送函數?與linux和mac os x/freebsd之間的非阻塞模式有區別嗎?

這是我收到...

連接到google.com(173.194.65.147)的80端口... OK GET/HTTP/1.1 主持人:google.com 的User-Agent: Mozilla的/ 5.0(Macintosh上,英特爾的Mac OS X 10_6_8)爲AppleWebKit/534.52.7(KHTML,例如Gecko)版本/ 5.1.2 Safari瀏覽器/ 534.52.7

發送失敗:套接字未連接

有人能幫助我理解這段代碼出了什麼問題?

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <netdb.h> 
#include <sys/socket.h> 
#include <sys/types.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <errno.h> 

void 
setSockNonBlock(int sock) 
{ 
    int    flags; 
    flags = fcntl(sock, F_GETFL, 0); 
    if (flags < 0) { 
    perror("fcntl(F_GETFL) failed"); 
    exit(EXIT_FAILURE); 
    } 
    if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { 
    perror("fcntl(F_SETFL) failed"); 
    exit(EXIT_FAILURE); 
    } 
} 

int 
getIP(const char *uri, char *hostname, struct in_addr * ip) 
{ 
    char   *tmp; 
    tmp = (char *) malloc(strlen(uri) + 1); 
    strncpy(tmp, uri, strlen(uri) + 1); 
    char   *p, *q; 
    p = strstr(tmp, "http://"); 
    if (p == NULL) { 
    p = tmp; 
    } else { 
    p = tmp + 7; 
    } 
    q = strstr(p, "/"); 
    if (q != NULL) { 
    *q = '\0'; 
    } else { 
    q = tmp + strlen(uri); 
    } 
    strncpy(hostname, p, q - p + 1); 
    free(tmp); 

    struct hostent *host; 
    if ((host = gethostbyname(hostname)) == NULL) { 
    fprintf(stderr, "gethostbyname failed, %s\n", hstrerror(h_errno)); 
    exit(EXIT_FAILURE); 
    } 
    int    i; 
    for (i = 0; host->h_addr_list[i] != NULL; i++) { 
    if (host->h_addrtype == AF_INET) { 
     memcpy(ip, (struct in_addr *) (host->h_addr_list[i]), sizeof(struct in_addr)); 
     return 1; 
    } 
    } 
    return 0; 
} 

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

    if (argc != 2) { 
    fprintf(stderr, "USAGE: %s uri\n", argv[0]); 
    exit(EXIT_FAILURE); 
    } 
    char   *uri; 
    uri = argv[1]; 

    char   *hostname; 
    hostname = (char *) malloc(strlen(uri) + 1); 
    struct in_addr ip; 
    if (!getIP(uri, hostname, &ip)) { 
    fprintf(stderr, "getIP for %s failed\n", uri); 
    exit(EXIT_FAILURE); 
    } 
    char   ip_str[INET_ADDRSTRLEN]; 
    if (!inet_ntop(AF_INET, &ip, ip_str, INET_ADDRSTRLEN)) { 
    perror("inet_ntop failed"); 
    exit(EXIT_FAILURE); 
    } 
    int    sock; 
    if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1) { 
    perror("socket failed"); 
    exit(EXIT_FAILURE); 
    } 
    setSockNonBlock(sock); 

    struct sockaddr_in serv_addr; 
    memset(&serv_addr, 0, sizeof(serv_addr)); 
    serv_addr.sin_family = AF_INET; 
    serv_addr.sin_addr = ip; 
    serv_addr.sin_port = htons(80); 
    if (connect(sock, (struct sockaddr *) & serv_addr, sizeof(serv_addr)) == -1 && (errno != EINPROGRESS)) { 
    perror("connect failed"); 
    exit(EXIT_FAILURE); 
    } 
    printf("Connect to %s(%s) on port 80 ... ok \n", hostname, ip_str); 

    char   request[1024] = ""; 
    char   *get; 
    get = strstr(uri, hostname); 
    if (get == NULL) { 
    get = "/"; 
    } else { 
    get += strlen(hostname); 
    if (*get == '\0') { 
     get = "/"; 
    } 
    } 

    sprintf(request, "GET %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/534.52.7 (KHTML, like Gecko) Version/5.1.2 Safari/534.52.7\r\n\r\n", get, hostname); 
    printf("%s\n", request); 
    free(hostname); 

    char   *p = request; 
    int    remaining = strlen(request); 
    ssize_t   sent_size; 
    while (remaining) { 
    sent_size = send(sock, p, remaining, 0); 

    if (sent_size == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { 
     continue; 
    } 
    if (sent_size <= 0) { 
     perror("sent failed"); 
     exit(EXIT_FAILURE); 
    } 
    remaining -= sent_size; 
    p += sent_size; 
    } 

    char   *resp = NULL; 
    int    resp_size = 0; 
    char   buf[1024]; 
    ssize_t   recv_size; 

    char   *tmp; 
    while (1) { 
    recv_size = recv(sock, buf, sizeof(buf), 0); 
    if (recv_size == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { 
     continue; 
    } 
    if (recv_size <= 0) { 
     break; 
    } 
    tmp = (char *) realloc(resp, resp_size + recv_size + 1); 
    if (tmp == NULL) { 
     fprintf(stderr, "Error: realloc failed, not enough memory to save response"); 
     if (resp) { 
     free(resp); 
     } 
     exit(EXIT_FAILURE); 
    } 
    resp = tmp; 

    tmp = resp + resp_size; 
    memcpy(tmp, buf, recv_size); 
    resp_size += recv_size; 
    resp[resp_size] = '\0'; 
    if (strstr(resp, "</html>")) { 
     break; 
    } 
    } 
    if (resp) { 
    printf("%s\n", resp); 
    free(resp); 
    } 
    close(sock); 
    return 0; 
} 
+2

當從'send'檢查錯誤時,請不要使用'sent_size <= 0'。零是要返回的有效值。 –

回答

0

是否有任何理由,以避免使用現有的HTTP相關的庫,例如libcurl的HTTP客戶端部分和D.Moreno's Onionlibmicrohttpd上的HTTP服務器端?

編寫自己的HTTP協議棧非常耗時!

1

我懷疑你的問題是你正在做一個無阻塞的connect(),但是立即通過一些需要幾毫秒執行的代碼,然後嘗試發送那個可能還沒有連接的套接字。

在連接之後,您可以通過在那裏粘貼一個sleep()幾秒鐘來做一個簡單的測試,看看是否是真正的原因。但更大的問題是,至少在目前,你沒有任何令人信服的理由來使用非阻塞套接字,因爲你沒有使用任何事情來處理阻塞的時間。例如,您的發送循環不會執行任何操作,而是檢查返回碼並嘗試重新發送。你可能會阻止。也許代碼利用非阻塞是你在下一個階段中的領導地位?

在任何情況下,考慮select()poll()(和朋友喜歡pselectppoll,...)。這將有助於複用。

+0

我編輯了上面的回覆,提及'poll'&'pselect' –

+0

我在連接之後添加了一個sleep(2),但現在應用程序似乎在無限循環中等待... –

+0

@Vincent Blondel - 我跑了你的pgm但阻塞或非阻塞你只是掛起(或循環)在recv這意味着你沒有得到任何數據。你可能想檢查你發送的內容。 – Duck