1、Linux编程要点
在Linux下编写获取本机网卡地址的程序,比较简单的方法是利用套接口(socket)和IO接口(ioctl)函数来获取网卡信息,需要引用如下文件:
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/if_ether.h>
#include <net/if.h>
#include <linux/sockios.h>
socket函数的原型是:
int socket(int domain,int type, int protocol);
本函数有以下3个输入参数:
domain参数:表示所使用的协议族;
type参数:表示套接口的类型;
protocol参数:表示所使用的协议族中某个特定的协议。
如果函数调用成功,套接口的描述符(非负整数)就作为函数的返回值,假如返回值为-1,就表明有错误发生。
利用socket函数来获取网卡MAC信息时,domain参数取值AF_INET,表示采用internet协议族;type参数指定为SOCK_DGRAM,表示采用数据报类型套接口,protocol参数在这种组合下只有唯一选择,故用0填充。
I/O控制函数ioctl用于对文件进行底层控制,这里的文件包含网卡、终端、磁带机、套接口等软硬件设施,实际的操作来自各个设备自己提供的ioctl接口。ioctl函数的原型如下:
int ioctl(int d,int request,…)
这里,参数d取值套接口的描述符,第一个request参数指定通过socket传输的I/O类型。本实验可以取值 SIONGIFHWADDR(0x8927),表示取硬件地址。其他取值及其含义详见/usr/includr/linux/sockios.h。其后的 request参数用于为实现I/O控制所必须传入或传出的参数。本实验需要用ifr结构传入网卡设备名,并传出6B的MAC地址。关键的程序段如下:
#include <netinet/if_ether.h>
#include <net/if.h>
char *device=”eth0”; //teh0是网卡设备名
unsigned char macaddr[ETH_ALEN]; //ETH_ALEN(6)是MAC地址长度
int s=socket(AF_INET,SOCK_DGRAM,0); //建立套接口
struct ifreq req;
int err;
strcpy(req.ifr_name,device); //将设备名作为输入参数传入
err=ioctl(s,SIOCGIFHWADDR,&req); //执行取MAC地址操作
close(s);
if(err!= -1)
{ memcpy(addr,req.ifr_hwaddr.sa_data,ETH_ALEN); //取输出的MAC地址
for(i=0;i<ETH_ALEN;i++)
printf(”%3d:”,macaddr[i]);
}
通过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地址函数
- 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;
- }
有时候,写程序的时候需要获取计算机的网络信息,比如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
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地址的方法相同,这里就不多说了