歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> linux編程獲取本機IP地址的三種方法

linux編程獲取本機IP地址的三種方法

日期:2017/3/3 13:49:53   编辑:Linux技術
這是一項不太清晰而且沒有多大意義的工作。一個原因是網絡地址的設置非常靈活而且都是允許用戶進行個性化設置的,比如一台計算機上可以有多塊物理網卡或者虛擬網卡,一個網卡上可以綁定多個IP地址,用戶可以為網卡設置別名,可以重命名網卡,用戶計算機所在網絡拓撲結構未知,主機名設置是一個可選項並且同樣可以為一個計算機綁定多個主機名等,這些信息都會有影響。脫離了網絡連接,單獨的網絡地址沒有任何意義。編程中遇到必須獲取計算機IP的場景,應該考慮將這一選項放到配置文件中,由用戶自己來選擇。
通過google,編程獲取IP地址大約有以下三種思路:
1. 通過gethostname()和gethostbyname()
#include <stdio.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
char hname[128];
struct hostent *hent;
int i;
gethostname(hname, sizeof(hname));
//hent = gethostent();
hent = gethostbyname(hname);
printf("hostname: %s/naddress list: ", hent->h_name);
for(i = 0; hent->h_addr_list[i]; i++) {
printf("%s/t", inet_ntoa(*(struct in_addr*)(hent->h_addr_list[i])));
}
return 0;
}
運行:
[whb@jcwkyl c]$ ./local_ip
hostname: jcwkyl.jlu.edu.cn
address list: 10.60.56.90
2. 通過枚舉網卡,API接口可查看man 7 netdevice
/*代碼來自StackOverflow:http://stackoverflow.com/questions/212528/linux-c-get-the-ip-address-of-local-computer */
#include <stdio.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
int main (int argc, const char * argv[]) {
struct ifaddrs * ifAddrStruct=NULL;
void * tmpAddrPtr=NULL;
getifaddrs(&ifAddrStruct);
while (ifAddrStruct!=NULL) {
if (ifAddrStruct->ifa_addr->sa_family==AF_INET) { // check it is IP4
// is a valid IP4 Address
tmpAddrPtr=&((struct sockaddr_in*)ifAddrStruct->ifa_addr)->sin_addr;
char addressBuffer[INET_ADDRSTRLEN];
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
printf("%s IP Address %s/n", ifAddrStruct->ifa_name, addressBuffer);
} else if (ifAddrStruct->ifa_addr->sa_family==AF_INET6) { // check it is IP6
// is a valid IP6 Address
tmpAddrPtr=&((struct sockaddr_in*)ifAddrStruct->ifa_addr)->sin_addr;
char addressBuffer[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
printf("%s IP Address %s/n", ifAddrStruct->ifa_name, addressBuffer);
}
ifAddrStruct=ifAddrStruct->ifa_next;
}
return 0;
}
運行 :
[whb@jcwkyl c]$ ./local_ip2
lo IP Address 127.0.0.1
eth0 IP Address 10.60.56.90
eth0:1 IP Address 192.168.1.3
lo IP Address ::
eth0 IP Address ::2001:da8:b000:6213:20f:1fff
eth0 IP Address 0:0:fe80::20f:1fff
3. 打開一個對外界服務器的網絡連接,通過getsockname()反查自己的IP
補充:


在linux下 獲取,修改本機IP地址的兩個函數

//獲取本機IP地址函數
view plaincopy
to clipboardprint?
QString GetLocalIp()
{
int sock_get_ip;
char ipaddr[50];
struct sockaddr_in *sin;
struct ifreq ifr_ip;
if ((sock_get_ip=socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
printf("socket create failse...GetLocalIp!/n");
return "";
}
memset(&ifr_ip, 0, sizeof(ifr_ip));
strncpy(ifr_ip.ifr_name, "eth0", sizeof(ifr_ip.ifr_name) - 1);
if( ioctl( sock_get_ip, SIOCGIFADDR, &ifr_ip) < 0 )
{
return "";
}
sin = (struct sockaddr_in *)&ifr_ip.ifr_addr;
strcpy(ipaddr,inet_ntoa(sin->sin_addr));
printf("local ip:%s /n",ipaddr);
close( sock_get_ip );
return QString( ipaddr );
}
//修改本機IP地址的函數
int SetLocalIp( const char *ipaddr )
{
int sock_set_ip;
struct sockaddr_in sin_set_ip;
struct ifreq ifr_set_ip;
bzero( &ifr_set_ip,sizeof(ifr_set_ip));
if( ipaddr == NULL )
return -1;
if(sock_set_ip = socket( AF_INET, SOCK_STREAM, 0 ) == -1);
{
perror("socket create failse...SetLocalIp!/n");
return -1;
}
memset( &sin_set_ip, 0, sizeof(sin_set_ip));
strncpy(ifr_set_ip.ifr_name, "eth0", sizeof(ifr_set_ip.ifr_name)-1);
sin_set_ip.sin_family = AF_INET;
sin_set_ip.sin_addr.s_addr = inet_addr(ipaddr);
memcpy( &ifr_set_ip.ifr_addr, &sin_set_ip, sizeof(sin_set_ip));
if( ioctl( sock_set_ip, SIOCSIFADDR, &ifr_set_ip) < 0 )
{
perror( "Not setup interface/n");
return -1;
}
//設置激活標志
ifr_set_ip.ifr_flags |= IFF_UP |IFF_RUNNING;
//get the status of the device
if( ioctl( sock_set_ip, SIOCSIFFLAGS, &ifr_set_ip ) < 0 )
{
perror("SIOCSIFFLAGS");
return -1;
}
close( sock_set_ip );
return 0;
}


在linux下 獲取本機MAC地址的函數

獲取本機MAC地址函數
QString GetLocalMac()
{
int sock_mac;
struct ifreq ifr_mac;
char mac_addr[30];
sock_mac = socket( AF_INET, SOCK_STREAM, 0 );
if( sock_mac == -1)
{
perror("create socket falise...mac/n");
return "";
}
memset(&ifr_mac,0,sizeof(ifr_mac));
strncpy(ifr_mac.ifr_name, "eth0", sizeof(ifr_mac.ifr_name)-1);
if( (ioctl( sock_mac, SIOCGIFHWADDR, &ifr_mac)) < 0)
{
printf("mac ioctl error/n");
return "";
}
sprintf(mac_addr,"%02x%02x%02x%02x%02x%02x",
(unsigned char)ifr_mac.ifr_hwaddr.sa_data[0],
(unsigned char)ifr_mac.ifr_hwaddr.sa_data[1],
(unsigned char)ifr_mac.ifr_hwaddr.sa_data[2],
(unsigned char)ifr_mac.ifr_hwaddr.sa_data[3],
(unsigned char)ifr_mac.ifr_hwaddr.sa_data[4],
(unsigned char)ifr_mac.ifr_hwaddr.sa_data[5]);
printf("local mac:%s /n",mac_addr);
close( sock_mac );
return QString( mac_addr );
}


在linux下 獲取,修改子網掩碼NETMASK的兩個函數

//獲取子網掩碼的函數
QString GetLocalNetMask()
{
int sock_netmask;
char netmask_addr[50];
struct ifreq ifr_mask;
struct sockaddr_in *net_mask;
sock_netmask = socket( AF_INET, SOCK_STREAM, 0 );
if( sock_netmask == -1)
{
perror("create socket failture...GetLocalNetMask/n");
return "";
}
memset(&ifr_mask, 0, sizeof(ifr_mask));
strncpy(ifr_mask.ifr_name, ifname, sizeof(ifr_mask.ifr_name )-1);
if( (ioctl( sock_netmask, SIOCGIFNETMASK, &ifr_mask ) ) < 0 )
{
printf("mac ioctl error/n");
return "";
}
net_mask = ( struct sockaddr_in * )&( ifr_mask.ifr_netmask );
strcpy( netmask_addr, inet_ntoa( net_mask -> sin_addr ) );
printf("local netmask:%s/n",netmask_addr);
close( sock_netmask );
return QString( netmask_addr );
}
//修改子NETMASK的函數
QString SetLocalNetMask(const char *szNetMask)
{
int sock_netmask;
char netmask_addr[32];
struct ifreq ifr_mask;
struct sockaddr_in *sin_net_mask;
sock_netmask = socket( AF_INET, SOCK_STREAM, 0 );
if( sock_netmask == -1)
{
perror("Not create network socket connect/n");
return "";
}
memset(&ifr_mask, 0, sizeof(ifr_mask));
strncpy(ifr_mask.ifr_name, "eth0", sizeof(ifr_mask.ifr_name )-1);
sin_net_mask = (struct sockaddr_in *)&ifr_mask.ifr_addr;
sin_net_mask -> sin_family = AF_INET;
inet_pton(AF_INET, szNetMask, &sin_net_mask ->sin_addr);
if(ioctl(sock_netmask, SIOCSIFNETMASK, &ifr_mask ) < 0)
{
printf("sock_netmask ioctl error/n");
return "";
}
}
//獲去GateWay
QString GetGateWay()
{
FILE *fp;
char buf[512];
char cmd[128];
char gateway[30];
char *tmp;
strcpy(cmd, "ip route");
fp = popen(cmd, "r");
if(NULL == fp)
{
perror("popen error");
return "";
}
while(fgets(buf, sizeof(buf), fp) != NULL)
{
tmp =buf;
while(*tmp && isspace(*tmp))
++ tmp;
if(strncmp(tmp, "default", strlen("default")) == 0)
break;
}
sscanf(buf, "%*s%*s%s", gateway);
printf("default gateway:%s/n", gateway);
pclose(fp);
return QString(gateway);
}
//設置網關
int SetGateWay(const char *szGateWay)
{
int ret = 0;
char cmd[128];
QString DefGW = GetGateWay();
const char *strGW = DefGW.latin1();
strcpy(cmd, "route del default gw ");
strcat(cmd, strGW);
ret = system(cmd);
if(ret < 0)
{
perror("route error");
return -1;
}
strcpy(cmd, "route add default gw ");
strcat(cmd, szGateWay);
ret = system(cmd);
if(ret < 0)
{
perror("route error");
return -1;
}
return ret;
}
補充2:
有時候,寫程序的時候需要獲取計算機的網絡信息,比如IP地址、電腦名稱、DNS等信息。IP地址和電腦名稱是比較容易獲取到的,而要想獲取地址掩碼、DNS、網關等信息就有些麻煩了。
在Windows下我們一般都是通過從注冊表讀取這些信息。在Linux怎麼做呢?其實,Linux下更加容易一些。因為我們可以拿現成的程序看它的源代碼。通過閱讀其源代碼找到解決該問題的方法。那麼,看哪個程序的源代碼呢?如果你使用過Linux,並且比較熟悉的話就肯定知道一個命令ifconfig。這個命令和Windows下的ipconfig差不多,都可以輸出網卡的信息,其中就包含DNS、掩碼等信息。所以,我們可以通過看它的源代碼來找到解決該問題的方法。

獲取系統中的網卡數量

並沒有那個系統調用提供網卡數量的獲取。但是,我們可以通過強大的proc文件系統獲取網卡數量的信息。實際上,ifconfig也是這樣做的,請看示例代碼如下:

0001 #include <stdio.h>

0002 #include <string.h>

0003 #include <errno.h>

0004

0005 int GetNetCardCount()

0006 {

0007     int nCount = 0;

0008     FILE* f = fopen("/proc/net/dev", "r");

0009    if (!f)

0010     {

0011         fprintf(stderr, "Open /proc/net/dev failed!errno:%d\n", errno);

0012         return nCount;

0013    }

0014

0015    char szLine[512];

0016

0017     fgets(szLine, sizeof(szLine), f);    /* eat line */

0018     fgets(szLine, sizeof(szLine), f);

0019

0020     while(fgets(szLine, sizeof(szLine), f))

0021     {

0022         char szName[128] = {0};

0023         sscanf(szLine, "%s", szName);

0024        int nLen = strlen(szName);

0025         if (nLen <= 0)continue;

0026         if (szName[nLen - 1] == ':') szName[nLen - 1] = 0;

0027         if (strcmp(szName, "lo") == 0)continue;

0028         nCount++;

0029     }

0030

0031     fclose(f);

0032     f = NULL;

0033     return nCount;

0034 }

0035

0036 int main(int argc, char* argv[])

0037 {

0038     printf("NetCardCount: %d\n", GetNetCardCount());

0039     return 0;

0040 }

獲取IP、掩碼、MAC及網關

獲取IP、掩碼、MAC和廣播地址是比較容易的,只需要調用對應的IOCTL即可。只是大家對Linux下的IOCTL可能不太熟悉。卻看示例代碼:
0001 void DispNetInfo(const char* szDevName)

0002 {

0003     int s = socket(AF_INET, SOCK_DGRAM, 0);

0004    if (s < 0)

0005     {

0006         fprintf(stderr, "Create socket failed!errno=%d", errno);

0007         return;

0008     }

0009

0010     struct ifreq ifr;

0011     unsigned char mac[6];

0012     unsigned long nIP, nNetmask, nBroadIP;

0013

0014    printf("%s:\n", szDevName);

0015

0016    strcpy(ifr.ifr_name, szDevName);

0017     if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0)

0018     {

0019        return;

0020     }

0021     memcpy(mac, ifr.ifr_hwaddr.sa_data, sizeof(mac));

0022     printf("\tMAC: %02x-%02x-%02x-%02x-%02x-%02x\n",

0023             mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

0024

0025     strcpy(ifr.ifr_name, szDevName);

0026     if (ioctl(s, SIOCGIFADDR, &ifr) < 0)

0027     {

0028         nIP = 0;

0029     }

0030    else

0031     {

0032         nIP = *(unsigned long*)&ifr.ifr_broadaddr.sa_data[2];

0033     }

0034     printf("\tIP: %s\n", inet_ntoa(*(in_addr*)&nIP));

0035

0036     strcpy(ifr.ifr_name, szDevName);

0037     if (ioctl(s, SIOCGIFBRDADDR, &ifr) < 0)

0038     {

0039         nBroadIP = 0;

0040     }

0041     else

0042     {

0043         nBroadIP = *(unsigned long*)&ifr.ifr_broadaddr.sa_data[2];

0044     }

0045     printf("\tBroadIP: %s\n", inet_ntoa(*(in_addr*)&nBroadIP));

0046

0047     strcpy(ifr.ifr_name, szDevName);

0048     if (ioctl(s, SIOCGIFNETMASK, &ifr) < 0)

0049     {

0050         nNetmask = 0;

0051     }

0052     else

0053     {

0054         nNetmask = *(unsigned long*)&ifr.ifr_netmask.sa_data[2];

0055     }

0056     printf("\tNetmask: %s\n", inet_ntoa(*(in_addr*)&nNetmask));

0057     close(s);

0058 }

那麼如何獲取網關地址呢?更加容易,但是,好像很少有人知道。反正我在網上沒有找到有人知道。最後看了nslookup的源代碼以後才知道正確的做法。代碼如下:
res_init();
for (int i = 0; i < _res.nscount; i++)
{
struct sockaddr* server = (struct sockaddr*)&_res.nsaddr_list[i];
printf("Server: %s\n", inet_ntoa(*(in_addr*)&(server->sa_data[2])));
}
代碼很簡單,就不做解釋了。
怎麼獲取網關呢?這個稍微有點麻煩一些,不過和獲取網卡數量相似,都是通過proc文件系統。這次分析的/proc/net/route文件。我就不再貼出示例代碼了。
最後,我把運行示例程序獲取到的信息附上,以供大家有個直觀的認識:
eth0:
MAC: 08-00-27-98-bf-f3
IP: 192.168.1.106
BroadIP: 255.255.255.255
Netmask: 255.255.255.0
Gateway: 192.168.1.1
eth1:
MAC: 08-00-27-16-f4-bf
IP: 192.168.1.108
BroadIP: 192.168.1.255
Netmask: 255.255.255.0
Gateway: 0.0.0.0
eth2:
MAC: 08-00-27-37-9c-91
IP: 0.0.0.0
BroadIP: 0.0.0.0
Netmask: 0.0.0.0
Gateway: 0.0.0.0
eth3:
MAC: 08-00-27-5a-d2-39
IP: 0.0.0.0
BroadIP: 0.0.0.0
Netmask: 0.0.0.0
Gateway: 0.0.0.0
NetCardCount: 4
DNS 0: 218.2.135.1
DNS 1: 61.147.37.1
補充3:
Linux下的網絡配置包含三個要素,分別是IP地址、子網掩碼和網關。本文將介紹如何在C語言中進行網絡的配置和配置信息的獲取。
【配置】
方法一
使用system()或exec*()調用ifconfig和route命令進行配置。這種方法的優點是使用簡單,缺點是效率比較低,且依賴於ifconfig與route命令。
示例:
見所附代碼中的函數ip_config_system()和ip_config_exec()。
方法二
建立一個socket,用ioctl()進行配置。這種方法的優點是效率較高,缺點是程序實現起來比較麻煩。
示例:
見所附代碼中的函數ip_config_ioctl()。
【獲取】
方法一
用popen()建立一個管道,管道的一端執行命令ifconfig和route,管道的另一端讀取收到的數據並進行相應的解析。這種方法的優點是使用簡單,缺點是效率比較低,且依賴於ifconfig與route命令。
示例:
見所附代碼中的函數ip_get_pipe()。
方法二
用fopen()打開/proc/net/route,可以獲取網關(在/proc/net中尚未發現比較好的獲取IP地址和掩碼的方法,知道的請發郵件至cugfeng
at gamil.com,謝謝)。這種方法的優點是使用簡單,效率比執行命令高,缺點是依賴於proc文件系統。
示例:
見所附代碼中的函數ip_get_proc()。
方法三
建立一個socket,用ioctl()進行獲取(用ioctl()尚未發現比較好的獲取網關的方法,知道的請發郵件至cugfeng at gamil.com,謝謝)。這種方法的優點是效率較高,缺點是程序實現起來比較麻煩。
示例:
見所附代碼中的函數ip_get_ioctl()。
BTW,用ioctl()的方法還可以獲取MAC地址,ioctl()命令為SIOCGIFHWADDR,具體用法與ioctl()獲取IP地址的方法相同,這裡就不多說了。
Copyright © Linux教程網 All Rights Reserved