编程接口指南

广播及确定网络配置

IPv6 不支持广播,仅 IPv4 支持广播。

数据报套接字发送的消息可以广播到已连接网络中的所有主机。此网络必须支持广播,因为系统不在软件中提供任何广播模拟。广播消息会给网络带来很高的负载,因为广播消息会强制网络中的每台主机都为其服务。通常出于以下两个原因之一使用广播:

要发送广播消息,请创建一个 Internet 数据报套接字:

s = socket(AF_INET, SOCK_DGRAM, 0);

将一个端口号绑定到此套接字:

sin.sin_family = AF_INET;

sin.sin_addr.s_addr = htonl(INADDR_ANY);

sin.sin_port = htons(MYPORT);

bind(s, (struct sockaddr *) &sin, sizeof sin);

通过将数据报发送到网络的广播地址可以仅在此网络中进行广播。 通过将数据报发送到 netinet/in.h 中定义的特殊地址 INADDR_BROADCAST 可以在所有已连接的网络中进行广播。

系统会提供一种机制来确定多条有关系统上网络接口的信息。这些信息包括 IP 地址和广播地址。SIOCGIFCONF ioctl(2) 调用会以单一的 ifconf 结构返回主机的接口配置。此结构包含一个 ifreq 结构数组。主机连接的每个网络接口所支持的所有地址族都具有自己的 ifreq 结构。

以下示例给出了 net/if.h 中定义的 ifreq 结构。


示例 7–14 net/if.h 头文件

struct ifreq {

#define IFNAMSIZ 16

char ifr_name[IFNAMSIZ]; /* if name, e.g., "en0" */

union {

  struct sockaddr ifru_addr;

  struct sockaddr ifru_dstaddr;

  char ifru_oname[IFNAMSIZ]; /* other if name */

  struct sockaddr ifru_broadaddr;

  short ifru_flags;

  int ifru_metric;

  char ifru_data[1]; /* interface dependent data */

  char ifru_enaddr[6];

} ifr_ifru;

#define ifr_addr ifr_ifru.ifru_addr

#define ifr_dstaddr ifr_ifru.ifru_dstaddr

#define ifr_oname ifr_ifru.ifru_oname

#define ifr_broadaddr ifr_ifru.ifru_broadaddr

#define ifr_flags ifr_ifru.ifru_flags

#define ifr_metric ifr_ifru.ifru_metric

#define ifr_data ifr_ifru.ifru_data

#define ifr_enaddr ifr_ifru.ifru_enaddr

};

可以获取接口配置的调用为:

/*

 * Do SIOCGIFNUM ioctl to find the number of interfaces

 *

 * Allocate space for number of interfaces found

 *

 * Do SIOCGIFCONF with allocated buffer

 *

 */

if (ioctl(s, SIOCGIFNUM, (char *)&numifs) == -1) {

        numifs = MAXIFS;

}

bufsize = numifs * sizeof(struct ifreq);

reqbuf = (struct ifreq *)malloc(bufsize);

if (reqbuf == NULL) {

        fprintf(stderr, "out of memory\n");

        exit(1);

}

ifc.ifc_buf = (caddr_t)&reqbuf[0];

ifc.ifc_len = bufsize;

if (ioctl(s, SIOCGIFCONF, (char *)&ifc) == -1) {

        perror("ioctl(SIOCGIFCONF)");

        exit(1);

}

...

}

使用此调用之后,buf 将包含一个 ifreq 结构数组。主机所连接的每个网络都具有一个关联的 ifreq 结构。这些结构的排序顺序有两种:

ifc.ifc_len 的值设置为 ifreq 结构所使用的字节数。

每个结构都有一组指示相应网络为运行或关闭、点对点或广播等等的接口标志。以下示例说明了 ioctl(2) 针对 ifreq 结构所指定的接口返回 SIOCGIFFLAGS 标志。


示例 7–15 获取接口标志

struct ifreq *ifr;

ifr = ifc.ifc_req;

for (n = ifc.ifc_len/sizeof (struct ifreq); --n >= 0; ifr++) {

   /*

    * Be careful not to use an interface devoted to an address

    * family other than those intended.

    */

   if (ifr->ifr_addr.sa_family != AF_INET)

      continue;

   if (ioctl(s, SIOCGIFFLAGS, (char *) ifr) < 0) {

      ...

   }

   /* Skip boring cases */

   if ((ifr->ifr_flags & IFF_UP) == 0 ||

      (ifr->ifr_flags & IFF_LOOPBACK) ||

      (ifr->ifr_flags & (IFF_BROADCAST | IFF_POINTOPOINT)) == 0)

      continue;

}

以下示例使用 SIOGGIFBRDADDR ioctl(2) 命令来获取接口的广播地址。


示例 7–16 接口的广播地址

if (ioctl(s, SIOCGIFBRDADDR, (char *) ifr) < 0) {

  ...

}

memcpy((char *) &dst, (char *) &ifr->ifr_broadaddr,

  sizeof ifr->ifr_broadaddr);

还可以使用 SIOGGIFBRDADDR ioctl(2) 来获取点对点接口的目标地址。

获取接口广播地址之后,使用 sendto(3SOCKET) 传输广播数据报:

sendto(s, buf, buflen, 0, (struct sockaddr *)&dst, sizeof dst);

针对主机所连接的每个接口使用 sendto(3SOCKET),前提是此接口支持广播或点对点寻址。