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

Linux下使用ioctl获取本地接口IP_第1张图片

主要有两个结构体,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
#include
#include
#include
#include
#include
#include

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 
#include 
#include 
#include 
#include 
#include 
#include 

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命令查看网络接口信息并对比输出结果如下:

Linux下使用ioctl获取本地接口IP_第2张图片

可以看到,使用ioctl获取本地IP的方法比gethostbyname()更加可靠,实际使用时可以根据需要获取指定网卡的IP信息。

你可能感兴趣的:(Linux编程)