1. 程式人生 > >Linux下程式設計獲取本地IP地址的常見方法

Linux下程式設計獲取本地IP地址的常見方法

轉載於:http://blog.csdn.net/k346k346/article/details/48231933

  在進行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>

用例如下:

#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地址。