您的位置:首页 > 理论基础 > 计算机网络

网络通信:单播、广播、组播

2014-07-04 13:12 387 查看
                                                                                                 
网络通信:单播、广播、组播

本文目录:

一、网络通信的分类、他们的定义和特点。

二、单播、广播、组播的传输信息的网络拓扑模型。

三、单播、广播、组播的编程实例。

一、网络通信的分类、他们的定义和特点。










二、单播、广播、组播的传输信息的网络拓扑模型。

(一)单播

如图8-1 所示,网络中存在信息发送者Source,UserA 和UserC 提出信息需求,网络采用单播方式传输信息。

                                                




单播传输特点归纳如下:
* Source 向每个Receiver 地址发送一份独立的拷贝信息:packets for UserA;packets for UserC。
* 网络为每个Receiver 分别建立一条独立的数据传送通路:Source→ RouterB → RouterE → RouterD → UserA;Source → RouterB → RouterE → RouterF → UserC。
单播方式下,网络中传输的信息量和需求该信息的用户量成正比,当需求该信息的用户量较大时,网络中将出现多份相同信息流。此时,带宽成为保证网络传输质量的重要瓶颈。
单播方式较适合用户稀少的网络,不利于信息规模化发送。

(二)广播


如图8-2 所示,网络中存在信息发送者Source,UserA 和UserC 提出信息需求,网络采用广播方式传输信息。
                            



广播传输特点归纳如下:
* Source 向本网络广播地址发送且仅发送一份报文:packets for all the network。
* 网络将报文拷贝传送到所有网段,不管是否需要,保证信息到达网络中所有的路由器和用户:UserB 也同样接收到一份拷贝。
广播方式下,网络中所有用户都能接收到该信息,当网络中需求该信息的用户量很小时,网络资源利用率将非常低,带宽浪费严重。不需要这些信息的用户也会受到影响。
广播方式较适合用户稠密的网络,信息安全性和有偿服务得不到保障。 

(三)多播


如图8-3 所示,网络中存在信息发送者Source、UserA 和UserC 提出信息需求,网络采用组播方式传输信息。

                                            




组播传输特点归纳如下:
* Multicast group 称为组播组,使用一个IP 组播地址标识。UserA 和UserC 两个信息接收者,加入该组播组,从而可以接收发往该组播组的数据。
* Source 称为组播源,向该组播组地址发送且仅发送一份报文:packets for the multicast group。网络传输过程中,相同的组播数据流在每一条链路上最多仅有一份。相比单播来说,使用组播方式传递信息,用户的增加不会显著增加网络的负载。
* 根据组播组成员的分布情况,组播路由协议为多目的端的数据包转送建立树型路由。报文在尽可能远的分叉路口(如RouterE)才开始复制和分发,最终传送到组播组成员。相比广播来说,组播数据仅被传输到有接收者的地方,不会造成网络资源的浪费。
* 网络中支持组播功能的路由器称为“组播路由器”,不仅提供组播路由功能,还能够在与网络用户连接的末梢网段上提供组成员管理功能(如RouterD 和RouterF)。同时,自己本身也可能是组播组成员。
* 组播组中的成员是动态的,网络中的用户主机可以在任何时刻加入和离开组播组。组成员可能广泛分布在网络中的任何地方。组播源通常不会同时是其发送数据的接收者,即不属于其对应的目的组播组。
* 一个源可以同时向多个组播组发送数据;多个源可以同时向一个组播组发送报文。
* 为了帮助理解,可以类比收看某电视频道的节目。
* 组播组是发送者和接收者之间的一个约定,如同电视频道。
* 电视台是组播源,它向某频道内发送数据。
* 电视机是接收者主机,观众打开电视机选择收看某频道的节目,表示主机加入某组播组;然后电视机播放该频道电视节目,表示主机接收到发送给这个组的数据。
* 观众可以随时控制电视机的开关和频道间的切换,表示主机动态的加入或退出某组播组。

三、单播、广播、组播的编程实例。


(1)单播实例:(一个echo服务器,可以同时和多个用户通信)

head.h

