2012-12-13 2 views
0

내가 작성한 다음 C 함수는 IPv4 연결을 허용하지만 IPv6은 허용하지 않는 파일 설명자를 반환합니다. 누군가가 잘못된 정보를 찾도록 도와 줄 수 있습니까? getaddrinfo()를 올바르게 사용하지 않았다고 생각합니다.c 웹 서버에서 ipv6 요청을 수락하는 데 문제가 발생했습니다.

연결을 수신하는 파일 설명자를 엽니 다.

/* 
* open_listenfd - open and return a listening socket on port 
*  Returns -1 and sets errno on Unix error. 
*/ 
int open_listenfd(int port) 
{ 
    const char* hostname=0; 
    // Converts port to string 
    char* pName = malloc(numPlaces(port) + 1); 
    sprintf(pName, "%d", port); 
    const char* portname= pName; 

    struct addrinfo hints; 
    memset(&hints,0,sizeof(hints)); 
    hints.ai_family=AF_UNSPEC; 
    hints.ai_socktype=SOCK_STREAM; 
    hints.ai_protocol= 0; 
    hints.ai_flags=AI_PASSIVE|AI_ADDRCONFIG; 
    struct addrinfo* res=0; 
    int err=getaddrinfo(hostname,portname,&hints,&res); 
    free(pName); 
    if (err!=0) { 
      return -1; 
    } 

    int listenfd, optval=1; 
    struct sockaddr_in serveraddr; 

    /* Create a socket descriptor */ 
    if ((listenfd = socket(res->ai_family,res->ai_socktype, res->ai_protocol)) < 0) 
     return -1; 

    /* Eliminates "Address already in use" error from bind. */ 
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, 
       (const void *)&optval , sizeof(int)) < 0) 
     return -1; 

    /* Listenfd will be an endpoint for all requests to port 
     on any IP address for this host */ 
    bzero((char *) &serveraddr, sizeof(serveraddr)); 
    serveraddr.sin_family = /*AF_INET;*/ AF_UNSPEC; 
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    serveraddr.sin_port = htons((unsigned short)port); 
    if (bind(listenfd, res->ai_addr, res->ai_addrlen/*(SA *)&serveraddr, sizeof(serveraddr)*/) < 0) 
    return -1; 
    freeaddrinfo(res); 

    /* Make it a listening socket ready to accept connection requests */ 
    if (listen(listenfd, 1024) < 0) 
     return -1; 
    return listenfd; 
} 
+0

서버가 ipv6 연결 만 수신하도록 하시겠습니까? – alk

답변

3

당신은 서버가 IPV4에 듣고 싶은 경우와 IPV6 당신을 해결 할 필요가 듣고 두 개의 소켓을 설정할 수 있습니다.

getaddrinfo()은 주어진 호스트 및/또는 서비스에 대해 하나 이상의 인터넷 주소에 대한 정보를 반환 할 수 있습니다.

hints 구조체의 구성원 ai_family이 호출자가 관심을 갖는 주소 패밀리를 지정합니다. AF_UNSPEC이 지정되면 IPV4 및 IPV6 주소가 반환 될 수 있습니다.

int open_listenfd(int port, int * pfdSocketIpV4, int * pfdSocketIpV6) 
{ 
    ... 

    struct addrinfo hints = {0}; 
    hints.ai_family = AF_UNSPEC; 
    hints.ai_socktype = SOCK_STREAM; 
    hints.ai_flags = AI_PASSIVE|AI_ADDRCONFIG; 
    hints.ai_next = NULL; 

    struct addrinfo * res = NULL; 

    int err=getaddrinfo(hostname, portname, &hints, &res); 
    free(pName); 
    if (err) 
    { 
    return -1; 
    } 

    struct addrinfo * pAddrInfoIpV4 = NULL; 
    struct addrinfo * pAddrInfoIpV6 = NULL; 

    { 
    struct addrinfo * pAddrInfo = res; 

    /* Loop over all address infos found until a IPV4 and a IPV6 address is found. */ 
    while (pAddrInfo) 
    { 
     if (!pAddrInfoIpV4 && AF_INET == pAddrInfo->ai_family) 
     { 
     pAddrInfoIpV4 = pAddrInfo; /* Take first IPV4 address available */ 
     } 
     else if (!pAddrInfoIpV6 && AF_INET6 == pAddrInfo->ai_family) 
     { 
     pAddrInfoIpV6 = pAddrInfo; /* Take first IPV6 address available */ 
     } 
     else 
     { 
     break; /* Already got an IPV4 and IPV6 address, so skip the rest */ 
     } 

     pAddrInfo= pAddrInfo->ai_next; /* Get next address info, if any */ 
    } 
    } 

    if (pAddrInfoIpV4) 
    { 
    ... /* create, bind and make IPV4 socket listen */ 
    int fdSocketIpV4 = socket(pAddrInfoIpV4->ai_family,... 

    *pfdSocketIpV4 = fdSocketIpV4; 
    } 

    if (pAddrInfoIpV6) 
    { 
    /* create, bind and make IPV6 socket listen */ 
    int fdSocketIpV6 = socket(pAddrInfoIpV6->ai_family,... 

    *pfdSocketIpV6 = fdSocketIpV6; 
    } 

    freeaddrinfo(res); 

    ... 

다음과 같이 호출 :

... 
int fdSocketIpV4 = -1;  
int fdSocketIpV6 = -1; 

if (0 > open_listenfd(port, &fdSocketIpV4, &fdSocketIpV6)) 
{ 
    printf("Error executing 'open_listenfd()'\n"); 
} 
else 
{ 
    ... /* go for accepting connectings on 'fdSocketIpV4' and 'fdSocketIpV6' */ 

업데이트 : 같은 주소가 당신이 그렇게처럼 코드를 MOD하는 것 같아서 사용할 수있는 경우

를 찾으려면

에 의해 논평 된대로 요한 sson 또 다른 방법은 이중 스택 소켓을 설정하는 것입니다.이 대답에서 언급 한대로 Ipv4와 Ipv6을 모두 지원하는 것입니다. How to support both IPv4 and IPv6 connections

+0

감사합니다. –

+1

@ MLD_Saturn : 천만에. 당신이 대답을 좋아한다면 그것을 upvote 무료입니다. 문제가 해결 된 경우 체크 표시를 클릭하여 승인 할 수도 있습니다. – alk

+1

2 개의 소켓을 설정해야한다는 것은 사실이 아닙니다. IPv6 소켓을 사용하는 옵션도 있습니다. –

관련 문제