总结一下,今天学习的关于通过socket,ioctl来获得ip,netmask等信息,其中很多内容参照了很多网上的信息,我会一一列出的我用的这个函数,就是下面这个函数,其中的有一些全局变量,很好懂,也就不多做解释了

 

一。下面对这个函数进行注解一下:

int get_nic_IP_Address() //获取各网卡IP地址、子网掩码

{

       struct ifreq ifreq;  //声明一个struct ifreq结构体(这个结构体中有很多重要的参数,具体可以参照第二的补充)

       int sock;

       int i;

       int tmpint;

       read_dev();//这个函数的功能是获得网卡名字(保存在下面提到的sys_nic_ip[][]数组中)并计算网卡总数(就是下面的sys_nic_count)

       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和子网掩码的过程。。。。

 

ioctl 获取本机网卡ip地址 | socket()

#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;
}
 
此方法仅供参考,也适用于获取其他信息。

--------------------------------------------------------------------------------------------------------------------------------------------------------------

简述:
创建一个套接口。
#include <winsock.h>
SOCKET PASCAL FAR socket( int af, int type, int protocol);
af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPA Internet地址格式。
type:新套接口的类型描述。
protocol:套接口所用的协议。如调用者不想指定,可用0。

注释


  socket()函数用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源。如果协议protocol未指定(等于0),则使用缺省的连接方式。
对于使用一给定地址族的某一特定套接口,只支持一种协议。但地址族可设为AF_UNSPEC(未指定),这样的话协议参数就要指定了。协议号特定于进行通讯的“通讯域”。支持下述类型描述:
类型 解释
SOCK_STREAM 提供有序的、可靠的、双向的和基于连接的字节流,使用带外数据传送机制,为Internet地址族使用TCP。
SOCK_DGRAM 支持无连接的、不可靠的和使用固定大小(通常很小)缓冲区的数据报服务,为Internet地址族使用UDP。
SOCK_STREAM类型的套接口为全双向的字节流。对于流类套接口,在接收或发送数据前必需处于已连接状态。用connect()调用建立与另一套接口的连接,连接成功后,即可用send()和recv()传送数据。当会话结束后,调用closesocket()。带外数据根据规定用send()和recv()来接收。
实现SOCK_STREAM类型套接口的通讯协议保证数据不会丢失也不会重复。如果终端协议有缓冲区空间,且数据不能在一定时间成功发送,则认为连接中断,其后续的调用也将以WSAETIMEOUT错误返回。
SOCK_DGRAM类型套接口允许使用sendto()和recvfrom()从任意端口发送或接收数据报。如果这样一个套接口用connect()与一个指定端口连接,则可用send()和recv()与该端口进行数据报的发送与接收。

返回值


  若无错误发生,socket()返回引用新套接口的描述字。否则的话,返回INVAID_SOCKET错误,应用程序可通过WSAGetLastError()获取相应错误代码。
错误代码:
WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。
WSAEAFNOSUPPORT:不支持指定的地址族。
WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。
WSAEMFILE:无可用文件描述字。
WSAENOBUFS:无可用缓冲区,无法创建套接口。
WSAEPROTONOSUPPORT:不支持指定的协议。
WSAEPROTOTYPE:指定的协议不适用于本套接口。
WSAESOCKTNOSUPPORT:本地址族中不支持该类型套接口。

参见


  accept()bind(), connect(), getsockname()getsockopt(), setsockopt(), listen(), recv(), recvfrom(), select(), send(), sendto(), shutdown(), ioctlsocket().4.2 数据库例程
socket()
函数原型:
SOCKET WSAAPI  socket(
int af,
int type,
int protocol
);
该函数及参数定义包含在winsock2.h头文件中,在MSDN中查不到具体参数。
参数选项及定义:
地址族af:(常用AF_INET实现TCP/UDP协议)
#define AF_UNSPEC       0               /* unspecified */
#define AF_UNIX         1               /* local to host (pipes, portals) */
#define AF_INET         2               /* internetwork: UDP, TCP, etc. */
#define AF_IMPLINK      3               /* arpanet imp addresses */
#define AF_PUP          4               /* pup protocols: e.g. BSP */
#define AF_CHAOS        5               /* mit CHAOS protocols */
#define AF_NS           6               /* XEROX NS protocols */
#define AF_IPX          AF_NS           /* IPX protocols: IPX, SPX, etc. */
#define AF_ISO          7               /* ISO protocols */
#define AF_OSI          AF_ISO          /* OSI is ISO */
#define AF_ECMA         8               /* european computer manufacturers */ 
#define AF_DATAKIT      9               /* datakit protocols */
#define AF_CCITT        10              /* CCITT protocols, X.25 etc */
#define AF_SNA          11              /* IBM SNA */ 
#define AF_DECnet       12              /* DECnet */
#define AF_DLI          13              /* Direct data link interface */
#define AF_LAT          14              /* LAT */
#define AF_HYLINK       15              /* NSC Hyperchannel */
#define AF_APPLETALK    16              /* AppleTalk */
#define AF_NETBIOS      17              /* NetBios-style addresses */
#define AF_VOICEVIEW    18              /* VoiceView */
#define AF_FIREFOX      19              /* Protocols from Firefox */
#define AF_UNKNOWN1     20              /* Somebody is using this! */
#define AF_BAN          21              /* Banyan */
#define AF_ATM          22              /* Native ATM Services */
#define AF_INET6        23              /* Internetwork Version 6 */
#define AF_CLUSTER      24              /* Microsoft Wolfpack */
#define AF_12844        25              /* IEEE 1284.4 WG AF */
套接字类型type:
#define SOCK_STREAM     1               /* stream socket */
#define SOCK_DGRAM      2               /* datagram socket */
#define SOCK_RAW        3               /* raw-protocol interface */
#define SOCK_RDM        4               /* reliably-delivered message */
#define SOCK_SEQPACKET  5               /* sequenced packet stream */
协议类型protocol:
#define IPPROTO_IP              0               /* dummy for IP */
#define IPPROTO_ICMP            1               /* control message protocol */
#define IPPROTO_IGMP            2               /* internet group management protocol */
#define IPPROTO_GGP             3               /* gateway^2 (deprecated) */
#define IPPROTO_TCP             6               /* tcp */
#define IPPROTO_PUP             12              /* pup */
#define IPPROTO_UDP             17              /* user datagram protocol */
#define IPPROTO_IDP             22              /* xns idp */
#define IPPROTO_ND              77              /* UNOFFICIAL net disk proto */