• socket,ioctl获取ip


    总结一下,今天学习的关于通过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 ", 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]
    ", ifreq->ifr_name);
          printf("local addr = [%s]
    ", 
                          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 */
  • 相关阅读:
    sql server 中 bit 字段的 查询方法
    C# 4.0新特性"协变"与"逆变"以及背后的编程思想
    marginleft是做边距,是宽度 left是定位盒子左上角左边位置的一个点
    什么是企业库 怎么使用企业库 企业库的好处 企业库的目标
    SQL各种Join用法(Full,Left,Out,Inner)
    JavaScript计算两个文本框内数据的乘积(四舍五入保留两位小数)
    SQL VIEW(视图)
    Sql ISNULL() 函数
    left join 和 left outer join 有什么区别
    18个不常见的C#关键字,您使用过几个?
  • 原文地址:https://www.cnblogs.com/LxwEmbedded/p/4728156.html
Copyright © 2020-2023  润新知