Linux下编程获取本地IP地址
在进⾏⽹络编程时,经常⽤到本机IP地址。本⽂罗列⼀下常见⽅法,以备不时之需。
获取本机IP地址,是⼀个相当灵活的操作,原因是⽹络地址的设置⾮常灵活⽽且都是允许⽤户进⾏个性化设置的。⽐如⼀台计算机上可以有多块物理⽹卡或者虚拟⽹卡,⼀个⽹卡上可以绑定多个IP地址,⽤户可以为⽹卡设置别名,可以重命名⽹卡。⽤户计算机所在⽹络拓扑结构未知,主机名设置是⼀个可选项,并且同样可以为⼀个计算机绑定多个主机名等,这些信息都会有影响。脱离了⽹络连接,单独的⽹络地址没有任何意义。编程中遇到必须获取计算机IP的场景,应该考虑将这⼀选项放到配置⽂件中,由⽤户⾃⼰来设置。参考⽹络和书本,编程获取本机IP地址⼤约有以下⼏种⽅法。
⽅法⼀:ioctl()获取本地IP地址 Linux 下 可以使⽤ioctl()函数以及结构体 struct ifreq和结构体struct ifconf来获取⽹络接⼝的各种信息。具体过程是先通过ictol获取本地的所有接⼝信息,存放到ifconf结构中,再从其中取出每个ifreq表⽰的ip信息(⼀般每个⽹卡对应⼀个IP地址,如:”eth0…、eth1…”)。
先了解结构体 struct ifreq和结构体struct ifconf:
//ifconf通常是⽤来保存所有接⼝信息的//if.h
struct ifconf {
int ifc_len; /* size of buffer */ union {
char *ifcu_buf; /*input from user->kernel*/
struct ifreq *ifcu_req; /* return from kernel->user*/ } ifc_ifcu;};
#define ifc_buf ifc_ifcu.ifcu_buf /*buffer address */#define ifc_req ifc_ifcu.ifcu_req /*array of structures*///ifreq⽤来保存某个接⼝的信息//if.h
struct ifreq {
char ifr_name[IFNAMSIZ]; union {
struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; struct sockaddr ifru_broadaddr; short ifru_flags; int ifru_metric; caddr_t ifru_data; } ifr_ifru;};
#define ifr_addr ifr_ifru.ifru_addr
#define ifr_dstaddr ifr_ifru.ifru_dstaddr
#define ifr_broadaddr ifr_ifru.ifru_broadaddr
如果本机的IP地址绑定在第⼀块⽹卡上,指定⽹卡名称,⽆需获取所有⽹卡的信息,即可获取,见如下函数:
string getLocalIP(){ int inet_sock; struct ifreq ifr; char ip[32]={NULL};
inet_sock = socket(AF_INET, SOCK_DGRAM, 0); strcpy(ifr.ifr_name, \"eth0\");
ioctl(inet_sock, SIOCGIFADDR, &ifr);
strcpy(ip, inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr)); return string(ip);}
如果想获取所有⽹络接⼝信息,参见如下代码:
#include #include #include #include #include #include #include #include int main(int argc,char* argv[]){
int sockfd;
struct ifconf ifconf;
struct ifreq *ifreq;
char buf[512];//缓冲区 //初始化ifconf
ifconf.ifc_len =512; ifconf.ifc_buf = buf;
if ((sockfd =socket(AF_INET,SOCK_DGRAM,0))<0) {
perror(\"socket\" ); exit(1); }
ioctl(sockfd, SIOCGIFCONF, &ifconf); //获取所有接⼝信息 //接下来⼀个⼀个的获取IP地址 ifreq = (struct ifreq*)ifconf.ifc_buf;
printf(\"ifconf.ifc_len:%d\\n\",ifconf.ifc_len);
printf(\"sizeof (struct ifreq):%d\\n\",sizeof (struct ifreq));
for (int i=(ifconf.ifc_len/sizeof (struct ifreq)); i>0; i--) {
if(ifreq->ifr_flags == AF_INET){ //for ipv4 printf(\"name =[%s]\\n\" , ifreq->ifr_name);
printf(\"local addr = [%s]\\n\" ,inet_ntoa(((struct sockaddr_in*)&(ifreq->ifr_addr))->sin_addr)); ifreq++; } }
getchar();//system(\"pause\");//not used in linux return 0;}
运⾏输出:
⽅法⼆:getsockname()获取本地IP地址 如果建⽴TCP连接的情况下,可以通过getsockname和getpeername函数来获取本地和对端的IP和端⼝号。前提是已经与对⽅建⽴了连接。 参考代码如下:
#include #include #include #include int main(int argc, char* argv[]){
int fd=socket(AF_INET,SOCK_STREAM,0);//创建本地sock描述符 struct sockaddr_in servaddr,localaddr,peeraddr; socklen_t len;
//初始化服务端地址并连接
bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(PORT);//PORT⾃⼰指定 char* servIP=”177.56.23.4”;//服务端IP
inet_pton(AF_INET,servIP,&servaddr.sin_addr);
if(connect(fd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0) {
cerr<<\"connect error\"<char buf[30]=\"\";bzero(&localaddr,sizeof(localaddr));
getsockname(fd,(struct sockaddr*)&localaddr,&len); //获取本地信息
cout<<\"local ip is \"<getpeername(fd,(struct sockaddr*)&peeraddr,&len); //获取对端信息cout<<\"peer ip is \"<< inet_ntop(AF_INET,&peeraddr.sin_addr,buf,sizeof(buf))<<\"peer port is \"<下⾯两种⽅法,都是通过主机名称来获取主机的IP地址,在获取本地IP地址时,⼀般都是回环地址,但可以有效的根据主机名称获取⽹络中的主机的IP地址,如通过域名获取域名对应的IP地址。要想精确的获取某块⽹卡绑定的IP地址,请根据ioctl()和接⼝名称(如eth0)来获取,具体实现见上⽂。
⽅法三:getaddrinfo()获取本地IP地址 注意,getaddrinfo()可以完成⽹络主机中主机名和服务名到地址的映射,但是⼀般不能⽤来获取本地IP地址,当它⽤来获取本地IP地址时,返回的⼀般是127.0.0.1本地回环地址。 所需头⽂件:
#include #include #include ⽤例如下:
#include #include #include #include #include #include int main(int argc,char* argv[]){
char host_name[128]={NULL};
gethostname(host_name, sizeof(host_name));//获取本地主机名称 printf(\"host_name:%s\\n\",host_name);
struct addrinfo *ailist=NULL,*aip=NULL; struct sockaddr_in *saddr; char *addr;
int ret=getaddrinfo(host_name,NULL,NULL,&ailist); for(aip=ailist; aip!=NULL; aip=aip->ai_next) {
if(aip->ai_family==AF_INET) {
saddr=(struct sockaddr_in*)aip->ai_addr; addr=inet_ntoa(saddr->sin_addr); }
printf(\"addr:%s\\n\",addr); }
printf(\"\\n-----------------baidu host info-------------------\\n\"); getaddrinfo(\"www.baidu.com\",\"http\",NULL,&ailist); for(aip=ailist; aip!=NULL; aip=aip->ai_next) {
if(aip->ai_family==AF_INET) {
saddr=(struct sockaddr_in*)aip->ai_addr; addr=inet_ntoa(saddr->sin_addr); }
printf(\"baidu addr:%s\\n\",addr); }
getchar(); return 0;}
使⽤gcc编译此程序会出现error: dereferencing pointer to incomplete type的错误,使⽤g++编译通过,程序输出:
⽅法四:gethostname()获取本地IP地址 gethostname()和getaddrinfo()的功能类似,⼀般⽤于通过主机名或者服务名,⽐如域名来获取主机的IP地址。但是要想获取本地IP地址的时候,⼀般获取的是回环地址127.0.0.1。
string getLocalIP(char* local_ip) {
// 获取本地IP时,⼀般都是127.0.0.1 char host_name[128]=\"\"; struct hostent *host_ent;
gethostname(host_name, sizeof(host_name)); host_ent = gethostbyname(host_name);
const char* first_ip = inet_ntoa(*(struct in_addr*)(host_ent->h_addr_list[0])); memcpy(local_ip, first_ip, 16); return string(host_name);}
注意,主机的地址是⼀个列表的形式,原因是当⼀个主机有多个⽹络接⼝时,及多块⽹卡或者⼀个⽹卡绑定多个IP地址时,⾃然就有多个IP地址。以上代码获取的是根据主机名称得到的第⼀个IP地址。C语⾔版本
char getLocalIP(char *ip){
int inet_sock; struct ifreq ifr;
inet_sock = socket(AF_INET, SOCK_DGRAM, 0); strcpy(ifr.ifr_name, \"eth0\");
ioctl(inet_sock, SIOCGIFADDR, &ifr);
strcpy(ip, inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr)); close(inet_sock); return 0;}
Reference: