如何使用套接字地址获取自己的IP地址?

如何使用套接字地址获取自己的IP地址?

问题描述:

我想获取启动我的程序的计算机的IP地址,然后将其发送到客户端,但我总是得到0.0.0.1而不是真正的IP地址(例如127.0.0.1) 。如何使用套接字地址获取自己的IP地址?

我目前能够获取端口,但不是IP地址。

我怎样才能得到它呢?

最好的解决方案是能够得到它sockaddr_in。下面是目前我在做什么:

int open_connection(char* ip, int* port) 
{ 
    int     sock; 
    struct sockaddr_in sin; 
    socklen_t    len; 
    int     i; 

    i = 0; 
    len = sizeof(sin); 
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
    return (-1); 
    bzero(&sin, sizeof(struct sockaddr_in)); 
    sin.sin_family = AF_INET; 
    if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) 
    perror("Error on bind"); 
    if (getsockname(sock, (struct sockaddr *)&sin, &len) != 0) 
    perror("Error on getsockname"); 
    strcpy(ip, inet_ntoa(sin.sin_addr)); // IP = 0.0.0.0 
    *port = sin.sin_port; 
    return (sock); 
} 

编辑:我知道我会与我的想法错了。所以我的问题是:什么是获得自己的IP地址的最佳方式?

+1

如果客户跟你说话......他已经有自己的IP地址。我很困惑你为什么需要明确地发送它。 – Pyrce 2013-04-10 00:22:23

+1

@Pyrce我正在做一个FTP服务器,并进入被动模式时,我需要发送的IP地址和服务器侦听端口。即使是同一个IP地址... – 2013-04-10 00:25:16

+2

也许这老题? http://*.com/questions/212528/get-the-ip-address-of-the-machine我认为绑定到0.0.0.0是一个特定的事情,但是,这意味着绑定所有IP地址 - 我怀疑你倒是让别的了绑定的,因为这就是你的过去。 – Rup 2013-04-10 00:28:48

当您bind()套接字为0.0.0.0时,这是套接字在调用getsockname()时唯一可用的IP。这意味着套接字绑定到所有本地接口。为了从套接字获取特定的IP,它必须绑定到特定的IP。

无论如何,使用套接字API获取机器的本地IP地址是错误的方法。一个常见的错误是使用gethostname()gethostbyname()getaddrinfo()来获取本地IP列表。通常这是有效的,但它有一些隐藏的陷阱可能会导致错误的信息,但人们往往忽略这个事实,或者甚至不知道它(首先我不知道它,但后来我学得更好)。

相反,您确实应该使用平台特定的API来枚举本地网络接口。这将提供更可靠的信息。 Windows有GetAdaptersInfo()GetAdaptersAddresses()。其他平台有getifaddrs()。那些会告诉你哪些本地IP可用。然后,您可以将bind()套接字设置为0.0.0.0,以便接受任何这些IP上的客户端,或者将bind()接收到特定的IP,以仅接受该IP上的客户端。

+0

请详细说明getaddrinfo()中的“* ... hidden gotchas ... *”吗? – alk 2013-04-10 06:20:49

+0

'gethostbyname()'和'getaddrinfo()'旨在使用DNS查找将主机名解析为IP。他们并不是要检索有关localhost的信息。 a)用户可能改变/损坏操作系统的DNS缓存(hosts文件等),b)PC的主机名被配置为根本没有IP或映射到可能不属于所有IP的多个IP到PC(思考负载平衡等)。在所有设置中传递本地主机名时,这些函数不能100%保证返回有效的本地IP地址。像'GetAdaptersInfo()'和'getifsaddrs()'这样的函数就是为此而设计的。 – 2013-04-10 08:31:28

+0

哦,是的,**外部** DNS涉及到的是你基本上所指的。我同意这一点。谢谢澄清。 – alk 2013-04-10 08:41:10

套接字API允许您枚举分配给您的网络接口的IP地址,但如果您从路由器后面连接到Internet,它不会告诉您“真实IP”是什么。

要知道它的唯一方法是通过询问外面的人。多数民众赞成在如FileZilla FTP服务器这样的服务器那样做。它们会指示您在服务器设置中将URL配置为“ip.php”脚本,如this one,以便它可以询问Internet的什么是公共IP地址,以便在被动模式下使用。

您也可以考虑使用STUN,一种在VoIP中广泛使用的协议来发现公共IP。

+1

+1。如果OP需要更多选项,我个人喜欢[ifconfig.me](http://ifconfig.me)和[canhazip.com](http://canhazip.com)。 – jweyrich 2013-04-10 00:42:37

+0

谢谢,这不符合我目前的需求(这是一个学校项目,并不意味着从互联网上工作)。我会看看我是否有时间来实施它。 – 2013-04-10 00:44:57

你可以调用的ioctl(袜子,SIOCGIFADDR,ADR)

看到netdevice(7)

@Remy Lebeau的回答我写返回当前机器的地址的功能。我只在macOS High Sierra上测试过。

interfaec可以跻身lo0什么,en0

ipVersion可以AF_INETAF_INET6

long int getInternalAddress(char* interface, sa_family_t ipVersion) 
{ 
    struct ifaddrs *ifaddrHead, *ifaddr; 
    /* int_8 */ 
    sa_family_t family; 
    int n; 
    char *interfaceName; 

    if (getifaddrs(&ifaddrHead) != 0) 
    { 
    fprintf(stderr, "ifaddrs error"); 
    } 

    /* iterate through address list */ 
    for (ifaddr = ifaddrHead, n = 0; ifaddr != NULL; ifaddr = ifaddr->ifa_next, n++) 
    { 
    family = ifaddr->ifa_addr->sa_family; 
    interfaceName = ifaddr->ifa_name; 

    if (!family || family != ipVersion || strcmp(interfaceName, interface)) continue; 

    struct sockaddr *addr = ifaddr->ifa_addr; 
    struct sockaddr_in* addr_in = (struct sockaddr_in*) addr; 
    long int address = addr_in->sin_addr.s_addr; 

    freeifaddrs(ifaddrHead); 

    return address; 
    } 

    freeifaddrs(ifaddrHead); 

    return 0; 
} 

要使用它,

int main() 
{ 
    long int address = getInternalAddress((char*) &"en0", AF_INET); 
    printf("%li\n", address); 

    return 0; 
} 

我仍然在C初学者,如果有什么错误,请告诉我。