總結一下,今天學習的關於經過socket,ioctl來得到ip,netmask等信息,其中不少內容參照了不少網上的信息,我會一一列出的我用的這個函數,就是下面這個函數,其中的有一些全局變量,很好懂,也就很少作解釋了html
一。下面對這個函數進行註解一下:ios
int get_nic_IP_Address() //獲取各網卡IP地址、子網掩碼數據庫
{數組
struct ifreq ifreq; //聲明一個struct ifreq結構體(這個結構體中有不少重要的參數,具體能夠參照第二的補充)網絡
int sock;socket
int i;tcp
int tmpint;函數
read_dev();//這個函數的功能是得到網卡名字(保存在下面提到的sys_nic_ip[][]數組中)並計算網卡總數(就是下面的sys_nic_count)post
for (i=0;i<sys_nic_count;i++)< span="">學習
{
if((sock=socket(AF_INET,SOCK_STREAM,0))<0)
{ //創建一個套接字
perror("socket");
return;
}
strcpy(ifreq.ifr_name,sys_nic_name[i]); //把網卡名字複製到ifreq結構體中的name變量(感受這個地方是必須的)
if(ioctl(sock,SIOCGIFADDR,&ifreq)<0)
{ //這裏涉及ioctl函數對於網絡文件的控制(下面會介紹)
sprintf(sys_nic_ip[i],"Not set");
}
else
{
sprintf(sys_nic_ip[i],"%d.%d.%d.%d", //把ip地址提取出來,保存(理解一下socketaddr_in和socketaddr的關係)
(unsigned char)ifreq.ifr_addr.sa_data[2],
(unsigned char)ifreq.ifr_addr.sa_data[3],
(unsigned char)ifreq.ifr_addr.sa_data[4],
(unsigned char)ifreq.ifr_addr.sa_data[5]);
}
if(ioctl(sock,SIOCGIFNETMASK,&ifreq)<0)
{ //個人理解是這個地方用SIOCGIFNETMASK,那麼ifreq中本來是存的ip地址,如今存成了子網掩碼了。。
sprintf(sys_nic_mask[i],"Not set");//把子網掩碼提取出來(但獲得的只是超網的劃分方式就是/xx)
}
else
{
sprintf(sys_nic_mask[i],"%d",
Count((unsigned char)ifreq.ifr_netmask.sa_data[2])+
Count((unsigned char)ifreq.ifr_netmask.sa_data[3])+
Count((unsigned char)ifreq.ifr_netmask.sa_data[4])+
Count((unsigned char)ifreq.ifr_netmask.sa_data[5]));
}
}
}
列出上面最後調用函數( Count()
) 和一些全副變量
:
char sys_nic_ip[20][20]; //各網卡IP
char sys_nic_mask[20][20]; //各網卡子網掩碼"/xx"
int countTable[256] =
{ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3,
3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3,
3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3,
3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3,
3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5,
5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3,
3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4,
4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3,
3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5,
5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5,
5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 };
int Count(int v)
{
return countTable[v];
}
應該理解了吧。。。挺經典的。。。不過網上的貌似就有一個版本。。。 非常氣惱
二。 對涉及的知識點進行補充
1.struct ifreq
{
char ifr_name[IFNAMSIZ];
union
{
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
struct sockaddr ifru_netmask;
struct sockaddr ifru_hwaddr;
short int ifru_flags;
int ifru_ivalue;
int ifru_mtu;
struct ifmap ifru_map;
char ifru_slave[IFNAMSIZ]; /* Just fits the size */
char ifru_newname[IFNAMSIZ];
__caddr_t ifru_data;
}ifr_ifru;
};
# define ifr_name ifr_ifrn.ifrn_name /* interface name */
# define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
# define ifr_addr ifr_ifru.ifru_addr /* address */
# define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */
# define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
# define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */
# define ifr_flags ifr_ifru.ifru_flags /* flags */
# define ifr_metric ifr_ifru.ifru_ivalue /* metric */
# define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
# define ifr_map ifr_ifru.ifru_map /* device map */
# define ifr_slave ifr_ifru.ifru_slave /* slave device */
# define ifr_data ifr_ifru.ifru_data /* for use by interface */
# define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */
# define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */
# define ifr_qlen ifr_ifru.ifru_ivalue /* queue length */
# define ifr_newname ifr_ifru.ifru_newname /* New name */
# define _IOT_ifreq _IOT(_IOTS(char),IFNAMSIZ,_IOTS(char),16,0,0)
# define _IOT_ifreq_short _IOT(_IOTS(char),IFNAMSIZ,_IOTS(short),1,0,0)
# define _IOT_ifreq_int _IOT(_IOTS(char),IFNAMSIZ,_IOTS(int),1,0,0)
2.ioctl 函數 (在網絡中的做用)
關於這個網絡相關的請求,就是ioctl在這裏面起的做用和各個參數的做用。。。能夠參照這個網頁,講解的很詳細:
http: //www.iteye.com/topic/309442
本例中用的2個ioctl控制函數。。上面已經解釋很清楚了
3.關於socketaddr_in和socketaddr的關係,下面貼出具體的定義:
struct sockaddr_in
{
short int sin_family; /* 地址族 */
unsigned short int sin_port; /* 端口號 */
struct in_addr sin_addr; /* IP地址 */
unsigned char sin_zero[8]; /* 填充0 以保持與struct sockaddr一樣大小 */
};
struct sockaddr
{
unsigned short sa_family; /* 地址族, AF_xxx */
char sa_data[14]; /* 14 字節的協議地址 */
};
比較一下,會發現長度同樣,因此這2個能夠通用的,不過要進行類型轉換, 比較一下就得出了爲何上面程序中能夠用:
(unsigned char)
ifreq.ifr_addr.sa_data[2],這種形式了,仍是解釋一下吧:這個ifr_addr是一個struct sockaddr結構體。它其中的sa_date[2]是否是照着上面sockaddr_in中的sin_add(也就是ip地址呢),該明白了吧。。。。
總結:經過這個函數,能夠很好的理解怎麼獲得ip和子網掩碼的過程。。。。
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
int inet_sock;
struct ifreq ifr;
inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
//eth0爲接口到名稱
strcpy(ifr.ifr_name, "eth1");
//SIOCGIFADDR標誌表明獲取接口地址
if (ioctl(inet_sock, SIOCGIFADDR, &ifr) == 0)
perror("ioctl");
printf("%s\n", inet_ntoa(((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr));
return 0;
}
--------------------------------------------------------------------------------------------------------------------------------------------------------------
ifreq結構定義在/usr/include/net/if.h,用來配置ip地址,激活接口,配置MTU等接口信息的。
其中包含了一個接口的名字和具體內容——(是個共用體,有多是IP地址,廣播地址,子網掩碼,MAC號,MTU或其餘內容)。
ifreq包含在ifconf結構中。而ifconf結構一般是用來保存全部接口的信息的。
--------------------------------------------------------------------------------------------------------------------------------------------------------------
用ioctl得到本地ip地址時要用到兩個結構體ifconf和ifreq,它們對於大多數人
來講都是比較陌生的,這裏給你們一種比較簡單的理解方法,固然只一種幫助
理解的方法,在描述中可能會有一些地方與真實定義有所出入,僅供參考.
首先先認識一下ifconf和ifreq:
//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的功能。
咱們的作法是:
1. 先經過ioctl得到本地全部接口的信息,並保存在ifconf中
2. 再從ifconf中取出每個ifreq中表示ip地址的信息
具體使用時咱們能夠認爲ifconf就有兩個成員:
ifc_len 和 ifc_buf,如圖一所示:
ifc_len:表示用來存放全部接口信息的緩衝區長度
ifc_buf:表示存放接口信息的緩衝區
因此咱們須要在程序開始時對ifconf的ifc_led和ifc_buf進行初始化
接下來使用ioctl獲取全部接口信息,完成後ifc_len內存放實際得到的藉口信息總長度
而且信息被存放在ifc_buf中。
以下圖示:(假設讀到兩個接口信息)
接下來咱們只須要從一個一個的接口信息獲取ip地址信息便可。
下面有一個簡單的參考:
#include
#include
#include
#include
#include in.h>
#include <string.h>
#include if.h>
#include
int main()
{
int i=0;
int sockfd;
struct ifconf ifconf;
unsigned char buf[512];
struct ifreq *ifreq;
//初始化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*)buf;
for(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++;
// }
}
return 0;
}
--------------------------------------------------------------------------------------------------------------------------------------------------------------