简体   繁体   中英

Get the local ip address with unix sockets

I am programming a simple UDP client server chat to get a little more familiar with network programming. Now to make it easier for the user to see what the servers IP address is (so the user can type it into the client to connect). That's why I would like the server to display its own local IP address.

This is my code: (edited code to handle multiple IP's)

// 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

It works, but it doesn't display the correct IP address. Everytime I run it there is a different IP being displayed and it's not the correct one. As I said I am still learning about network programming, so I don't know why that is. Could somebody point me in the right direction?

You will not be able to get your "global" IP address like this, only the local one. In order for the client to connect to you, it needs the global IP address, which is visible from the Internet, ie your gateway's (router's) IP. This is because your router translates your computer's IP address to a globally visible one and stores it in a table, so that incoming connections can be correctly routed to the correct sub-network. This is called NAT ing (find out more about NAT here ).

If you want a client to connect to you explicitly , you need to give them your router's IP address (use command-line tools for this, depending on your OS) and configure port-forwarding so that the router will automatically forward the packets arriving on (IP_router, port_router) to (IP_host, port_host). This is done in the router's config panel. Depending on your router, this will be found somewhere in the menus. You can connect to your router by typing something like 192.068.0.1 , or 192.168.0.0 .

在Windows中可以使用gethostbyname并通过枚举hostent h_addr_list ......在Linux上,你可以创建一个临时的窝,并使用ioctlSIOCGIFCONF并通过接口枚举。

// 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;

}

I think that at the end of your code, you have misused the data structure sockaddr_in. The line:

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

should be replaced with:

struct sockaddr_in *ipv4 = (struct sockaddr_in *)serverInfo->ai_addr;
void *addr = &(ipv4->sin_addr);
inet_ntop(AF_INET, addr, serverIPAddress, INET_ADDRSTRLEN);

Soo, thanks to the advice I got here, I changed my approach and instead of trying to resolve the hostname of my machine to an IP address, I am now simply listing all the network interfaces and their IP addresses.

Here is my code:

// 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

Just in case somebody else is trying something similar.

Also in case somebody is interested here is the chat repo: https://github.com/Foaly/Chat

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM