Получил локальное имя хоста, работает в Windows, но не в Linux

Я написал программу для получения имени локального хоста. если это не очевидно, я имею в виду получить имя хоста локальной машины, аналогичное методу gethostname, а не получить строку localhost

Я использую getaddrinfo с NULL для имени хоста, а затем звоню getnameinfo с первым адресом.

Он отлично работает на компьютерах с Windows и дает мне каноническое имя хоста, но в Linux он не может дать мне локальное имя хоста и дает мне :: для IPv6 или 0.0.0.0 для IPv4.

Обратите внимание, что я использую флаг AI_PASSIVE в getaddrinfo, что означает, что он дает мне не более двух адресов.

В getnameinfo, если я использую NI_NAMEREQD, произойдет сбой.

Я делаю это, потому что у меня есть сервер, иногда я хочу слушать все интерфейсы, а иногда и определенный интерфейс, у меня уже есть addrinfo, и я просто хочу получить от него имя хоста, если это возможно.

Как я могу заставить его работать в Linux? я ничего не пропустил?

Спасибо за любую помощь.

Вот мой код: (кросс-платформенный, Windows и Linux, просто скопируйте и вставьте). Вы можете скомпилировать и запустить на обоих и увидеть разницу.

#include <iostream> 

#ifdef WIN32

#ifdef UNICODE
#undef UNICODE /* don't want Unicode, to keep code compatibility */
#endif

#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
class CWSAInitializer{
public:
    CWSAInitializer(){
        WSADATA data;
        WSAStartup(MAKEWORD(2,2),&data);
    }
    ~CWSAInitializer(){
        WSACleanup();
    }
}autoInit;

#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#endif

const char* get_hostname(struct addrinfo* info, char buff[], int buff_len)
{
    int addrlen = (int)info->ai_addrlen;
    int res = getnameinfo(info->ai_addr, addrlen, buff, buff_len, NULL, 0, 0);
    if(res){
        return NULL;
    }
    return buff;
}

int main () 
{
    char temp[100];
    addrinfo *ai,hints;
    memset(&hints,0,sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_NUMERICSERV;
    hints.ai_flags |= AI_PASSIVE;

    int res = getaddrinfo(NULL, "0", &hints, &ai); 
    if(res){
        std::cerr<<gai_strerror(res)<<std::endl;
        return -1;
    }
    std::cout<< get_hostname(ai,temp,100)<<std::endl;
    freeaddrinfo(ai);

    return 0;
}

ИЗМЕНИТЬ

  1. Сервер также должен принимать клиентов с других машин, поэтому он не может сообщить о прослушивании :: или 0.0.0.0.

  2. Я знаю, что могу исправить это следующим образом:

if(std::string("::") == hostname || std::string("0.0.0.0") == hostname) gethostname(hostname,100)

  1. Я не хочу получать в результате localhost, если только адрес не 127.0.0.1 или ::1.

person SHR    schedule 02.12.2013    source источник
comment
Если вам нужно ТОЛЬКО локальное имя хоста, почему бы вам не использовать INADDR_LOOPBACK?   -  person ciphermagi    schedule 02.12.2013
comment
Мне не нужен локальный интерфейс. сервер слушает снаружи, я хочу знать, какое имя хоста я сейчас слушаю. Если клиент попытается подключиться к :: или к 0.0.0.0, он не подключится.   -  person SHR    schedule 02.12.2013


Ответы (1)


getaddrinfo() возвращает начало списка узлов struct addrinfo.

Просматривает этот список, используя struct addrinfo член ai_next, чтобы найти адреса всех интерфейсов машины.

Чтобы получить что-либо, кроме адресов с подстановочными знаками, не указывайте флаг AI_PASSIVE в передаваемых подсказках.


Обновление различных имен, присвоенных машине, и/или адресов ее интерфейсов:

  1. Имя хоста ("hostname"), возвращаемое gethostname(), является атрибутом хоста. Он настраивается на хосте и не хранится ни в какой внешней базе данных. Это имя хоста по определению не связано ни с одним из имен, на которые разрешаются адреса возможных интерфейсов. Однако имя хоста может быть настроено так, чтобы оно соответствовало имени, в которое разрешается один из адресов (интерфейсов машины).

  2. Может быть более одного интерфейса, предоставляемого машиной. Адрес каждого из этих интерфейсов должен разрешаться в другое имя, которое, в свою очередь, также может отличаться от имени, возвращаемого gethostname() (см. 1. выше). Это относится ко всем типам интерфейсов, включая IPv4 и IPv6. Также это виртуальный групповой адрес (0.0.0.0для IPv4), который позволяет программам привязываться и прослушивать все интерфейсы (здесь указан IPv4), предоставляемые машиной. Адрес подстановочного знака не имеет нет имени.

Вывод из 1. и 2. таков: Нет имени "... как [возвращено] gethostname(), но каноническое."!

person alk    schedule 02.12.2013
comment
И в Windows, и в Linux у меня есть два адреса в списке: один дает мне ::, другой 0.0.0.0 я использовал AI_PASSIVE намеренно. Я не хочу получать localhost Я хочу получить каноническое имя хоста. - person SHR; 02.12.2013
comment
Итак, если я правильно понял, если я хочу, чтобы это работало в Linux, если я слушаю ЛЮБОЙ интерфейс (задал NULL для 1-го параметра getaddrinfo), мне нужно использовать gethostname или использовать другой getaddrinfo без флага AI_PASSIVE. может действительно другого выхода нет. - person SHR; 02.12.2013
comment
Это нехорошо, это дает мне имя localhost. Мне нужно THE имя машины. как gethostname, но канонично. - person SHR; 02.12.2013
comment
этого не существует, не так ли? каждый адрес имеет свое имя, и на одной машине может быть много сетевых карт и много адресов. - person andrew cooke; 03.12.2013