C++ 使用unix套接字获取本地ip地址

C++ 使用unix套接字获取本地ip地址,c++,sockets,unix,network-programming,C++,Sockets,Unix,Network Programming,我正在编写一个简单的UDP客户端-服务器聊天程序,以便对网络编程更加熟悉。现在,让用户更容易看到服务器的IP地址是什么,这样用户就可以将其键入客户端进行连接。这就是为什么我希望服务器显示自己的本地IP地址 这是我的代码:用于处理多个IP的已编辑代码 它可以工作,但是它不能显示正确的IP地址。每次我运行它都会显示一个不同的IP,它不是正确的IP。正如我所说,我仍在学习网络编程,所以我不知道这是为什么。有人能告诉我正确的方向吗?您将无法获得这样的全局IP地址,只有本地IP地址。为了让客户端连接到您,

我正在编写一个简单的UDP客户端-服务器聊天程序,以便对网络编程更加熟悉。现在,让用户更容易看到服务器的IP地址是什么,这样用户就可以将其键入客户端进行连接。这就是为什么我希望服务器显示自己的本地IP地址

这是我的代码:用于处理多个IP的已编辑代码


它可以工作,但是它不能显示正确的IP地址。每次我运行它都会显示一个不同的IP,它不是正确的IP。正如我所说,我仍在学习网络编程,所以我不知道这是为什么。有人能告诉我正确的方向吗?

您将无法获得这样的全局IP地址,只有本地IP地址。为了让客户端连接到您,它需要全局IP地址,该地址可以从Internet上看到,即网关路由器的IP。这是因为路由器将计算机的IP地址转换为全局可见的IP地址,并将其存储在表中,以便将传入连接正确路由到正确的子网络。这叫做NATing了解更多关于NAT的信息


如果您希望客户端显式连接到您,则需要为其提供路由器的IP地址。根据您的操作系统,使用命令行工具进行此操作,并配置端口转发,以便路由器将自动将到达IP_路由器、端口_路由器的数据包转发到IP_主机、端口_主机。这是在路由器的配置面板中完成的。根据您的路由器,这将在菜单中的某个位置找到。您可以通过键入192.068.0.1或192.168.0.0之类的内容连接到路由器。

在Windows上,您可以使用gethostbyname并通过hostent h\u addr\u列表进行枚举。。。在Linux上,您可以创建一个临时套接字,并在SIOCGIFCONF上使用ioctl,然后通过接口进行枚举。

我认为在代码末尾,您误用了中的数据结构sockaddr\u。
// 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;

}
该行:

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

Soo,多亏了我在这里得到的建议,我改变了我的方法,不再试图将我机器的主机名解析为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
以防万一别人也在尝试类似的事情


如果有人感兴趣,这里还有聊天室回复:

谢谢您的快速回答!但正如我在原始问题中所说,我只想获取我的本地IP地址。聊天客户端非常简单,只应在本地网络中工作,如我的大学或我的家庭网络。好的。如果只是本地的,可以通过几种方式实现。您的客户端可以针对您客户端的特定端口在网络上广播UDP发现数据包,例如987654,在该端口上侦听某台计算机的服务器将使用发现数据包的源IP、端口信息建立TCP连接。另一种方法是简单地使用ipconfig来查找你的IP地址,如果你不想重复以上所有内容的话。因此,当我打开服务器时,它会说:欢迎!在IP:x.x.x.x下可以访问此服务器,然后我可以转到另一台机器,打开一个客户端并在中键入IP地址。它应该是一种方便的功能,因此不必每次启动服务器时都调用ipconfig。例如,我的大学PC没有安装ipconfig……这通常是不可能的,因为您的计算机可能有多个网络接口。要枚举您的网络接口,请使用上面myk发布的算法。然后你可以滚动浏览网络接口,选择你知道的是你的网卡。我想我会列出所有的网络接口。出于好奇,如果有多个接口,getaddrinfo不应该返回列表吗?的确!这是有道理的。我修改了我的代码以正确地处理它,并处理从getaddrinfo返回的多个addrinfo。但是我得到的唯一东西是127.0.1.1,它不是我的环回,但不是我的本地IP地址。它有可能返回错误的结果。最好改用GetAdapterInfo或GetAdapterAddresses。在Linux上,改用GetIFADRS。SIOCGIFCONF仅支持IPv4,而GetIFADRS同时支持IPv4和IPv6。感谢您的评论。在什么情况下,我会使用gethostbyname得到错误的结果?不知道我是否有理由检查那里的代码…使用gethostname和gethostbyname可能并不总是只返回机器的IP地址。从技术上讲,它们将返回本地主机名,然后返回该主机名解析到的地址。一台机器可以配置为其名称不解析为IP,也可以配置为其名称解析为多个IP,其中只有一个IP是IP 渴望本地机器。使用直接枚举本地网络接口的特定于平台的API,如GetAdapterInfo |地址和getifaddrs,可以避免这种陷阱。
// 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