在進行Linux網絡編程時,經常用到本機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 <netdb.h>
-
#include <arpa/inet.h>
-
#include <sys/socket.h>
-
#include <unistd.h>
-
#include <sys/ioctl.h>
-
#include <net/if.h>
-
#include <stdio.h>
-
#include <stdlib.h>
-
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 <sys/socket.h>
-
#include <arpa/inet.h>
-
#include <unistd.h>
-
#include <netdb.h>
-
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"<<endl;
-
return -1;
-
}
-
char buf[30]="";
-
bzero(&localaddr,sizeof(localaddr));
-
getsockname(fd,(struct sockaddr*)&localaddr,&len); //獲取本地信息
-
cout<<"local ip is "<<inet_ntop(AF_INET,&localaddr.sin_addr,buf,sizeof(buf))<<"local port is"<<ntohs(localaddr.sin_port)<<endl;
-
bzero(&peeraddr,sizeof(peeraddr));
-
getpeername(fd,(struct sockaddr*)&peeraddr,&len); //獲取對端信息
-
cout<<"peer ip is "<< inet_ntop(AF_INET,&peeraddr.sin_addr,buf,sizeof(buf))<<"peer port is "<<ntohs(peeraddr.sin_port)<<endl;
-
return 1;
-
}
-
}
下面兩種方法,都是通過主機名稱來獲取主機的IP地址,在獲取本地IP地址時,一般都是迴環地址,但可以有效的根據主機名稱獲取網絡中的主機的IP地址,如通過域名獲取域名對應的IP地址。
要想精確的獲取某塊網卡綁定的IP地址,請根據ioctl()和接口名稱(如eth0)來獲取,具體實現見上文。
方法三:getaddrinfo()獲取本地IP地址
注意,getaddrinfo()可以完成網絡主機中主機名和服務名到地址的映射,但是一般不能用來獲取本地IP地址,當它用來獲取本地IP地址時,返回的一般是127.0.0.1本地迴環地址。
所需頭文件:
-
#include <sys/types.h>
-
#include <sys/socket.h>
-
#include <netdb.h>
- 1
- 2
- 3
用例如下:
-
#include <netdb.h>
-
#include <arpa/inet.h>
-
#include <sys/socket.h>
-
#include <unistd.h>
-
#include <stdio.h>
-
#include <stdlib.h>
-
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地址。