## 1. 單播和廣播的比較
### UDP數據報單播示例:

1. 通過ARP將IP地址轉換為目的以太網地址:00:0a:95:79:bc:b4
2. 中間主機的以太網接口看到該幀后把它的目的以太網地址與自己的以太網地址(00:04:ac:17:bf:38)進行比較。既然它們不一致,該接口于是忽略這個幀。可見單播幀不會對該主機造成任何額外開銷,因為忽略它們的是接口而不是主機。
3. 右側主機的以太網接口也看到這個幀,當它比較該幀的目的以太網地址和自己的以太網地址時,會發現它們相同。該接口于是讀入整個幀,讀入完畢后可能產生一個硬件中斷,致使相應設備驅動程序從接口內存中讀取該幀。既然該幀類型為0x8000,該幀承載的分組于是被置于IP的輸入隊列。
?? 單播IP數據報僅通過目的IP地址指定的單個主機接收。子網上的其他主機都不受任何影響。
### UDP數據報廣播示例

1. 左側主機發送該數據報時候發現,IP地址定向為廣播地址,則將此IP映射為:ff:ff:ff:ff:ff:ff的以太網地址。這個地址使得該子網上的每一個以太網接口都接收該幀。
2. 右側的那個主機把該UDP數據報傳遞給綁定端口520的應用進程。一個應用進程無需就為接收廣播UDP數據報而進行任何特殊處理:它只需要創建一個UDP套接字,并把應用的端口號捆綁到其上。
3. 然而中間的那個主機沒有任何應用進程綁定UDP端口520.該主機的UDP代碼于是丟棄這個已收取的數據報。該主機絕不能發送一個ICMP端口不可達消息,因為這么做可能產生廣播風暴,即子網上大量主機幾乎同時產生一個響應,導致網絡在這段時間內不可用。
4. 左側主機的數據報也被傳遞給自己。
### 廣播存在的根本問題:
?? 子網上未參加相應廣播應用的所有主機也不得不沿著協議棧一路向上完整的處理收取的UDP廣播數據報,直到該數據報歷經UDP層時被丟棄為止。另外,子網上所有非IP的主機也不得不在數據鏈路層街搜完整的幀,然后在丟棄它。
### 廣播實例1:
tserv.c:
~~~
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <time.h>
#define MAXLINE 1024
#define SA struct sockaddr
int main(int argc, char **argv)
{
struct sockaddr_in srvaddr;
int sockfd, on = 1;
int num, i;
time_t ticks;
char mesg[MAXLINE + 1];
if (argc != 3){
printf("usage:%s<ip address><port>\n", argv[0]);
exit(0);
}
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(int));
bzero(&srvaddr, sizeof(srvaddr));
srvaddr.sin_family = AF_INET;
if (inet_pton(AF_INET, argv[1], &srvaddr.sin_addr) <= 0){
printf("wrong dest ip address\n");
exit(0);
}
srvaddr.sin_port = htons(atoi(argv[2]));
for ( ; ; ){
ticks = time(NULL);
snprintf(mesg, sizeof(mesg), "%.24s\r\n", ctime(&ticks));
sendto(sockfd, mesg, strlen(mesg), 0, (SA *)&srvaddr, sizeof(srvaddr));
sleep(5);
}
return 0;
}
~~~
tcli.c:
~~~
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/types.h>
#define MAXLINE 1024
#define SA struct sockaddr
int main(int argc, char **argv)
{
struct sockaddr_in cliaddr;
int sockfd, n, opt;
char mesg[MAXLINE + 1];
if (argc != 2){
printf("usage:%s<port>\n", argv[1]);
exit(0);
}
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sin_port = htons(atoi(argv[1]));
cliaddr.sin_addr.s_addr = htonl(INADDR_ANY);
opt = SO_REUSEADDR;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bind(sockfd, (SA *)&cliaddr, sizeof(cliaddr));
n = read(sockfd, mesg, MAXLINE);
if (n > 0){
mesg[n] = 0;
printf("%s\n", mesg);
}
return 0;
}
~~~
程序輸出:
服務端:
~~~
leichaojian@ThinkPad-T430i:~$ ./tserv 192.168.0.255 9876
~~~
客戶端1:
~~~
leichaojian@ThinkPad-T430i:~$ ./tcli 9876
Tue Oct 14 20:40:43 2014
~~~
客戶端2:
~~~
leichaojian@ThinkPad-T430i:~$ ./tcli 9876
Tue Oct 14 20:40:53 2014
~~~
### 廣播實例2:
bcastsrv.c:
~~~
#include <sys/socket.h>
#include <signal.h>
#include <stdio.h>
#include <netinet/in.h>
#include <time.h>
#include <errno.h>
extern int errno;
#define MAXLINE 1024
#define SA struct sockaddr
static void recvfrom_alarm(int signo);
void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen);
int main(int argc, char **argv)
{
struct sockaddr_in srvaddr;
int sockfd, on = 1;
int n;
char mesg[MAXLINE + 1];
if (argc != 3){
printf("usage:%s<ip addr><port>\n", argv[0]);
exit(0);
}
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&srvaddr,sizeof(srvaddr));
srvaddr.sin_family = AF_INET;
inet_pton(AF_INET, argv[1], &srvaddr.sin_addr);
srvaddr.sin_port = htons(atoi(argv[2]));
dg_cli(stdin, sockfd, (SA *)&srvaddr, sizeof(srvaddr));
return 0;
}
static void recvfrom_alarm(int signo)
{
return;
}
void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
int n;
const int on = 1;
char sendline[MAXLINE], recvline[MAXLINE + 1];
socklen_t len;
struct sockaddr *preply_addr;
preply_addr = malloc(servlen);
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
signal(SIGALRM, recvfrom_alarm);
while (fgets(sendline, MAXLINE, fp) != NULL){
sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
// alarm(3);
// for ( ; ; ){
len = servlen;
n = recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);
if (n < 0){
if (errno == EINTR)
break;
else
printf("recvfrom error\n");
} else{
recvline[n] = 0;
inet_ntop(AF_INET, &preply_addr, sendline, sizeof(sendline));
printf("from %s:%s\n", sendline, recvline);
}
// }
}
free(preply_addr);
}
~~~
bcastcli.c:
~~~
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
#define MAXLINE 1024
#define SA struct sockaddr
int main(int argc, char **argv)
{
struct sockaddr_in cliaddr;
int sockfd, n, len;
char mesg[MAXLINE];
time_t ticks;
if (argc != 2){
printf("usage:%s<port>\n", argv[0]);
exit(0);
}
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sin_port = htons(atoi(argv[1]));
cliaddr.sin_addr.s_addr = htonl(INADDR_ANY);
cliaddr.sin_family = AF_INET;
bind(sockfd, (SA *)&cliaddr, sizeof(cliaddr));
for ( ; ; ){
len = sizeof(cliaddr);
n = recvfrom(sockfd, mesg, MAXLINE, 0, (SA *)&cliaddr, &len);
if (n < 0){
// sleep(5);
continue;
}
mesg[n] = 0;
printf("recv: %s\n", mesg);
ticks = time(NULL);
snprintf(mesg, sizeof(mesg), "%.24s", ctime(&ticks));
sendto(sockfd, mesg, 2424, 0, (SA *)&cliaddr, len);
}
return 0;
}
~~~
服務端:
~~~
leichaojian@ThinkPad-T430i:~$ ./bcastsrv 192.168.0.255 9876
hello
from 16.224.187.0:Tue Oct 14 21:45:38 2014
what
from 16.224.187.0:Tue Oct 14 21:45:43 2014
ha
from 16.224.187.0:Tue Oct 14 21:45:47 2014
~~~
客戶端:
~~~
leichaojian@ThinkPad-T430i:~$ ./bcastcli 9876
recv: hello
recv: what
recv: ha
~~~