Настройка IPv4 / IPv6 адреса и порта для структуры хранения sockaddr
Я портирую приложение IPv4 на AF-независимую кодовую базу(она должна работать с IPv4 и IPv6). Теперь я использую sockaddr_storage везде, где могу, однако теперь я должен установить(заполнить) sockaddr_storage. Но я не знаю, каков правильный путь. Предыдущий код был:
// defined in data_socket.h
struct sockaddr_in laddr;
Теперь есть эта функция, которая устанавливает sin_addr и sin_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");
}
Как вы видите, это старый стиль (использует IPv4).
Теперь мои изменения приведены ниже. Во-первых, я изменился.sockaddr_in
Чтобы sockaddr_storage
// defined in data_socket.h
struct sockaddr_storage laddr;
Затем я изменил код выше, чтобы поддерживать IPv4 и IPv6 :
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;
}
}
Где GetAddrFamily()
- это:
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: %sn", 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;
}
Кажется, мой путь-это комплекс. Правильно ли это делать? Поскольку я изменил sockaddr_in на sockaddr_storage, на самом деле я просто хочу обратного ответа на этот вопрос: получение IPV4-адреса из sockaddr структура
Я пытаюсь найти лучшее решение, например здесь: http://www.kame.net/newsletter/19980604/ он говорит, что никогда не используйте inet_ntop()
и inet_pton()
, однако некоторые другие(например, сетевой учебник Beej) говорят, что inet_ntop()
и inet_pton()
должны использоваться для IPv6.
Является ли мой способ реализации правильным или я должен его изменить?
2 ответа:
Я настоятельно рекомендую позволить
getaddrinfo
делать всю тяжелую работу, например.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); }
Если я правильно понял, вы бросаете
this
первому члену? не делайте этого, назовите имя участника.Я также облегчил бы чтение, введя область
{}
и локальную переменную для обоих случаев, что-то вроде:{ struct sockaddr_in * in4 = reinterpret_cast< struct sockaddr_in * >(&this->addr); in4->laddr.sin_port = htons(port); ... etc }
Поскольку вы используете C++, а не C для этого, используйте приведения в стиле C++. Приведения в стиле C в C++ далеко не однозначны.