如何从C程序中输出主机的IP地址?

15

我需要使用C语言显示本地计算机的所有IP地址。如何实现?


15
"puts("127.0.0.1");"的意思是打印输出字符串"127.0.0.1",这只是个玩笑 :) - pmg
2
所有的IP地址是哪些?计算机分配的IP地址?您网络上的IP地址?此外,您正在使用哪个平台?C语言本身对IP一无所知。它必须进行操作系统调用,而这些调用可能在不同操作系统之间有所不同。 - David Thornley
6
已标记为“Linux”。 - anon
3
int i,j,k,l; for(i=0;i<256;++i) { for(j=0;j<256;++j) { for(k=0;k<256;++k) { for(l=0;l<256;++l) { printf("\n%d.%d.%d.%d",i,j,k,l); } } } }:) - Jacob
2
@Jacob: 他说的是所有IP地址,而不是所有IPv4地址;-) - Steve Jessop
8个回答

13
#include <stdio.h>
#include <stropts.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/netdevice.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

int print_addresses(const int domain)
{
  int s;
  struct ifconf ifconf;
  struct ifreq ifr[50];
  int ifs;
  int i;

  s = socket(domain, SOCK_STREAM, 0);
  if (s < 0) {
    perror("socket");
    return 0;
  }

  ifconf.ifc_buf = (char *) ifr;
  ifconf.ifc_len = sizeof ifr;

  if (ioctl(s, SIOCGIFCONF, &ifconf) == -1) {
    perror("ioctl");
    return 0;
  }

  ifs = ifconf.ifc_len / sizeof(ifr[0]);
  printf("interfaces = %d:\n", ifs);
  for (i = 0; i < ifs; i++) {
    char ip[INET_ADDRSTRLEN];
    struct sockaddr_in *s_in = (struct sockaddr_in *) &ifr[i].ifr_addr;

    if (!inet_ntop(domain, &s_in->sin_addr, ip, sizeof(ip))) {
      perror("inet_ntop");
      return 0;
    }

    printf("%s - %s\n", ifr[i].ifr_name, ip);
  }

  close(s);

  return 1;
}

int main(int argc, char *argv[])
{
  int domains[] = { AF_INET, AF_INET6 };
  int i;

  for (i = 0; i < sizeof(domains) / sizeof(domains[0]); i++)
    if (!print_addresses(domains[i]))
      return 1;

  return 0;
}

除了将所有IP地址与默认网关的IP地址进行比较之外,有什么更好的方法可以找出哪个接口是互联网连接的“默认”接口吗? - daisy
@warl0ck 在一般情况下,这可能会很棘手。你的问题可以作为一个独立的Stack Overflow问题来提出。 - Greg Bacon

10

你的问题可能不太精确,但我不确定为什么大家都在挑剔你。

我认为你正在询问基础知识,如果是这样,你可能想要使用getifaddrs函数。该手册页面有一个小示例程序。

您也可以使用ioctl()函数的SIOCGIFCONF选项来获取类似的信息。这里和网络上有示例代码

如果您搜索这些和类似术语,您会发现此问题以各种形式被问过。您需要深入挖掘一下。

还要注意,如果您在NAT后面,这些方法将无法提供您网络的公共IP地址。


9

在C语言中还有另一种方法。不过,我必须说......从shell中有很多方法可以做到这一点,那么意义何在呢?

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>

#include <errno.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>


void show_address_info( struct ifaddrs *ifa ){
  struct sockaddr_in *s4;
  struct sockaddr_in6 *s6;
  /* ipv6 addresses have to fit in this buffer */
  char buf[64];

  if (AF_INET == ifa->ifa_addr->sa_family){
    s4 = (struct sockaddr_in *)(ifa->ifa_addr);
    if (NULL == inet_ntop(ifa->ifa_addr->sa_family, (void *)&(s4->sin_addr), buf, sizeof(buf))){
      printf("%s: inet_ntop failed!\n", ifa->ifa_name);
    } else {
      printf("IPv4 addr %s: %s\n", ifa->ifa_name, buf);
    }
  }
  else if (AF_INET6 == ifa->ifa_addr->sa_family) {
    s6 = (struct sockaddr_in6 *)(ifa->ifa_addr);
    if (NULL == inet_ntop(ifa->ifa_addr->sa_family, (void *)&(s6->sin6_addr), buf, sizeof(buf))) {
      printf("%s: inet_ntop failed!\n", ifa->ifa_name);
    } else {
      printf("IPv6 addr %s: %s\n", ifa->ifa_name, buf);
      }
  }

}


