天天看點

廣播資料包-大聲說「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;
}