Получить локальный IP-адрес с сокетами Unix

0

Я программирую простой чат-клиент UDP-клиента, чтобы немного узнать о сетевом программировании. Теперь, чтобы облегчить пользователю просмотр IP-адресов серверов (чтобы пользователь мог ввести его в клиент для подключения). Поэтому я хотел бы, чтобы сервер отображал собственный локальный IP-адрес.

Это мой код: (отредактированный код для обработки нескольких IP-адресов)

// get the hostname of the server
char hostname[128];
if (gethostname(hostname, sizeof(hostname)) == -1) {
    std::cout << "Could not get the hostname of the server. Error: " << std::strerror(errno) << std::endl;
    return 1;
}

struct addrinfo hints, *serverInfo, *p;

std::memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; // use AF_INET (IPv4) or AF_INET6 (IPv6) to force version
hints.ai_socktype = SOCK_DGRAM;

// try to get information about our hostname
int rv;
if ((rv = getaddrinfo(hostname, NULL, &hints, &serverInfo)) != 0) {
    std::cout << "Failed to get information about the host \"" << hostname << "\". Error: " << gai_strerror(rv) << std::endl;
    return 1;
}

std::cout << "IP addresses for " << hostname << ":" << std::endl;

// iterate over all the infos we got and try to extract the IP address
for(p = serverInfo; p != NULL; p = p->ai_next) {
    char serverIPAddress[INET6_ADDRSTRLEN];
    void *addr;
    std::string ipVersion;

    // get the pointer to the address itself,
    // different fields in IPv4 and IPv6:
    if (p->ai_family == AF_INET) { // IPv4
        struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
        addr = &(ipv4->sin_addr);
        ipVersion = "IPv4";
    } else { // IPv6
        struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
        addr = &(ipv6->sin6_addr);
        ipVersion = "IPv6";
    }

    // take the IP address of our hostname and convert it into a readable format
    inet_ntop(p->ai_family, addr, serverIPAddress, sizeof(serverIPAddress));
    std::cout << "  " << ipVersion << ": " << serverIPAddress << std::endl;
}

freeaddrinfo(serverInfo); // free the linked list

Он работает, но не отображает правильный IP-адрес. Каждый раз, когда я запускаю его, отображается другой IP-адрес, и он не является правильным. Как я уже сказал, я все еще изучаю сетевое программирование, поэтому я не знаю, почему это так. Может ли кто-нибудь указать мне в правильном направлении?

Теги:
sockets
network-programming

5 ответов

0
Лучший ответ

Су, благодаря советам, которые я получил здесь, я изменил свой подход и вместо того, чтобы пытаться разрешить имя хоста моего компьютера на IP-адрес, теперь я просто перечисляю все сетевые интерфейсы и их IP-адреса.

Вот мой код:

// get the hostname of the server
char hostname[128];
if (gethostname(hostname, sizeof(hostname)) == -1) {
    std::cout << "Could not get the hostname of the server. Error: " << std::strerror(errno) << std::endl;
    return 1;
}

std::cout << "IP addresses for " << hostname << ":" << std::endl << std::endl;

struct ifaddrs *networkInterfaceList, *p;

getifaddrs (&networkInterfaceList); // get information about the network interfaces

// iterate over all the network interfaces we got and try to extract their IP address
for (p = networkInterfaceList; p != NULL; p = p->ifa_next) {
    char serverIPAddress[INET6_ADDRSTRLEN];
    void *addr;
    std::string ipVersion;

    // get the pointer to the address itself,
    // different fields in IPv4 and IPv6:
    if (p->ifa_addr->sa_family == AF_INET) { // IPv4
        struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ifa_addr;
        addr = &(ipv4->sin_addr);
        ipVersion = "IPv4";
    } else { // IPv6
        struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ifa_addr;
        addr = &(ipv6->sin6_addr);
        ipVersion = "IPv6";
    }

    // take the IP address of our hostname and convert it into a readable format
    inet_ntop(p->ifa_addr->sa_family, addr, serverIPAddress, sizeof(serverIPAddress));

    // sometime the IP address is empty so only print if there is one
    if (std::strlen(serverIPAddress) > 0)
        std::cout << "Interface: " << std::setw(6) << std::left << p->ifa_name << " " << ipVersion << ": " << serverIPAddress << std::endl;
}

freeifaddrs(networkInterfaceList); // free the linked list

На всякий случай кто-то пытается что-то подобное.

Также в случае, если кому-то интересно, это чат-репо: https://github.com/Foaly/Chat

