Время подключения сокета с использованием getaddrinfo истекло

Я часами пытался выяснить свою проблему, просто нашел решение, когда писал свой вопрос (это всегда помогает, когда вам нужно формализовать свою проблему и объяснить ее). Выкладываю, надеюсь кому-то поможет.

Используя getaddrinfo, если я попытаюсь подключить сокет к моему серверу, делая (как я думал) именно то, что объясняется на множестве веб-сайтов, а также в примере кода страницы руководства getaddrinfo, это НЕ ПРОДАЕТСЯ с «время ожидания соединения истекло» сообщение об ошибке: (упрощение кода, чтобы быть более кратким)

void connect_UsingGetAddrInfo_Wrong (std::string host, unsigned short int port, int& socketfd)
{
    //simplified loops & error handling for concision
    int x;

    int domain = AF_INET;         // IP_v4
    int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams.  

    addrinfo hints, *addr;
    //fine-tune hints according to which socket you want to open
    hints.ai_family = domain; 
    hints.ai_socktype = socketType; 
    hints.ai_protocol = 0;           // no enum : possible value can be read in /etc/protocols
    hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;

    x =  getaddrinfo(hostname, NULL, &hints, &addr);
    //shall rather loop on addr linked list, but this is not the topic here.

    socketfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
    x = connect(socketfd, addr->ai_addr, addr->ai_addrlen);
}

Однако мне удалось подключить сокет к тому же серверу, используя метод gethostbyname.

void connect_UsingGetHostByName_Deprecated (std::string host, unsigned short int port, int& socketfd)
{
    //simplified loops & error handling for concision
    int x;

    int domain = AF_INET;         // IP_v4
    int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams.  

    struct hostent DNS, *r;
    char buf[1024];
    x = gethostbyname_r(hostname.c_str(), & DNS, buf, sizeof(buf), & r, & err));
    socketfd = socket(domain, socketType, 0);

    //server.
    sockaddr_in server;
    memset(&server, 0x00, sizeof(server));
    server.sin_family=domain;
    server.sin_port=htons(port);
    memcpy(& server.sin_addr.s_addr, DNS.h_addr, (size_t) DNS.h_length);
    x = connect(socketfd, (struct sockaddr *) & server, sizeof(server));
}

Работающий код показывает, что обе версии правильно получают действительный IP-адрес сервера. Тем не менее первый не будет подключаться и истечет время ожидания. Почему ?


person Pellekrino    schedule 20.06.2018    source источник
comment
Откуда берутся эти переменные x, domain, socketType, hostname, fd?   -  person Tormund Giantsbane    schedule 20.06.2018
comment
//упрощенные циклы и обработка ошибок для краткости. Я должен был добавить также объявление int и error var. Поскольку я хотел опубликовать 4 похожих фрагмента кода, я старался оставить только минимум, чтобы понять различия и сделать его кратким. Я мог бы наклеить все с трассами, лупами и ошибками, тут бы это не помогло. тогда позвольте мне исправить несколько объявлений   -  person Pellekrino    schedule 20.06.2018
comment
Наконец, я добавил прототип функции вопросов и ответов и объявление типа для каждой переменной (надеюсь, я не пропустил ни одного).   -  person Pellekrino    schedule 20.06.2018


Ответы (1)


Причина, по которой он продолжал терпеть неудачу, заключалась в следующем: чтобы получить адресную информацию, я оставил поле «сервис» равным NULL. Он все равно вернет успех и предоставит вам адрес (который вы можете сопоставить с getnameinfo с правильным IP-адресом). Тем не менее, адрес нельзя будет использовать для подключения вашего сокета!

Здесь я нашел гибридную версию обоих методов: https://codereview.stackexchange.com/questions/17863/socket-connect-realization-gethostbyname-or-getnameinfo Это работает, но я не покупаю кастинг

void connect_UsingGetAddrInfo_HYBRID (std::string host, unsigned short int port, int& socketfd)
{
    //simplified loops & error handling for concision
    int x;

    int domain = AF_INET;         // IP_v4
    int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams. 

    addrinfo hints, *addr;
    //fine-tune hints according to which socket you want to open
    hints.ai_family = domain; 
    hints.ai_socktype = socketType; 
    hints.ai_protocol = 0;           // no enum : possible value can be read in /etc/protocols
    hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;

    x =  getaddrinfo(host, NULL, &hints, &addr);
    socketfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);

    //here is the hybrid part
    sockaddr_in servAddr;
    memset(&servAddr, 0, sizeof(servAddr));
    servAddr.sin_family = addr->ai_family;
    servAddr.sin_addr.s_addr = *((uint32_t*) & (((sockaddr_in*)addr->ai_addr)->sin_addr));
    servAddr.sin_port        = htons(port);

    x=connect(socketfd, (struct sockaddr*) &servAddr, sizeof(servAddr));
}

В итоге это помогло мне найти первопричину:

void connect_UsingGetAddrInfo_FIXED (std::string host, unsigned short int port, int& socketfd)
{
    //simplified loops & error handling for concision
    int x;

    int domain = AF_INET;         // IP_v4
    int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams.  

    addrinfo hints, *addr;
    //fine-tune hints according to which socket you want to open
    hints.ai_family = domain; 
    hints.ai_socktype = socketType; 
    hints.ai_protocol = 0;           // no enum : possible value can be read in /etc/protocols
    hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;

    //Precise here the port !
    const char* service = std::to_string(port).c_str();

    x =  getaddrinfo(host, service, &hints, &addr);
    socketfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
    x = connect(socketfd, addr->ai_addr, addr->ai_addrlen);
}

Надеюсь, это поможет кому-то однажды!

person Pellekrino    schedule 20.06.2018