<span style="font-size:12px;">#ifndef _HEAD_H_
#define _HEAD_H_

#include<stdio.h>
#include<string.h>
#include<strings.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<error.h>
#include<errno.h>
#include<unistd.h> //fork
#include<sys/wait.h>

#define SER_PORT 50004
#define SER_IP "192.168.192.128"
#define CLIENT_PORT 50000
#define CLIENT_IP "192.168.192.128"

#define BUFF_SIZE 100
#define SIZE 20

typedef struct _USER
{
char name[SIZE];
char passwd[SIZE];
}usr;
#define sys_err(_msg) error(EXIT_FAILURE,errno,_msg)
/*errno是个全局变量,调用之后,可以在发生错误的情况下,也能输出——msg信息
*作为调试用
* */
#endif
</span>

server.c

<span style="font-size:12px;">#include "head.h"
int socket_init()
{
int sockfd;
struct sockaddr_in sockaddr;
if((( sockfd=socket(AF_INET,SOCK_STREAM,0))==-1))
sys_err("socket");

bzero(&sockaddr,sizeof(sockaddr));
sockaddr.sin_family=AF_INET;
sockaddr.sin_port=htons(SER_PORT);
sockaddr.sin_addr.s_addr=inet_addr("127.0.0.1");
printf("%d=====\n",sockfd);

if((bind(sockfd,(struct sockaddr*)&sockaddr,sizeof(sockaddr)))==-1)
sys_err("bind");

listen(sockfd,5); //要懂得监听的作用,其实就是建立一个缓冲队列,让创建的队列最多可以允许五个套接字进入
return sockfd;
}

int do_echo(int connectfd)
{
char sbuf[BUFF_SIZE];
char rbuf[BUFF_SIZE];
int n;
printf("int the server ==========\n");
while(1)
{
printf("===============in the do_echo\n");
bzero(rbuf,BUFF_SIZE);
if((n=recv(connectfd,rbuf,BUFF_SIZE,0))==-1)
sys_err("recv");

if(n==0) //如果接受为0,代表客户端已经断线,返回-1
return -1;

printf("server:rbuf==%s\n",rbuf);
sprintf(sbuf,"%s %s",rbuf,"---------echo");

send(connectfd,sbuf,strlen(sbuf),0);
}

return ;
}
int main(int argc,const char * argv[])
{
//步骤:
/*
*1 创建套接字socket();
*2 与IP地址绑定bind();
*3监听 listen();
*4如果有链接,接受链接,进行三次握手accept();
*5与客户端进行数据的传输read(),send();
*6关闭套接字close(sockfd);
*
*注意:要掌握熟练掌握各个接口的调用
* 为了实现多客户端和服务器的交互,该怎么做
* */

int sockfd;//具有基本属性的套接字描述符
int connectfd;//进行三次握手之后的套接字描述符

sockfd=socket_init();
//listen(sockfd,5); //要懂得监听的作用,其实就是建立一个缓冲队列,让创建的将要
//连接的套接字描述符进队和出队
/*
*
*
* The accept() system call is used with connection-based socket types
* (SOCK_STREAM, SOCK_SEQPACKET). It extracts the first connection request on the
* queue of pending connections for the listening socket, sockfd, creates a new
*
*
* */
pid_t pid;

signal(SIGCHLD,SIG_IGN);

while(1)
{

connectfd=accept(sockfd,NULL,NULL); //一个服务器可以和多个客户端进程多次握手连接。 切记,connect()函数在UDP协议中也可以用,不是TCP的专用
if(connectfd == -1)
sys_err("accept");

if(-1==(pid=fork()))
{
perror("fork");
}
else if(pid==0)
{

printf("do_echo===%d\n", do_echo(connectfd));
close(connectfd); //将子进程从父进程复制到的套接字资源,关闭了
close(sockfd);
}
}
close(connectfd);//父进程关闭自己的套接字
close(sockfd);
return 0;

}</span><span style="font-size:14px;">
</span>

(2)广播实例:
sender.c  :