0
// Get a Vector of the IP addresses of this computer
std::vector<std::string> getIPAddresses() {

    std::vector<std::string> IPAddresses;
    int TempAddress = 0;
    char* TempIPAddress = (char*) malloc(16);

    ifaddrs* ifap = NULL;   //Head of the interface address linked list

    if (getifaddrs(&ifap) == 0 && ifap != NULL) {

        //Get another pointer to move down the linked list
        ifaddrs* current = ifap;

        //Move down the linked list until we get to a NULL pointer
        while (current != NULL) {

            //Create a pointer of the correct type to work with
            const sockaddr_in* interfaceAddress = reinterpret_cast<const sockaddr_in*>(current->ifa_addr);

            if (current->ifa_addr != NULL) {
                if (current->ifa_addr->sa_family == AF_INET) {
                    //printf("%s:", current->ifa_name);
                    if (interfaceAddress != NULL) {
                        TempAddress = ntohl(interfaceAddress->sin_addr.s_addr);
                        sprintf(TempIPAddress, "%d.%d.%d.%d", (TempAddress >> 24) & 0xFF, (TempAddress >> 16) & 0xFF,
                               (TempAddress >> 8) & 0xFF, TempAddress & 0xFF);
                        //Don't include the lookback address
                        if (strcmp(TempIPAddress, "127.0.0.1") != 0) {
                            IPAddresses.push_back(std::string(TempIPAddress));
                        }
                        //printf("%s\n", TempIPAddress);
                    }
                }
            }

            //Move to the next node in the linked-list
            current = current->ifa_next;
        }

        //Release the interface memory
        freeifaddrs(ifap);
        ifap = NULL;
    }

    return IPAddresses;

}
0

В Windows вы можете использовать gethostbyname и перечислить через hostent h_addr_list... на Linux вы можете создать временную розетку и использовать ioctl на SIOCGIFCONF и перечислить через интерфейсы.

  • 0
    Вы должны быть осторожны при использовании gethostbyname() таким образом. Есть способы, которыми он может вернуть неправильные результаты. GetAdaptersInfo() лучше использовать GetAdaptersInfo() или GetAdaptersAddresses() . В Linux используйте вместо этого getifaddrs() . SIOCGIFCONF поддерживает только IPv4, тогда как getifaddrs() поддерживает как IPv4, так и IPv6.
  • 0
    Спасибо за комментарий. В каких ситуациях я получу неправильные результаты с gethostbyname ? Интересно, стоит ли мне пересматривать код там ...
Показать ещё 1 комментарий
0

Я думаю, что в конце вашего кода вы неправильно использовали структуру данных sockaddr_in. Линия:

inet_ntop(AF_INET, &(serverInfo->ai_addr), serverIPAddress, INET_ADDRSTRLEN);

следует заменить на:

struct sockaddr_in *ipv4 = (struct sockaddr_in *)serverInfo->ai_addr;
void *addr = &(ipv4->sin_addr);
inet_ntop(AF_INET, addr, serverIPAddress, INET_ADDRSTRLEN);
  • 0
    На самом деле! Это имеет смысл. Я адаптировал свой код для правильной обработки, а также для обработки нескольких addrinfo , возвращаемых из getaddrinfo . Но единственное, что я получаю, это 127.0.1.1 (что не является моей петлей), но не мой локальный IP-адрес.
0

Вы не сможете получить свой "глобальный" IP-адрес, как этот, только локальный. Чтобы клиент мог подключиться к вам, ему нужен глобальный IP-адрес, который отображается в Интернете, то есть IP-адрес вашего шлюза (маршрутизатора). Это связано с тем, что ваш маршрутизатор переводит IP-адрес вашего компьютера в глобально видимый и сохраняет его в таблице, так что входящие соединения могут быть правильно перенаправлены в правильную подсетей. Это называется NATing (подробнее об NAT здесь).

Если вы хотите, чтобы клиент явно подключался к вам, вам нужно указать им свой IP-адрес маршрутизатора (используйте для этого средства командной строки, в зависимости от вашей ОС) и настройте переадресацию портов, чтобы маршрутизатор автоматически перенаправлял поступающие пакеты (IP_router, port_router) до (IP_host, port_host). Это делается на панели конфигурации маршрутизатора. В зависимости от вашего маршрутизатора это будет найдено где-нибудь в меню. Вы можете подключиться к маршрутизатору, набрав что-то вроде 192.068.0.1 или 192.168.0.0.

  • 0
    Спасибо за быстрый ответ! Но, как я уже говорил в первоначальном вопросе, я хочу получить только мой локальный IP-адрес. Клиент чата действительно очень прост и предназначен только для работы в локальной сети (например, в моих университетах или в моей домашней сети).
  • 0
    ХОРОШО. Если это только локально, это можно сделать несколькими способами. Ваш клиент может транслировать пакет обнаружения UDP в сети, ориентируясь на определенный порт вашего клиента (например, 987654), сервер, прослушивающий какую-либо машину на этом порту, установит TCP-соединение, используя источник пакета обнаружения (IP, порт) Информация. Другой способ - просто использовать ipconfig для поиска вашего IP-адреса, если вы не хотите проходить через все вышеперечисленное.
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню