2012-07-12 30 views
7

Tôi porting một ứng dụng IPv4 sang một codebase AF-độc lập (nó nên làm việc với IPv4 và IPv6). Bây giờ tôi đang sử dụng sockaddr_storage bất cứ nơi nào tôi có thể, tuy nhiên bây giờ tôi phải đặt (điền) một số sockaddr_storage. Nhưng tôi không biết cách chính xác là gì. Mã trước đây là:Thiết địa chỉ IPv4/IPv6 và cổng vào một cấu trúc sockaddr_storage

// defined in data_socket.h 
struct sockaddr_in laddr; 

Bây giờ có chức năng này mà bộ sin_addrsin_port:

void DataSocket::SetLocalAddr(const char *addr, const int port) 
{ 
    this->laddr.sin_port = htons(port); 
    if(addr != NULL) 
     this->laddr.sin_addr.s_addr = inet_addr(addr); 
    else 
     this->laddr.sin_addr.s_addr = inet_addr("0.0.0.0"); 
} 

Như bạn thấy đây là phong cách cũ (sử dụng IPv4).

Bây giờ, các thay đổi của tôi ở bên dưới. Trước tiên tôi đã thay đổi sockaddr_in để sockaddr_storage

// defined in data_socket.h 
struct sockaddr_storage laddr; 

Sau đó, tôi đã thay đổi mã trên để hỗ trợ IPv4IPv6:

void DataSocket::SetLocalAddr(const char *addr, const int port) 
{ 
switch (this->GetAddrFamily(addr)) { 
    case AF_INET: 
     (struct sockaddr_in *) this->laddr.sin_port = htons(port); 
     if(addr != NULL) 
      inet_pton(AF_INET, addr, (struct sockaddr_in *) this->laddr.sin_addr); 
     else 
      inet_pton(AF_INET, "0.0.0.0", (struct sockaddr_in *) this->laddr.sin_addr); 
     break; 

    case AF_INET6: 
     (struct sockaddr_in6 *) this->laddr.sin6_port = htons(port); 
     if(addr != NULL) 
      inet_pton(AF_INET6, addr, (struct sockaddr_in6 *) this->laddr.sin6_addr); 
     else 
      inet_pton(AF_INET6, "0:0:0:0:0:0:0:0", (struct sockaddr_in6 *) this->laddr.sin6_addr); 
     break; 

    default: 
     return NULL; 

} 

}

đâu GetAddrFamily() là :

int DataSocket::GetAddrFamily(const char *addr) 
{ 
    struct addrinfo hints, *res; 
    int status, result; 

    memset(&hints, 0, sizeof(hints)); 
    hints.ai_family = AF_UNSPEC; 
    hints.ai_socktype = SOCK_STREAM; 

    if ((status = getaddrinfo(addr, 0, &hints, &res)) != 0) 
    { 
     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); 
     return false; 
    } 

    result = res->ai_family; // This might be AF_INET, AF_INET6,etc.. 
    freeaddrinfo(res); // We're done with res, free it up 

    return result; 
} 

Dường như cách của tôi phức tạp. Đây có phải là cách đúng để thực hiện việc này không? Bởi vì tôi đã thay đổi sockaddr_in để sockaddr_storage, thực sự tôi chỉ muốn điều ngược lại của câu hỏi này: Getting IPV4 address from a sockaddr structure

Tôi đang cố gắng tìm ra giải pháp tốt nhất, ví dụ ở đây: http://www.kame.net/newsletter/19980604/ nó nói bao giờ sử dụng inet_ntop()inet_pton(), tuy nhiên một số người khác (như hướng dẫn mạng của Beej) nói rằng inet_ntop()inet_pton() nên được sử dụng cho IPv6 ứng dụng dựa trên.

Cách triển khai của tôi có đúng không hoặc tôi có nên thay đổi không?

+1

Đây là một lý do tại sao tôi thích 'boost :: asio :: ip :: address', nó tóm tắt đi' boost :: asio :: ip :: address_v4' và 'boost :: asio :: ip :: address_v6'. – Chad

+1

Hãy cho tôi biết khi nào C có chức năng thành viên. – Puppy

+0

@DeadMG oops xin lỗi vì thẻ 'c' –

Trả lời

5

Tôi khuyên bạn nên để getaddrinfo thực hiện tất cả việc nâng hạng nặng, ví dụ:

void DataSocket::SetLocalAddr(const char *addr, const unsigned short int port) 
{ 
    struct addrinfo hints, *res; 
    int status; 
    char port_buffer[6]; 

    sprintf(port_buffer, "%hu", port); 

    memset(&hints, 0, sizeof(hints)); 
    hints.ai_family = AF_UNSPEC; 
    hints.ai_socktype = SOCK_STREAM; 
    /* Setting AI_PASSIVE will give you a wildcard address if addr is NULL */ 
    hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE; 

    if ((status = getaddrinfo(addr, port_buffer, &hints, &res) != 0) 
    { 
     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); 
     return; 
    } 

    /* Note, we're taking the first valid address, there may be more than one */ 
    memcpy(&this->laddr, res->ai_addr, res->ai_addrlen); 

    freeaddrinfo(res); 
} 
+0

Cảm ơn bạn đã nhập mã. Nhưng tôi đã nhầm lẫn với dòng lệnh 'memcpy (& this-> laddr, res-> ai_addr, res-> ai_addrlen); '. 'Res-> ai_addr' là cấu trúc sockaddr, tôi đoán nó được tự động chuyển thành' sockaddr_in' hoặc 'sockaddr_in6'? Làm thế nào nó được lưu trữ trong 'this-> laddr' (vì' this-> laddr' là cấu trúc 'sockaddr_storage')? –

+0

Và tôi đoán tôi nên sử dụng 'port_buffer'instead của' cổng' trong 'getaddrinfo', bởi vì' getaddrinfo() 'ngoại trừ một const char * (aka string)? –

+1

'res-> ai_addr' trỏ đến một số biến thể của' struct sockaddr', hoặc 'sockaddr_in' hoặc' sockaddr_in6'. Không có chuyển đổi nào xảy ra, cấu trúc đơn giản được sao chép như vậy. 'struct sockaddr_storage' có kích thước và bố cục cho phép nó được đúc thành bất kỳ biến thể' sockaddr' nào, tất cả đều có chung một tiêu đề. lại: đi qua một chuỗi như cổng, tôi sẽ không bận tâm, nhưng đó là mã của bạn. (cũng có, typo cố định, getaddrinfo có nghĩa là bộ đệm) – Hasturkun

1

Nếu tôi hiểu chính xác bạn đang truyền this cho thành viên đầu tiên? đừng làm vậy, hãy đặt tên cho thành viên.

Tôi cũng sẽ làm cho nó dễ dàng hơn để đọc bằng cách giới thiệu một phạm vi {} và một biến địa phương cho cả hai trường hợp, một cái gì đó như:

{ 
struct sockaddr_in * in4 = reinterpret_cast< struct sockaddr_in * >(&this->addr); 
in4->laddr.sin_port = htons(port); 
... etc 
} 

Một kể từ khi bạn đang sử dụng C++ và không C cho rằng, việc sử dụng C++ phong cách phôi. C-style phôi trong C + + là xa để mơ hồ.

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