Linux下使用ioctl獲取本地介面IP
阿新 • • 發佈:2019-02-09
最近在ubuntu上寫FTP伺服器時封裝了getlocalIP來獲取本機IP,函式內部使用gethostname()獲取主機名,使用gethostbyname()來獲取主機IP列表,但是用該函式獲取的ip繫結socket去使用connect方法時會失敗,errno程式碼為22,invalid argument,列印輸出獲取的ip,發現獲取的ip為127.0.1.1。經查詢文件,gethostbyname()會去解析/etc/hosts檔案來獲取ip,檢視該檔案發現裡面只有兩行1.127.0.0.1 localhost 2.127.1.1.1 主機名 ,所以根據主機名來獲取IP會返回127.0.1.1,與用ifconfig檢視到的ip不相等。搜尋了一下,網上有其他人遇到了此問題,可以通過解析/etc/sysconfig/network-scripts目錄下的ifcfg-eth0等網絡卡配置檔案來讀取真實IP,但是ubuntu沒有該目錄,經過查閱資料,可以使用ioctl讀取網絡卡資訊獲取IP,終端下輸入 man netdevice
主要有兩個結構體,struct ifconf 和 struct ifreq,具體含義由ioctl第二個引數決定,前者儲存所有網絡卡裝置得到資訊,後者儲存網絡卡介面名稱及對應的IP地址等資訊,ioctl函式原型如下:
int ioctl(int d, int request, ...);
第二個引數為設定的動作,與網路相關的引數如下(來自百度百科):
類別 | Request | 說明 | 資料型別 |
套 接 口 | SIOCATMARK SIOCSPGRP SIOCGPGRP | 是否位於帶外標記 設定套介面的程序ID 或程序組ID 獲取套介面的程序ID 或程序組ID | int int int |
文 件 | FIONBIO FIOASYNC FIONREAD FIOSETOWN FIOGETOWN | 設定/ 清除非阻塞I/O 標誌 設定/ 清除訊號驅動非同步I/O 標誌 獲取接收快取區中的位元組數 設定檔案的程序ID 或程序組ID 獲取檔案的程序ID 或程序組ID | int int int int int |
接 口 | SIOCGIFCONF SIOCSIFADDR SIOCGIFADDR SIOCSIFFLAGS SIOCGIFFLAGS SIOCSIFDSTADDR SIOCGIFDSTADDR SIOCGIFBRDADDR SIOCSIFBRDADDR SIOCGIFNETMASK SIOCSIFNETMASK SIOCGIFMETRIC SIOCSIFMETRIC SIOCGIFMTU SIOCxxx | 獲取所有介面的清單 設定介面地址 獲取介面地址 設定介面標誌 獲取介面標誌 設定點到點地址 獲取點到點地址 獲取廣播地址 設定廣播地址 獲取子網掩碼 設定子網掩碼 獲取介面的測度 設定介面的測度 獲取介面MTU (還有很多取決於系統的實現) | struct ifconf struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq |
ARP | SIOCSARP SIOCGARP SIOCDARP | 建立/ 修改ARP 表項 獲取ARP 表項 刪除ARP 表項 | struct arpreq struct arpreq struct arpreq |
路 由 | SIOCADDRT SIOCDELRT SIOCRTMSG | 增加路徑 刪除路徑 獲取路由表 | struct rtentry struct rtentry struct rtentry |
流 | I_xxx |
1.直接獲取指定網絡卡ip
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<sys/ioctl.h>
#include<sys/socket.h>
#include<net/if.h>
#include<string.h>
int main()
{
int sockfd;
struct ifreq ifr;
struct sockaddr_in sin;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket error");
return -1;
}
strcpy(ifr.ifr_name,"wlan0");
if(ioctl(sockfd, SIOCGIFADDR, &ifr) < 0)//直接獲取IP地址
{
perror("ioctl error");
return -1;
}
memcpy(&sin, &ifr.ifr_dstaddr, sizeof(sin));
printf("ip is %s \n",inet_ntoa(sin.sin_addr));
return 0;
}
直接獲取無線網絡卡IP,輸出結果如下:
2.獲取所有網路裝置並輸出IP:
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <string.h>
int main()
{
int i = 0;
int sockfd;
struct ifconf ifconf;
struct ifreq *ifreq;
unsigned char buf[1024];
//初始化ifconf
ifconf.ifc_len = 1024;
ifconf.ifc_buf = buf;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket error");
exit(1);
}
//獲取所有介面資訊
ioctl(sockfd, SIOCGIFCONF, &ifconf);
//逐個獲取Ip地址
ifreq = (struct ifreq*)buf;
for(i = (ifconf.ifc_len/sizeof(struct ifreq)); i>0; i--)
{
printf("name = [%s] : ",ifreq->ifr_name);
printf("%s\n",inet_ntoa( ((struct sockaddr_in *)&(ifreq->ifr_addr))->sin_addr));
ifreq++;
}
return 0;
}
使用ifconfig命令檢視網路介面資訊並對比輸出結果如下:
可以看到,使用ioctl獲取本地IP的方法比gethostbyname()更加可靠,實際使用時可以根據需要獲取指定網絡卡的IP資訊。