int main(int argc, char **argv){
  struct ifaddrs *myaddrs, *ifa;
  int status;

  status = getifaddrs(&myaddrs);
  if (status != 0){
    perror("getifaddrs failed!");
    exit(1);
  }

  for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next){
    if (NULL == ifa->ifa_addr){
      continue;
    }
    if ((ifa->ifa_flags & IFF_UP) == 0) {
      continue;
    }
    show_address_info(ifa);
  }
  freeifaddrs(myaddrs);
  return 0;
}

在许多环境中,如共享库内部,生成一个 shell 不是一个选项。如果你使用 fork + exec,可能会导致与你的库链接的进程的 atexit 处理程序运行,这可能是灾难性的。此外,你认为那些 shell 实用程序是如何实现的呢?它们是用 C 实现的。 - cmccabe

5

你可以简单地作弊,查看/sbin/ifconfig/的源代码吗?借鉴他人的经验并不是什么坏事...


3

目前还不是一个完整的解决方案,但可以查看/proc/net

  • dev按名称列出可用的接口设备,
  • route列出一些路由,ipv6_route也是如此,
  • arp列出实际路由表中的设备(不包括本地主机)。

这个方法不像其他解决方案那么高级,但可以通过简单的文件读取来完成。但这只适用于Linux系统。


2

您需要使用POSIX函数getaddrinfo() - 它返回所有IP地址的链接列表。

有关详细信息和示例,请参见man getaddrinfo


-2
$ sudo ifconfig | grep 'inet addr' | cut -d':' -f2 | cut -d' ' -f1
213.xx.xxx.xx
192.168.xx.x
127.0.0.1

您可以将其放入popen()中:

/* not tested */
ph = popen("sudo ifconfig | grep 'inet addr' | cut -d':' -f2 | cut -d' ' -f1", "r");
while (fgets(buf, sizeof buf, ph)) {
    /* ip address, in nul-terminated string format, is in `buf` */
}
pclose(ph);

2
在C代码中使用shell命令是不可取的。我认为这样做并不可取。使用实际语言和系统/库调用的解决方案要好得多,特别是对于像这样的初学者问题。 - Steve Lazaridis

-3
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>

/*
 * Who sez?
 * http://blog.stackoverflow.com/2010/01/stack-overflow-where-we-hate-fun/
 */
int main(int argc, char *argv[])
{
  int status;
  const char * const cmd =  /* die from END is too chatty */
    "/sbin/ifconfig -a | \
     perl -lne \
       'print $1 if /inet6? addr:\\s*(\\S+)/; \
        END { $. > 0 or \
                warn(\"no output from ifconfig\\n\"), \
                exit 1; }'";

  status = system(cmd);
  if (status < 0) {
    perror("system");
    return 1;
  }
  else if (status != 0) {
    const char *extra;
    status = WEXITSTATUS(status);
    extra = status == 127 ? " (is /bin/sh ok?)" : "";
    fprintf(stderr, "%s: command failed with status %d%s\n",
            argv[0], status, extra);
  }

  return 0;
}

2
用C语言写Perl...这有点作弊 :P。而且完全不可移植。 - Macha
3
对于“-1 for being a wrapper around a higher language and other utility”的翻译:对于作为高级语言和其他实用工具的包装器而言,扣1分。 - PP.
@Macha 哪个Linux发行版不带有perl?我还给出了一个全C的答案。http://blog.stackoverflow.com/2010/01/stack-overflow-where-we-hate-fun/ - Greg Bacon
1
相较于已接受答案,+2 的代码更加简洁和易于维护。 - Christopher Bottoms
@GregBacon:很多很多!你想从OpenWrt开始吗? - user2284570

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接