2016-08-18 13 views
0

사용자가 바인드 호출에 사용 된 로컬 주소를 선택할 수있는 TCP 서버 응용 프로그램을 만들고 싶습니다. 사용자는 호스트 이름 또는 IP 주소의 텍스트 표현을 제공 할 수 있으므로 getaddrinfo 함수를 사용하여 텍스트 표현을 하나 또는 여러 개의 sockaddr 구조체 (필요한 경우 이름 조회 수행)로 변환하는 방법을 생각했습니다.getaddrinfo를 사용하여 서버 소켓 주소를 선택하는 방법은 무엇입니까?

이제 내 문제가있다 : 바인딩 요청에 사용할 수있는 소켓 주소를 얻기 위해 힌트 구조에 AI_PASSIVE 플래그를 설정해야하기 때문에 getaddrinfo 함수가 내 필요에 맞지 않는 것처럼 보입니다. 그러나 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 매개 변수입니까? 의도는 무엇입니까? 그것은 나에게 어떤 의미가 없습니다. 바인드 할 수 있고 지정된 호스트 이름이나 IP 주소에 텍스트 표현으로 전혀 대응하지 않는 로컬 주소를 찾는 방법이 있습니까? 지정된 노드 이름이 로컬 주소에 해당하고 AI_PASSIVE가 이 아니고이 아닌 경우 getaddrinfo에서 반환 한 주소가 바인딩에 사용할 수 없습니까? 또는 더 나쁜 것은 바인딩에만 적합하고이 경우에는 반환되지 않는 주소가 있습니까?

관련 (그리고 대답 만족) : getaddrinfo: in what way is AI_PASSIVE ignored if the nodename is specified?

답변

3

나는 참조 문제가 만족스럽게 대답되지 않았고 IMO 전체 기능이 제대로 지정 동의합니다.

나는 AI_PASSIVE 플래그의 이름이 잘못되었다고 생각합니다. AI_ANY_ADDRESS과 같은 이름이어야합니다. "INADDR_ANYIN6ADDR_ANY_INIT을 하드 코딩 할 필요가 없도록"이 시스템의 모든 노드 주소 (주소 패밀리 내 등)에 바인드하는 데 사용할 수있는 토큰을 제공하십시오 "를 의미합니다. 따라서 노드 이름 인수를 제공하면 AI_PASSIVE의 요점을 없앨 수 있습니다. 당신은 "여기에 바인딩하려는 주소가 있습니다"라고 말하고 있습니다. bindconnect에 의해 허용 주소의 구조가 근본적으로 모든 경우에 동일하기 때문에 실제로

는,이 모두가 잘 밖으로 작동 (제외 당신 수 없습니다 connect에 "모든"주소 특별한). 그래서 노드 이름을 지정할 때 AI_PASSIVE을 지정했는지 여부는 중요하지 않습니다. 지정된 이름/주소로 bind 또는 connect 호출에 적합한 매개 변수를 가져옵니다.

"주소 정보"가 bind 또는 connect을 의미하는 것은 아닙니다. bind으로 보낼 수없는 주소를 로컬 컴퓨터의 주소가 아니기 때문에 또는 다른 이유 때문에 성공적으로 얻을 수 있습니다. (분명히 connect도 여러 가지 이유로 실패 할 수 있습니다. 해당 주소에 응답하는 컴퓨터가 없거나 포트에서 수신 대기중인 서버가 없을 수 있습니다.)

IP 주소가 아닌 노드 이름을 제공하고 이름 확인이 여러 패밀리 (로컬 시스템에서 모두 사용 가능함)에 여러 IP 주소를 제공하면 해당 주소 모두에 대해 bind을 사용할 수 있어야합니다.

결론 : 사용자가 주소를 지정할 수있게하려면 getaddrinfo에 주소를 제공하십시오. bind 전화에서 사용할 수있는 주소 (있는 경우)가 있어야합니다.

+0

바인딩/연결을위한 주소를 구분하는 스펙은 오해의 소지가 있으며 의미가 없습니다. 이러한 구분이 없으면 INADDR_ANY/IN6ADDR_ANY_INIT에 대한 간단한 추상화 인 AI_PASSIVE 플래그는 여전히 고유 한 권한으로 유용하며 모든 사용 사례가 충족됩니다. 이 위대한 설명에 감사드립니다! – purefanatic