网络通信:单播、广播、组播
本文文件夹:
一、网络通信的分类、他们的定义和特点。
二、单播、广播、组播的传输信息的网络拓扑模型。
三、单播、广播、组播的编程实例。
一、网络通信的分类、他们的定义和特点。
二、单播、广播、组播的传输信息的网络拓扑模型。
(一)单播
如图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)单播实例:(一个echoserver,能够同一时候和多个用户通信)
head.h
#ifndef _HEAD_H_#define _HEAD_H_#include #include #include #include #include #include #include #include #include #include #include //fork#include #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 20typedef struct _USER{ char name[SIZE]; char passwd[SIZE];}usr;#define sys_err(_msg) error(EXIT_FAILURE,errno,_msg)/*errno是个全局变量,调用之后,能够在错误发生的情况下,也能输出——msg信息*作为调试用* */#endif
server.c
#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,代表client已经断线,返回-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与client进行数据的传输read(),send(); *6关闭套接字close(sockfd); * *注意:要掌握熟练掌握各个接口的调用 * 为了实现多client和服务器的交互,该怎么做 * */ 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); //一个服务器能够和多个client进程多次握手连接。 切记,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;}
(2)广播实例: sender.c :
#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;}
(3)组播实例:
sender.c :
#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);
//#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;}
recevier.c :
#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;}
參考文献:http://www.xici.net/d79537500.htm
未经同意,禁止转载