定义部分参考 http://colobu.com/2014/10/21/udp-and-unicast-multicast-broadcast-anycast/
单播(unicast):
是指封包在计算机网络的传输中,目的地址为单一目标的一种传输方式。它是现今网络应用最为广泛,通常所使用的网络协议或服务大多采用单播传输,例如一切基于TCP的协议。
组播(multicast):
也叫多播, 多点广播或群播。 指把信息同时传递给一组目的地址。它使用策略是最高效的,因为消息在每条网络链路上只需传递一次,而且只有在链路分叉的时候,消息才会被复制。
多播组通过 D 类 IP 地址和标准 UDP 端口号指定。D 类 IP 地址在 224.0.0.0 和 239.255.255.255 的范围内(包括两者)。地址 224.0.0.0 被保留,不应使用。
广播(broadcast):
是指封包在计算机网络中传输时,目的地址为网络中所有设备的一种传输方式。实际上,这里所说的“所有设备”也是限定在一个范围之中,称为“广播域”。
任播(anycast):
是一种网络寻址和路由的策略,使得资料可以根据路由拓朴来决定送到“最近”或“最好”的目的地。
单播:
每次只有两个实体相互通信,发送端和接收端都是唯一确定的。
在IPv4网络中,0.0.0.0到223.255.255.255属于单播地址。
你对小月月喊“小月月”,那么只有小月月回过头来答应你。
组播
“组播”这个词通常用来指代IP组播。IP组播是一种通过使用一个组播地址将数据在同一时间以高效的方式发往处于TCP/IP网络上的多个接收者的协议。此外,它还常用来与RTP等音视频协议相结合。
互联网架构师戴夫·克拉克是这样描述IP组播的:“你把数据包从一头放进去,网络就会试图将它们传递到想要得到它们的人那里。”
组播报文的目的地址使用D类IP地址, D类地址不能出现在IP报文的源IP地址字段。
你在大街上大喊一声“美女”, 会有一群女性回头看你。
组播地址(参考 iana)
组播组可以是永久的也可以是临时的。组播组地址中,有一部分由官方分配的,称为永久组播组。永久组播组保持不变的是它的ip地址,组中的成员构成可以发生变化。永久组播组中成员的数量都可以是任意的,甚至可以为零。那些没有保留下来供永久组播组使用的ip组播地址,可以被临时组播组利用。
- 224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
- 224.0.1.0~224.0.1.255是公用组播地址,Internetwork Control Block;
- 224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;
- 239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。
永久的组播地址:
- 224.0.0.0 基准地址(保留)
- 224.0.0.1 所有主机的地址 (包括所有路由器地址)
- 224.0.0.2 所有组播路由器的地址
- 224.0.0.3 不分配
- 224.0.0.4 dvmrp路由器
- 224.0.0.5 所有ospf路由器
- 224.0.0.6 ospf DR/BDR
- 224.0.0.7 st路由器
- 224.0.0.8 st主机
- 224.0.0.9 rip-2路由器
- 224.0.0.10 Eigrp路由器
- 224.0.0.11 活动代理
- 224.0.0.12 dhcp 服务器/中继代理
- 224.0.0.13 所有pim路由器
- 224.0.0.14 rsvp封装
- 224.0.0.15 所有cbt路由器
- 224.0.0.16 指定sbm
- 224.0.0.17 所有sbms
- 224.0.0.18 vrrp
以太网传输单播ip报文的时候,目的mac地址使用的是接收者的mac地址。但是在传输组播报文时,传输目的不再是一个具体的接收者,而是一个成员不确定的组,所以使用的是组播mac地址。组播mac地址是和组播ip地址对应的。iana(internet assigned number authority)规定,组播mac地址的高24bit为0x01005e,mac 地址的低23bit为组播ip地址的低23bit。
由于ip组播地址的后28位中只有23位被映射到mac地址,这样就会有32个ip组播地址映射到同一mac地址上。
广播
并非所有的计算机网络都支持广播,例如X.25网络和帧中继都不支持广播,而且也没有在“整个互联网范围中”的广播。IPv6亦不支持广播,广播相应的功能由组播代替。
通常,广播都是限制在局域网中的,比如以太网或令牌环网络。因为广播在局域网中造成的影响远比在广域网中小得多。
以太网和IPv4网都用全1的地址表示广播,分别是ff:ff:ff:ff:ff:ff和255.255.255.255。
令牌环网络使用IEEE 802.2控制域中的一个特殊值来表示广播。你在公司大喊一声“放假了”, 全部同事都会响应,大叫爽死了。
任播
任播是与单播、广播和组播不同的方式。
在单播中,在网络位址和网络节点之间存在一一对应的关系。
在广播和组播中,在网络位址和网络节点之间存在一对多的关系:每一个目的位址对应一群接收可以复制资讯的节点。
在任播中,在网络位址和网络节点之间存在一对多的关系:每一个位址对应一群接收节点,但在任何给定时间,只有其中之一可以接收到传送端来的资讯。
在互联网中,通常使用边界网关协议来实现任播。
作为老板,你在公司大喊一声“开发组的过来一个人”, 总会有一个人灰溜溜去响应, 挨批还是发钱啊?
TCP(传输控制协议) | UDP(用户数据报协议) | |
---|---|---|
是否连接 | 面向连接 | 面向非连接 |
传输可靠性 | 可靠的 | 不可靠的 |
应用场合 | 传输大量的数据 | 少量数据 |
速度 | 慢 | 快 |
package com.demo.test;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Scanner;
import java.util.Timer;
import java.util.TimerTask;
public class UdpDemo {
private static int PORT_A = 7777;
private static int PORT_B = 9999;
private static String MULTICAST_ADDRESS = "225.0.0.1";
private static String EXIT = "exit";
private enum SocketType {
UNICAST, // 单播
MULTICAST, // 多播(组播)
BROADCAST, // 广播
ANYCAST // 任播
}
private static SocketType type = SocketType.UNICAST;
static class UdpClient {
protected DatagramSocket socket = null;
private SocketType type;
public UdpClient(int port, SocketType type) {
this.type = type;
byte[] recvBuffer = new byte[1024];
final DatagramPacket recvPacket = new DatagramPacket(recvBuffer, recvBuffer.length);
try {
/*
* 1、本机地址
* InetAddress addr = InetAddress.getLocalHost();
* (注意本机地址和环回地址是不一样的。绑定到本机地址的话,本机发送到环回地址收不到,反之亦然)
*
* 2、环回地址
* InetAddress addr = InetAddress.getLoopbackAddress();
* InetAddress addr = InetAddress.getByName("127.0.0.1");
* InetAddress addr = InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 });
*
* 3、广播地址
* InetAddress addr = InetAddress.getByAddress(new byte[] { 255, 255, 255, 255 });
*/
switch (type) {
case UNICAST:
case BROADCAST:
socket = new DatagramSocket(port, InetAddress.getLoopbackAddress());
break;
case MULTICAST:
socket = new MulticastSocket(port);
((MulticastSocket) socket).joinGroup(InetAddress.getByName(MULTICAST_ADDRESS));
break;
case ANYCAST:
// 暂时未实现
return;
}
new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
socket.receive(recvPacket);
System.out.println("received packet from " + recvPacket.getAddress().getHostAddress() + " : " + recvPacket.getPort());
//注意由于DatagramPacket的缓冲区复用,本次收到的最后一个字符后并不会补'\0',而是使用一个长度标记
String msg = new String(recvPacket.getData(), recvPacket.getOffset(), recvPacket.getLength());
System.out.println("received " + msg);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
socket.close();
}
}
}
}).start();
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
public boolean send(InetAddress addr, int port, String msg) {
byte[] buffer = msg.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
System.out.println("send to " + addr.getHostAddress());
try {
switch (type) {
case UNICAST:
packet.setAddress(addr);
break;
case MULTICAST:
packet.setAddress(InetAddress.getByName(MULTICAST_ADDRESS));
break;
case BROADCAST:
packet.setAddress(InetAddress.getByAddress(new byte[] { (byte) 255, (byte) 255, (byte) 255, (byte) 255 }));
break;
case ANYCAST:
// 暂时未实现
return false;
}
packet.setPort(port);
socket.send(packet);
return true;
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}
static class UdpClientA extends UdpClient {
public UdpClientA() {
super(PORT_A, type);
}
public void startScanner() {
// scanner必须写在线程中,如果阻塞主线程,那么输出将无法打印出来
new Thread(new Runnable() {
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
String line;
InetAddress addr = InetAddress.getLoopbackAddress();
while (scanner.hasNext()) {
line = scanner.nextLine();
if(!send(addr, PORT_B, line)) {
break;
}
if (EXIT.equals(line)) {
break;
}
}
scanner.close();
}
}).start();
}
public void doSchedule() {
InetAddress addr;
try {
addr = InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 });
} catch (UnknownHostException e) {
e.printStackTrace();
return;
}
final InetAddress thisAddr = addr;
new Timer().schedule(new TimerTask() {
@Override
public void run() {
if(!send(thisAddr, PORT_B, "hello world")) {
this.cancel();
}
}
}, 0, 5000);
}
}
static class UdpClientB extends UdpClient {
public UdpClientB() {
super(PORT_B, type);
}
}
public static void main(String[] args) {
UdpClientA clientA = new UdpClientA();
clientA.startScanner();
clientA.doSchedule();
new UdpClientB();
}
}