UDP广播与多播

测试者: Space Sun

使用UDP协议进行信息的传输之前不需要建议连接。换句话说就是客户端向服务器发送信息,客户端只需要给出服务器的ip地址和端口号,然后将信息封装到一个待发送的报文中并且发送出去。至于服务器端是否存在,或者能否收到该报文,客户端根本不用管。

通常我们讨论的udp的程序都是一对一的单播程序。本章将讨论一对多的服务:广播(broadcast)、多播(multicast)。对于广播,网络中的所有主机都会接收一份数据副本。对于多播,消息只是发送到一个多播地址,网络知识将数据分发给哪些表示想要接收发送到该多播地址的数据的主机。总得来说,只有UDP套接字允许广播或多播。

UDP广播

广播UDP与单播UDP的区别就是IP地址不同,广播使用广播地址255.255.255.255,将消息发送到在同一广播网络上的每个主机。值得强调的是:本地广播信息是不会被路由器转发。当然这是十分容易理解的,因为如果路由器转发了广播信息,那么势必会引起网络瘫痪。这也是为什么IP协议的设计者故意没有定义互联网范围的广播机制。

广播地址通常用于在网络游戏中处于同一本地网络的玩家之间交流状态信息等。广播就不在写演示程序了,读者可以将ECHO程序的ip地址改为广播地址即可。

其实广播顾名思义,就是想局域网内所有的人说话,但是广播还是要指明接收者的端口号的,因为不可能接受者的所有端口都来收听广播。

UDP多播

同样的UDP多播也要指明接受者的端口号,而且与广播相似的是多播与单播之间的区别还在于地址。ipv4中的多播地址范围是:224.0.0.0到239.255.255.255。在JAVA中,多播一样十分好实现,要实现多播,就要用到MulticastSocket类,其实该类就是DatagramSocket的子类,在使用时除了多播自己的一些特性外,把它当做DatagramSocket类使用就可以了。下面将给出一个简单的多播接受数据的例子:

// 发送端
#include
#include
#include
#include
#include
#include
#include
#include
#include
usingnamespacestd;
intmain()
{
setvbuf(stdout, NULL, _IONBF, 0);
fflush(stdout);
intsock = -1;
if((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
cout<
returnfalse;
}
constintopt = 1;
//设置该套接字为广播类型,
intnb = 0;
nb = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&opt,sizeof(opt));
if(nb == -1)
{
cout<
returnfalse;
}
structsockaddr_in addrto;
bzero(&addrto,sizeof(structsockaddr_in));
addrto.sin_family=AF_INET;
addrto.sin_addr.s_addr=htonl(INADDR_BROADCAST);
addrto.sin_port=htons(6000);
intnlen=sizeof(addrto);
while(1)
{
sleep(1);
//从广播地址发送消息
charsmsg[] = {"abcdef"};
intret=sendto(sock, smsg, strlen(smsg), 0, (sockaddr*)&addrto, nlen);
if(ret<0)
{
cout<
}
else
{
printf("ok ");
}
}
return0;
}
// 接收端 
#include
#include
#include
#include
#include
#include
#include
#include
#include
usingnamespacestd;
intmain()
{
setvbuf(stdout, NULL, _IONBF, 0);
fflush(stdout);
// 绑定地址
structsockaddr_in addrto;
bzero(&addrto,sizeof(structsockaddr_in));
addrto.sin_family = AF_INET;
addrto.sin_addr.s_addr = htonl(INADDR_ANY);
addrto.sin_port = htons(6000);
// 广播地址
structsockaddr_in from;
bzero(&from,sizeof(structsockaddr_in));
from.sin_family = AF_INET;
from.sin_addr.s_addr = htonl(INADDR_ANY);
from.sin_port = htons(6000);
intsock = -1;
if((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
cout<
returnfalse;
}
constintopt = 1;
//设置该套接字为广播类型,
intnb = 0;
nb = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&opt,sizeof(opt));
if(nb == -1)
{
cout<
returnfalse;
}
if(bind(sock,(structsockaddr *)&(addrto),sizeof(structsockaddr_in)) == -1)
{
cout<
returnfalse;
}
intlen =sizeof(sockaddr_in);
charsmsg[100] = {0};
while(1)
{
//从广播地址接受消息
intret=recvfrom(sock, smsg, 100, 0, (structsockaddr*)&from,(socklen_t*)&len);
if(ret<=0)
{
cout<
}
else
{
printf("%s\t", smsg);
}
sleep(1);
}
return0;
}

自已在Linux虚拟机下测试可以成功, 前提是要把主机设置在同一网段内,

还有就是不要忘记关闭Linux的防火墙.. 可以使用setup命令进行设置。

(我在测试的时候只能发送不收接收,折磨了我半天,后来才想到是Linux防火墙的问题。。)

UDP组播

实现服务器对客户端分组播放(发送数据)。

实现步骤:

server.c

1、创建服务器套接字

#include
#include
int socket(int domain, int type, int protocol); 返回套接字

2、构建服务器地址结构

struct sockaddr_in serveraddr;
#include
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); //IP
serveraddr.sin_port = htons(SERVER_PORT);//端口

3、绑定地址

int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);

4、构建组播属性结构

struct ip_mreqn group;
#include
inet_pton(AF_INET,GROUP,&group.imr_multiaddr);//设置组播地址
net_pton(AF_INET,"0.0.0.0",&group.imr_address);//设置本地地址
group.imr_ifindex=if_nametoindex("ent0");//设置网卡接口

5、设置组播权限和属性

setsockopt(sockfd,IPPROTO_IP,IP_MULTICAST_IF,&group,
sizeof(group));//设置组播权限及选项

6、设置客户端组播地址

struct sockaddr_in cliaddr;
bzero(&cliaddr,sizeof(cliaddr));
cliaddr.sin_family=AF_INET;
inet_pton(AF_INET,GROUP,&cliaddr.sin_addr.s_addr);
cliaddr.sin_port=htons(CLIENT_PORT);

7、发送数据

sendto(sockfd,buf,strlen(buf),0,(structsockaddr*)&cliaddr,
sizeof(cliaddr));//往组播地址发送信息,返回数据大小

client.c

1、创建客户端套接字

2、构建客户端地址结构

3、绑定地址

4、构建组播结构

5、设置组播权限和属性

6、接收数据

#include
#include
len=recvfrom(confd,buf,sizeof(buf),0,NULL,0);//接收数据

关于虚拟机的网卡配置,建议选择桥接模式。NAT的模式的话,是受限制的,可能会收不到广播消息。

具体的参考网上的文章吧。。