Java UDP 單播、多播(組播)、廣播、任播(未實現)
單播、組播、廣播、任播的定義
單播(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的區別
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();
}
}