2016-08-18 63 views
0

我想創建一個TCP服務器應用程序,讓用戶選擇在綁定調用中使用的本地地址。用戶可以提供主機名或IP地址的文本表示,所以我想使用getaddrinfo函數將文本表示轉換爲一個或多個sockaddr結構(如果需要,執行名稱查找)。如何使用getaddrinfo選擇服務器套接字地址?

現在這是我的問題:getaddrinfo函數似乎不符合我的需要,因爲它需要在提示結構中設置AI_PASSIVE標誌以獲取可用於綁定調用的套接字地址。但是,如果我使用AI_PASSIVE,則不能再使用nodename參數,這會使用戶選擇本地地址的整個目的失敗。如果我不提供AI_PASSIVE,getaddrinfo將只返回那些可用於connect,sendto和sendmsg調用的地址,但可能有地址可用於綁定,但不能用於連接,sendto或sendmsg調用,這些地址可省略。請參閱有關getaddrinfo/freeaddrinfo函數的POSIX規範。

爲了澄清我的需要,這裏是應用程序的素描時,我想創建:

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <sys/socket.h> 
#include <netdb.h> 

int main(int argc, char **argv) 
{ 
    if (argc != 3) { 
     fprintf(stderr, "Usage: %s address port\n", argv[0]); 
     return EXIT_FAILURE; 
    } 

    struct addrinfo hints = {0}; 
    hints.ai_family = AF_UNSPEC; 
    hints.ai_socktype = SOCK_STREAM; 
    hints.ai_flags = AI_PASSIVE; 

    struct addrinfo *result; 
    /* The next line won't work as intended! It will behave as if the 
    AI_PASSIVE flag was not set. */ 
    if (getaddrinfo(argv[1], argv[2], &hints, &result) != 0) { 
     perror("getaddrinfo"); 
     return EXIT_FAILURE; 
    } 

    /* Iterate result list */ 
    int sfd; 
    struct addrinfo *rp = result; 
    do { 
     sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); 
     if (sfd == -1) 
      continue; 

     if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) 
      break; /* Success */ 

     close(sfd); 
     rp = rp->ai_next; 
    } while (rp != NULL); 

    freeaddrinfo(result); 

    if (rp == NULL) { /* No address succeeded */ 
     fprintf(stderr, "Could not bind\n"); 
     return EXIT_FAILURE; 
    } 

    /* ... use socket bound to sfd ... */ 

    close(sfd); 

    return EXIT_SUCCESS; 
} 

我其實有關於該主題的幾個問題。首先,爲什麼是使用AI_PASSIVE標誌時被禁止的nodename參數?意圖是什麼?這對我沒有任何意義。有沒有(最好是符合POSIX的)方法來查找我可以綁定到的本地地址,並且與文本表示中的給定主機名或IP地址相對應?假設給定節點名稱對應於本地地址,並且AI_PASSIVE是而不是集合,那麼getaddrinfo將返回哪些地址,這些地址不能用於綁定?或者更糟糕的是,是否有任何地址只適合綁定,在這種情況下不會返回?

相關的(而不是回答令人滿意):getaddrinfo: in what way is AI_PASSIVE ignored if the nodename is specified?

回答

3

我同意引用的問題沒有得到滿意的答覆和國際海事組織全功能指定很差。我相信AI_PASSIVE標誌是錯誤的。它應該被稱爲AI_ANY_ADDRESS。它只是意味着「給我一個令牌,我可以使用它綁定到此係統上的任何節點地址(在地址族中)」,以便您不必硬編碼INADDR_ANYIN6ADDR_ANY_INIT。因此,如果您提供了一個節點名稱參數,則可以避免使用AI_PASSIVE。你說「這是我想要綁定的地址」。

在實踐中,這一切工作了罰款,因爲bindconnect接受地址的結構基本上是在所有情況下相同的(除非你不能connect到特殊的「任何」地址)。這就是爲什麼 - 當您指定節點名稱時 - 是否指定AI_PASSIVE並不重要。您將獲得適用於指定名稱/地址的bindconnect調用的參數。

獲取「地址信息」並不意味着bindconnect會成功。您可以成功獲取您不能bind的地址 - 因爲它不是本地計算機的地址 - 或者出於其他原因。 (顯然,connect也可能因多種原因而失敗 - 在該地址可能沒有機器應答,或者沒有服務器在端口上偵聽等等。)

如果您提供的節點名稱不是IP地址,並且名稱解析提供不同系列的多個IP地址(本地計算機上都提供了這些IP地址),則您應該可以對所有這些地址使用bind

所以,底線:如果你想允許用戶指定地址,只需提供到getaddrinfo。你回來的地址(如果有的話)應該可以用於撥打bind

+0

因此,這種區分地址之間的綁定/連接的規範只是簡單的誤導,沒有任何意義。沒有這種區別,AI_PASSIVE標誌作爲INADDR_ANY/IN6ADDR_ANY_INIT的簡單抽象仍然是有用的,並且所有用例都滿足。謝謝你這個偉大的解釋! – purefanatic

相關問題