天天看點

Java UDP單點傳播、多點傳播、廣播、任播定義與實作

Github示例:https://github.com/Nuclear-Core-Learning/TCPIP-Socket/tree/master/src/Chapter4 

目錄

單點傳播、多點傳播、廣播、任播的定義

單點傳播(unicast)

多點傳播(multicast)

廣播(broadcast)

任播(anycast)

詳細介紹(來自維基百科)

單點傳播

多點傳播

廣播

任播

單點傳播、多點傳播、廣播的封裝

 多點傳播基礎示例

簡單封裝示例

單點傳播、多點傳播、廣播完整封裝示例

Java發送多點傳播(多點傳播)資料包 

單點傳播、多點傳播、廣播、任播的定義

單點傳播(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(使用者資料報協定)
是否連接配接 面向連接配接 面向非連接配接
傳輸可靠性 可靠的 不可靠的
應用場合 傳輸大量的資料 少量資料
速度

 多點傳播基礎示例

服務端:VoteMulticastReceiver.java

package Chapter4;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.util.Arrays;
import Chapter3.VoteMsg;
import Chapter3.VoteMsgTextCoder;
/**
 * 
 * @TODO     (功能說明:多點傳播多點傳播https://baike.baidu.com/item/%E7%BB%84%E6%92%AD%E5%9C%B0%E5%9D%80/6095039?fr=aladdin)
 * @author   PJL
 * @package  Chapter4
 * @motto    學習需要毅力,那就秀毅力
 * @file     VoteMulticastReceiver.java
 * @time     2019年10月28日 下午10:02:47
 * <li>
 * 224.0.0.0~224.0.0.255為預留的多點傳播位址(永久組位址),位址224.0.0.0保留不做配置設定,其它位址供路由協定使用;
 * 224.0.1.0~224.0.1.255是公用多點傳播位址,可以用于Internet;
 * 224.0.2.0~238.255.255.255為使用者可用的多點傳播位址(臨時組位址),全網範圍内有效;
 * 239.0.0.0~239.255.255.255為本地管理多點傳播位址,僅在特定的本地範圍内有效。
 * </li>
 */
public class VoteMulticastReceiver {

	public static void main(String[] args) throws IOException {
		
		if(args.length==0) {
			args=new String[2];
			args[0]="224.0.0.221";
			args[1]="8380";
		}

		if (args.length != 2) { // Test for correct # of args
			throw new IllegalArgumentException("Parameter(s): <Multicast Addr> <Port>");
		}

		InetAddress address = InetAddress.getByName(args[0]); // Multicast address
		if (!address.isMulticastAddress()) { // Test if multicast address
			throw new IllegalArgumentException("Not a multicast address");
		}

		int port = Integer.parseInt(args[1]); // Multicast port
		MulticastSocket sock = new MulticastSocket(port); // for receiving
		sock.joinGroup(address); // Join the multicast group

		VoteMsgTextCoder coder = new VoteMsgTextCoder();

		// Receive a datagram
		DatagramPacket packet = new DatagramPacket(new byte[VoteMsgTextCoder.MAX_WIRE_LENGTH],
				VoteMsgTextCoder.MAX_WIRE_LENGTH);
		sock.receive(packet);

		VoteMsg vote = coder.fromWire(Arrays.copyOfRange(packet.getData(), 0, packet.getLength()));

		System.out.println("Received Text-Encoded Request (" + packet.getLength() + " bytes): ");
		System.out.println(vote);

		sock.close();
	}
}
           

用戶端:VoteMulticastSender.java 

package Chapter4;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import Chapter3.VoteMsg;
import Chapter3.VoteMsgCoder;
import Chapter3.VoteMsgTextCoder;
/**
 * 
 * @TODO     (功能說明:多點傳播多點傳播https://baike.baidu.com/item/%E7%BB%84%E6%92%AD%E5%9C%B0%E5%9D%80/6095039?fr=aladdin)
 * @author   PJL
 * @package  Chapter4
 * @motto    學習需要毅力,那就秀毅力
 * @file     VoteMulticastSender.java
 * @time     2019年10月28日 下午10:03:11
 * <li>
 * 224.0.0.0~224.0.0.255為預留的多點傳播位址(永久組位址),位址224.0.0.0保留不做配置設定,其它位址供路由協定使用;
 * 224.0.1.0~224.0.1.255是公用多點傳播位址,可以用于Internet;
 * 224.0.2.0~238.255.255.255為使用者可用的多點傳播位址(臨時組位址),全網範圍内有效;
 * 239.0.0.0~239.255.255.255為本地管理多點傳播位址,僅在特定的本地範圍内有效。
 * </li>
 */
public class VoteMulticastSender {

	public static final int CANDIDATEID = 475;

	public static void main(String args[]) throws IOException {
		
		if(args.length==0) {
			args=new String[3];
			args[0]="224.0.0.221";
			args[1]="8380";
			args[2]="255";
		}

		if ((args.length < 2) || (args.length > 3)) { // Test # of args
			throw new IllegalArgumentException("Parameter(s): <Multicast Addr> <Port> [<TTL>]");
		}

		InetAddress destAddr = InetAddress.getByName(args[0]); // Destination
		if (!destAddr.isMulticastAddress()) { // Test if multicast address
			throw new IllegalArgumentException("Not a multicast address");
		}

		int destPort = Integer.parseInt(args[1]); // Destination port
		int TTL = (args.length == 3) ? Integer.parseInt(args[2]) : 1; // Set TTL

		// MulticastSocket 執行個體實際上就是一個 UDP 套接字(DatagramSocket)
		MulticastSocket sock = new MulticastSocket();
		sock.setTimeToLive(TTL); // Set TTL for all datagrams

		VoteMsgCoder coder = new VoteMsgTextCoder();

		VoteMsg vote = new VoteMsg(true, true, CANDIDATEID, 1000001L);

		// Create and send a datagram
		byte[] msg = coder.toWire(vote);
		DatagramPacket message = new DatagramPacket(msg, msg.length, destAddr, destPort);
		System.out.println("Sending Text-Encoded Request (" + msg.length + " bytes): ");
		System.out.println(vote);
		sock.send(message);

		sock.close();
	}
}
           

簡單封裝示例

服務端:MulticastListener.java

package Chapter4;

import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
/**
 * 
 * @TODO     (功能說明:服務端)
 * @author   PJL
 * @package  Chapter4
 * @motto    學習需要毅力,那就秀毅力
 * @file     MulticastListener.java
 * @time     2019年10月28日 下午10:52:20
 */
public class MulticastListener {

	private int port;
	private String host;

	public MulticastListener(String host, int port) {
		this.host = host;
		this.port = port;
	}

	public void listen() {
		byte[] data = new byte[256];
		try {
			InetAddress ip = InetAddress.getByName(this.host);
			MulticastSocket ms = new MulticastSocket(this.port);
			//ms.setBroadcast(true);
			ms.joinGroup(ip);
			DatagramPacket packet = new DatagramPacket(data, data.length);
			ms.receive(packet);
			String message = new String(packet.getData(), 0, packet.getLength());
			System.out.println(message);
			ms.send(packet);
			ms.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		int port = 1234;
		String host = "224.0.0.1";
		MulticastListener ml = new MulticastListener(host, port);
		while (true) {
			ml.listen();
		}
	}

}
           

用戶端:MulticastSender.java

package Chapter4;

import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
/**
 * 
 * @TODO     (功能說明:用戶端)
 * @author   PJL
 * @package  Chapter4
 * @motto    學習需要毅力,那就秀毅力
 * @file     MulticastSender.java
 * @time     2019年10月28日 下午10:52:01
 */
public class MulticastSender {

	private int port;
	private String host;
	private String data;

	public MulticastSender(String data, String host, int port) {
		this.data = data;
		this.host = host;
		this.port = port;
	}

	public void send() {
		try {
			InetAddress ip = InetAddress.getByName(this.host);
			DatagramPacket packet = new DatagramPacket(this.data.getBytes(), this.data.length(), ip, this.port);
			MulticastSocket ms = new MulticastSocket();
			ms.send(packet);
			ms.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		int port = 1234;
		String host = "224.0.0.1";
		String data = "hello world.";
		MulticastSender ms = new MulticastSender(data, host, port);
		ms.send();
	}

}
           

單點傳播、多點傳播、廣播完整封裝示例

UDPMulticastModel.java 任意播未實作。

package Chapter4;

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;
/**
 * 
 * @TODO     (功能說明:單點傳播、多點傳播、廣播https://www.2cto.com/kf/201610/555014.html)
 * @author   PJL
 * @package  Chapter4
 * @motto    學習需要毅力,那就秀毅力
 * @file     UDPMulticast.java
 * @time     2019年10月28日 下午10:23:00
 */
public class UDPMulticastModel {
	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();
	}

}
           

Java發送多點傳播(多點傳播)資料包 

java發送多點傳播(多點傳播)資料包。首先要加入多點傳播組,然後才能向多點傳播組發送多點傳播資料包和接收多點傳播資料包。

以下代碼,在區域網路中測試有效。

測試代碼

public static void main(String[] args)
{
    final MulticastSocket socket=createMulticastGroupAndJoin("224.0.0.0",5000);  //加入多點傳播組,設定多點傳播組的監聽端口為5000
    new Thread(new Runnable() {
        @Override
        public void run() {
            sendData(socket,"luanpeng".getBytes(),"224.0.0.0");  //向多點傳播組發送資料
        }
    }).start();
    String message = recieveData(socket,"224.0.0.0");//接收多點傳播組傳來的消息
    System.out.println(message);
}
           

建立一個多點傳播組,并加入此多點傳播組,多點傳播位址為D類ip,目的位址為224.0.0.0~239.255.255.255

public static MulticastSocket createMulticastGroupAndJoin(String groupurl,int port) // 建立一個多點傳播組并加入此組的函數
{
    try {
        InetAddress group = InetAddress.getByName(groupurl); // 設定多點傳播組的位址為239.0.0.0
        MulticastSocket socket = new MulticastSocket(port); // 初始化MulticastSocket類并将端口号與之關聯
        socket.setTimeToLive(1); // 設定多點傳播資料報的發送範圍為本地網絡
        socket.setSoTimeout(10000); // 設定套接字的接收資料報的最長時間
        socket.joinGroup(group); // 加入此多點傳播組
        return socket;
    } catch (Exception e1) {
        System.out.println("Error: " + e1); // 捕捉異常情況
        return null;
    }
}
           

向多點傳播組發送資料的函數,以UDP形式發送

public static void sendData(MulticastSocket socket,byte[] data,String groupurl) // 向多點傳播組發送資料的函數
{
    try {
        InetAddress group=InetAddress.getByName(groupurl);
        // 存儲在數組中
        DatagramPacket packet = new DatagramPacket(data, data.length, group, socket.getPort()); // 初始化DatagramPacket
        socket.send(packet); // 通過MulticastSocket執行個體端口向多點傳播組發送資料
        util.out("以UDP形式發送多點傳播封包");
    } catch (Exception e1) {
        System.out.println("Error: " + e1); // 捕捉異常情況
    }
}
           

從多點傳播組接收資料的函數

public static String recieveData(MulticastSocket socket,String groupurl)
{
    String message;
    try {
        InetAddress group=InetAddress.getByName(groupurl);
        byte[] data = new byte[512];
        DatagramPacket packet=new DatagramPacket(data, data.length,group,socket.getPort());
        socket.receive(packet); // 通過MulticastSocket執行個體端口從多點傳播組接收資料
        // 将接受的資料轉換成字元串形式
        message = new String(packet.getData());
    } catch (Exception e1) {
        System.out.println("Error: " + e1); // 捕捉異常情況
        message = "Error: " + e1;
    }
    return message;
}
           

參考文章:

https://www.2cto.com/kf/201610/555014.html

http://blog.sina.com.cn/s/blog_5017ea6c0101bv8l.html

https://blog.csdn.net/luanpeng825485697/article/details/78176319