天天看点

广播数据包-大声说「Hello,World」

  • 英文小册原文地址:​​beej.us/guide/bgnet…​​
  • 作者:Beej
  • 中文翻译地址:​​www.chanmufeng.com/posts/netwo…​​

到目前为止,我们已经讨论了怎么从一台主机发送数据到另一台主机。但是,如果有这种可能,你肯定想同时把数据发送给多台主机。

用​

​UDP​

​​(只能用​

​UDP​

​​,​

​TCP​

​不行)和标准的IPv4,可以通过一种叫做广播(broadcasting)的机制来实现。IPv6不支持广播,你得用一种更高级的技术——多播(multicasting)来实现。这个技术我们这次不会讲,毕竟我们现在还停留在IPv4的32位阶段呢,就不异想天开了。

Wait!你别溜走,自己偷摸开始广播,我还没开始介绍呢。你必须设置套接字选项​

​SO_BROADCAST​

​,然后才能在网络上发送广播数据包,这就好比是在导弹发射开关上盖的一个小塑料罩,你的一根指头就可以控制所有!

但是,说真的,使用广播数据包有一个危险,因为每个收到广播包的系统都要拨开一层层像洋葱皮一样的封装,直到系统知道这个资料是要发送到哪个 port 为止,然后系统会开始处理这个数据或者将其丢弃。

无论怎样,接收广播数据包的每台机器都要做很多工作,而且因为它们都在本地网络上,所以可能会有很多机器做很多不必要的工作。

现在,有很多方法可以解决这个问题。。。

等一下,真的有很多方法吗?

那是什么表情阿?让我告诉你吧,发送广播包的方式有很多,所以重点就是:你该如何指定广播数据的目地地址?

有两种常用的方法:

  1. 将数据发送到指定子网的广播地址,就是把子网( subnet's network)的主机( host)部分全部填​

    ​1​

    ​​。例如,我的网络是​

    ​192.168.1.0​

    ​​,我的子网掩码是​

    ​255.255.255.0​

    ​​,所以地址的最后一个字节是我的主机号(因为根据网络掩码,前3个字节是网络号),所以我的广播地址是​

    ​192.168.1.255​

    ​​。在Unix下,​

    ​ifconfig​

    ​会告诉你这些信息的。你可以将这种类型的广播数据包发送到远程网络和本地网络,但你会面临数据包被目标路由器丢弃的风险。(如果他们不放弃它,那这些广播数据可能会淹没他们的局域网。)
  2. 将数据发送到“全局(global)”广播地址。这是​

    ​255.255.255.255​

    ​,即INADDR_BROADCAST。许多机器会自动将其与您的网络号码「按位与」,以将其转换为网络广播地址,但有些机器却不会。具有讽刺意味的是,路由器不会把这种类型的广播数据包从你的本地网络转发出去。
$ talker 192.168.1.2 foo
sent 3 bytes to 192.168.1.2
$ talker 192.168.1.255 foo
sendto: Permission denied
$ talker 255.255.255.255 foo
sendto: Permission denied      
/*
** broadcaster.c -- a datagram "client" like talker.c, except
**                  this one can broadcast
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define SERVERPORT 4950 // the port users will be connecting to

int main(int argc, char *argv[])
{
  int sockfd;
  struct sockaddr_in their_addr; // connector's address information
  struct hostent *he;
  int numbytes;
  int broadcast = 1;
  //char broadcast = '1'; // if that doesn't work, try this

  if (argc != 3) {
    fprintf(stderr,"usage: broadcaster hostname message\n");
    exit(1);
  }

  if ((he=gethostbyname(argv[1])) == NULL) {  // get the host info
    perror("gethostbyname");
    exit(1);
  }

  if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
    perror("socket");
    exit(1);
  }

  // this call is what allows broadcast packets to be sent:
  if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast,
    sizeof broadcast) == -1) {
    perror("setsockopt (SO_BROADCAST)");
    exit(1);
  }

  their_addr.sin_family = AF_INET;   // host byte order
  their_addr.sin_port = htons(SERVERPORT); // short, network byte order
  their_addr.sin_addr = *((struct in_addr *)he->h_addr);
  memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);

  if ((numbytes=sendto(sockfd, argv[2], strlen(argv[2]), 0,
       (struct sockaddr *)&their_addr, sizeof their_addr)) == -1) {
    perror("sendto");
    exit(1);
  }

  printf("sent %d bytes to %s\n", numbytes,
    inet_ntoa(their_addr.sin_addr));

  close(sockfd);

  return 0;
}