1. 程式人生 > >Linux下使用ioctl獲取本地介面IP

Linux下使用ioctl獲取本地介面IP

最近在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資訊。