- 英文小冊原文位址:beej.us/guide/bgnet…
- 作者:Beej
- 中文翻譯位址:www.chanmufeng.com/posts/netwo…
到目前為止,我們已經讨論了怎麼從一台主機發送資料到另一台主機。但是,如果有這種可能,你肯定想同時把資料發送給多台主機。
用
UDP
(隻能用
UDP
,
TCP
不行)和标準的IPv4,可以通過一種叫做廣播(broadcasting)的機制來實作。IPv6不支援廣播,你得用一種更進階的技術——多點傳播(multicasting)來實作。這個技術我們這次不會講,畢竟我們現在還停留在IPv4的32位階段呢,就不異想天開了。
Wait!你别溜走,自己偷摸開始廣播,我還沒開始介紹呢。你必須設定套接字選項
SO_BROADCAST
,然後才能在網絡上發送廣播資料包,這就好比是在飛彈發射開關上蓋的一個小塑膠罩,你的一根指頭就可以控制所有!
但是,說真的,使用廣播資料包有一個危險,因為每個收到廣播包的系統都要撥開一層層像洋蔥皮一樣的封裝,直到系統知道這個資料是要發送到哪個 port 為止,然後系統會開始處理這個資料或者将其丢棄。
無論怎樣,接收廣播資料包的每台機器都要做很多工作,而且因為它們都在本地網絡上,是以可能會有很多機器做很多不必要的工作。
現在,有很多方法可以解決這個問題。。。
等一下,真的有很多方法嗎?
那是什麼表情阿?讓我告訴你吧,發送廣播包的方式有很多,是以重點就是:你該如何指定廣播資料的目地位址?
有兩種常用的方法:
- 将資料發送到指定子網的廣播位址,就是把子網( subnet's network)的主機( host)部分全部填
。例如,我的網絡是1
,我的子網路遮罩是192.168.1.0
,是以位址的最後一個位元組是我的主機号(因為根據網絡掩碼,前3個位元組是網絡号),是以我的廣播位址是255.255.255.0
。在Unix下,192.168.1.255
會告訴你這些資訊的。你可以将這種類型的廣播資料包發送到遠端網絡和本地網絡,但你會面臨資料包被目标路由器丢棄的風險。(如果他們不放棄它,那這些廣播資料可能會淹沒他們的區域網路。)ifconfig
- 将資料發送到“全局(global)”廣播位址。這是
,即INADDR_BROADCAST。許多機器會自動将其與您的網絡号碼「按位與」,以将其轉換為網絡廣播位址,但有些機器卻不會。具有諷刺意味的是,路由器不會把這種類型的廣播資料包從你的本地網絡轉發出去。255.255.255.255
$ 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;
}