第一种:获取指定网卡名称的ipv4地址
这种方法通过调用Linux提供的应用接口函数:
getifaddrs、getnameinfo,遍历设备所有网卡获取相关配置信息;getnameinfo函数获取本地网卡ip地址时,
有一个bug:
获取到的ip地址可能被内环地址覆盖,而获取不到真正的ip地址。
例子:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>
#include <termios.h>
#include <ifaddrs.h>
char* hostname_to_ip(char * hostname );
int main(int agrc,char *agrv[])
{
char *data;
data = hostname_to_ip(agrv[1]);
if(NULL == data)
return 0;
printf("%s\n",data);
free(data);
return 0;
}
char* hostname_to_ip(char * hostname ) //获取hostname对应的ipv4地址//
{
struct ifaddrs *ifaddr, *ifa;
int family, s;
char *host = (char*)malloc(sizeof(char)*1024);
memset(host,0,sizeof(char)*1024);
if (getifaddrs(&ifaddr) == -1)
{
// perror("getifaddrs");
//exit(EXIT_FAILURE);
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
continue;
s=getnameinfo(ifa->ifa_addr,sizeof(struct sockaddr_in),host, 1024, NULL, 0, 10);
if((strcmp(ifa->ifa_name,hostname)==0)&&(ifa->ifa_addr->sa_family==AF_INET))
{
if (s != 0)
{
// printf("getnameinfo() failed: %s\n", gai_strerror(s));
//exit(EXIT_FAILURE);
}
freeifaddrs(ifaddr);
return host;
}
}
}
出现bug的原因:
当套接字被绑定到一个未指定的地址时,getsockname函数并不总是返回关于主机地址的信息,除非套接字已经连接到connect或accept(例如,使用ADDR_ANY)。
除非套接字已连接,否则Windows套接字应用程序不能假定将指定该地址。
将用于套接字的地址是未知的,除非套接字在多主机中使用时是连接的。
如果套接字使用无连接协议,则在套接字上发生I/O之前,地址可能不可用。
/
第二种:建立socket对象,利用socket对象扫描出设备可用网卡
涉及重要的结构体:
struct ifreq、struct ifconf。这里将避开第一种方案带来的问题。
例子:
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int get_local_ip(char *ip,char *dev)
{
int fd, intrface, retn = 0;
struct ifreq buf[INET_ADDRSTRLEN]; //这个结构定义在/usr/include/net/if.h,用来配置和获取ip地址,掩码,MTU等接口信息的
struct ifconf ifc;
/*1 建立socket链接,利用ioctl来自动扫描可用网卡*/
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)
{
ifc.ifc_len = sizeof(buf);
// caddr_t,linux内核源码里定义的:typedef void *caddr_t;
ifc.ifc_buf = (caddr_t)buf;
if (!ioctl(fd, SIOCGIFCONF, (char *)&ifc)) /*2 这里获取到可用网卡清单,包括网卡ip地址,mac地址*/
{
intrface = ifc.ifc_len/sizeof(struct ifreq); //计算出有效网卡的数量//
while (intrface-- > 0)
{
if (!(ioctl(fd, SIOCGIFADDR, (char *)&buf[intrface]))) /*3 遍历并索引指定网卡的地址*/
{
if(strcmp(buf[intrface].ifr_ifrn.ifrn_name,dev) == 0)
{
ip=(inet_ntoa(((struct sockaddr_in*)(&buf[intrface].ifr_addr))->sin_addr));
printf("IP:%s\n", ip);
}
}
}
}
close(fd);
return 0;
}
}
int main(int agrc,char *agrv[])
{
char ip[64];
memset(ip, 0, sizeof(ip));
get_local_ip(ip,agrv[1]); //执行时:./a.out ethx//
return 0;
}
对结构体的详解:
|
struct ifconf
{
int ifc_len; /* Size of buffer. */
union
{
__caddr_t ifcu_buf;
struct ifreq *ifcu_req;
} ifc_ifcu;
};
//通常用来保存所有的接口信息
|
第三种,通过函数
inet_ntop,获取到网卡信息,类似于第一种方式,换了个函数。
例子:
#include <stdio.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
int get_local_ip(char *ip) {
struct ifaddrs *ifAddrStruct;
void *tmpAddrPtr=NULL;
getifaddrs(&ifAddrStruct);
while (ifAddrStruct != NULL) {
if (ifAddrStruct->ifa_addr->sa_family==AF_INET) {
tmpAddrPtr=&((struct sockaddr_in *)ifAddrStruct->ifa_addr)->sin_addr;
inet_ntop(AF_INET, tmpAddrPtr, ip, INET_ADDRSTRLEN);
printf("%s IP Address:%s\n", ifAddrStruct->ifa_name, ip);
}
ifAddrStruct=ifAddrStruct->ifa_next;
}
//free ifaddrs
freeifaddrs(ifAddrStruct);
return 0;
}
int main()
{
char ip[16];
memset(ip, 0, sizeof(ip));
get_local_ip(ip);
return 0;
}
注意:
和第一种方式一样,如果设备网卡只是被分配到ip地址而为发生任何IO通信,将被认为无效网卡!!!