<span style="font-size:14px;">#include"head.h"
int main(int argc,const char * argv[])
{

int sockfd;
struct sockaddr_in sockaddr;
char rbuf[BUFF_SIZE];
if(-1==(sockfd=socket(AF_INET,SOCK_DGRAM,0))) //由于TCP只支持一对一通信,UDP可以一对一,一对多,多对一,多对多。由于这个广播。是多对多。所以只能是SOCK_DGRAM

perror("socket");

bzero(&sockaddr,sizeof(sockaddr));
sockaddr.sin_family=AF_INET;
sockaddr.sin_port=htons(BRAOD_PORT); //#define BRAOD_PORT 50001
sockaddr.sin_addr.s_addr=inet_addr(BRAOD_IP);//#define BRAOD_IP "192.168.1.255"

if(-1==bind(sockfd,(struct sockaddr *)&sockaddr,sizeof(sockaddr)))
perror("bind");

while(1)
{
bzero(&rbuf,sizeof(rbuf));
recvfrom(sockfd,rbuf,BUFF_SIZE,0,(struct sockaddr*)&sockaddr,sizeof(sockaddr));
printf("--------%s-------\n",rbuf);
}

close(sockfd);
return 0;
}</span>

(3)组播实例:


sender.c :

<span style="font-size:14px;">#include"head.h"
int main(int argc ,const char * argv[])
{
/*
*创建组播发送方的步骤:
*创建套接字
*设置组播地址(可选)
*发送数据
*关闭套接字
* */

int sockfd;
struct sockaddr_in multiaddr;
char sbuf[BUFF_SIZE]="hello,xiaowang....\n";
int n;
if(-1==(sockfd=socket(AF_INET,SOCK_DGRAM,0)))
perror("socket");

bzero(&multiaddr,sizeof(multiaddr));
multiaddr.sin_family=AF_INET;
multiaddr.sin_port=htons(MULTI_PORT); //#define MULTI_PORT 50002 只要是用户自定义端口即可
multiaddr.sin_addr.s_addr=inet_addr(MULTI_IP);</span>
<span style="font-size:14px;">            //#define MULTI_IP "224.4.4.4"  D类地址是组播地址224.0.0.1---239.255.255.254

while(1)
{
sleep(1);
if(-1==(n=sendto(sockfd,sbuf,strlen(sbuf),0,(struct sockaddr*)&multiaddr,sizeof(multiaddr))))
perror("sendto");

printf("in the sending..........\n");
}
return 0;
}</span>


recevier.c :

<span style="font-size:14px;">#include"head.h"
int main(int argc,const char * argv[])
{
int sockfd;
int n;
char rbuf[BUFF_SIZE];
struct sockaddr_in groupaddr;

//创建套接字
if(-1==(sockfd=socket(AF_INET,SOCK_DGRAM,0)))
perror("socket");

//加入组播
struct ip_mreqn multiaddr; //这些在Man 2 setsockopt 时在signal的有关描述中可以查到的
multiaddr.imr_multiaddr.s_addr=inet_addr(MULTI_IP);
multiaddr.imr_address.s_addr=inet_addr(LOCAL_IP);
multiaddr.imr_ifindex=0;

//设置套接字允许发送组播信息的属性
if(-1==setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&multiaddr,sizeof(multiaddr)))
perror("setsockopt");

//绑定组播地址
bzero(&groupaddr,sizeof(groupaddr));
groupaddr.sin_family=AF_INET;
groupaddr.sin_port=htons(MULTI_PORT);
groupaddr.sin_addr.s_addr=inet_addr(MULTI_IP);

bind(sockfd,(struct sockaddr*)&groupaddr,sizeof(groupaddr));

int t;
while(1)
{
// bzero(&rbuf,sizeof(rbuf));
t=sizeof(groupaddr);
if(-1==(n=recvfrom(sockfd,rbuf,BUFF_SIZE,0,(struct sockaddr*)&groupaddr,&t)))
perror("recvfrom");
sleep(1);
printf("%s-----------\n",rbuf);
}
return 0;
}
</span>
 

参考文献:http://www.xici.net/d79537500.htm

                                                              未经允许,禁止转载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: