Java 基于UDP 实现单播、组播、广播 Socket 编程

UDP信息传递的方式

单播(unicast):

是指封包在计算机网络的传输中,目的地址为单一目标的一种传输方式。它是现今网络应用最为广泛,通常所使用的网络协议或服务大多采用单播传输,例如一切基于TCP的协议。

组播(multicast):

也叫多播, 多点广播或群播。 指把信息同时传递给一组目的地址。它使用策略是最高效的,因为消息在每条网络链路上只需传递一次,而且只有在链路分叉的时候,消息才会被复制。

多播组通过 D 类 IP 地址和标准 UDP 端口号指定。D 类 IP 地址在 224.0.0.0 和 239.255.255.255 的范围内(包括两者)。地址 224.0.0.0 被保留,不应使用。

广播(broadcast):

是指封包在计算机网络中传输时,目的地址为网络中所有设备的一种传输方式。实际上,这里所说的“所有设备”也是限定在一个范围之中,称为“广播域”。

 

详细介绍(来自维基百科)

单播:

每次只有两个实体相互通信,发送端和接收端都是唯一确定的。

在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控制域中的一个特殊值来表示广播。

 

你在公司大喊一声“放假了”, 全部同事都会响应,大叫爽死了。

 

 

Java 基于 UDP 的 Socket编程

单播:

ChatDemo.java
package udp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
*
*
* 编写一个聊天的工具
* 有收数据部分,和发数据部分
* 这两个不部分同时执行
* 就需要多线程技术
* 一个线程控制收,一个线程控制发
*
* 因为收和发动作是不一致的,所以要定义两个方法
* 而且这两个方法要封装到不同的类中
*
* @author 言曌
* @date 2017/12/6 下午8:25
*/
class Send implements Runnable {
private DatagramSocket socket;
public Send(DatagramSocket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while((line = bufferedReader.readLine()) != null) {
byte[] buff = line.getBytes();
DatagramPacket packet = new DatagramPacket(buff,buff.length, InetAddress.getByName("192.168.168.106"),10001);
socket.send(packet);
if("bye".equals(line)) {
break;
}
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Rece implements Runnable {
private  DatagramSocket socket;
public Rece(DatagramSocket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
while(true) {
byte[] buff = new byte[1024];
DatagramPacket packet = new DatagramPacket(buff,buff.length);
socket.receive(packet);
String ip = packet.getAddress().getHostAddress();
String data = new String(packet.getData(),0,packet.getLength());
if("bye".equals(data)) {
System.out.println(ip+"已经离开了");
continue;
}
System.out.println(ip+"说"+data);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class ChatDemo {
/**
*  可以持续发送到 192.168.168.106:10001
*  发送到接受者的10001端口,接受者需要在10001端口接受
*/
public static void main(String args[]) throws SocketException {
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket reveSocket = new DatagramSocket(10001);
new Thread(new Send(sendSocket)).start();
new Thread(new Rece(reveSocket)).start();
}
}
说明

我这里有两台电脑

一台 ip:192.168.168.107  Mac

一台电脑:192.168.168.106  Windows

上面的代码是 Mac 的代码,即 192.168.168.107 不断给 192.168.168.106 的端口10001发送数据

192.168.168.106 在 10001接收

 

效果图

Mac 端的 IDEA 控制台发送数据并回车

Windows 端的 IDEA 控制台接收数据

组播

ChatDemo2.java
package udp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
/**
*
*
* 编写一个聊天的工具
* 有收数据部分,和发数据部分
* 这两个不部分同时执行
* 就需要多线程技术
* 一个线程控制收,一个线程控制发
*
* 因为收和发动作是不一致的,所以要定义两个方法
* 而且这两个方法要封装到不同的类中
*
* @author 言曌
* @date 2017/12/6 下午8:25
*/
class Send2 implements Runnable {
private MulticastSocket socket;
public Send2(MulticastSocket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
InetAddress group = InetAddress.getByName("228.5.6.7");
socket.joinGroup(group);
String line = null;
while((line = reader.readLine())!= null) {
byte[] buff = line.getBytes();
DatagramPacket packet = new DatagramPacket(buff,buff.length,group,6789);
socket.send(packet);
if("bey".equals(line)) {
break;
}
}
socket.close();
} catch (IOException e) {
throw new RuntimeException("发送端失败");
}
}
}
class Rece2 implements Runnable {
private  MulticastSocket socket;
public Rece2(MulticastSocket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
byte[] buff = new byte[1024];
InetAddress group = InetAddress.getByName("228.5.6.7");
socket.joinGroup(group);
while(true) {
DatagramPacket packet = new DatagramPacket(buff,buff.length);
socket.receive(packet);
String data = new String(packet.getData(),0,packet.getLength());
String ip = packet.getAddress().getHostAddress();
if("bye".equals(data)) {
System.out.println(ip+"离开了");
continue;
}
System.out.println("ip:"+ip+" 说:"+data);
}
} catch (IOException e) {
throw new RuntimeException("接受端失败");
}
}
}
public class ChatDemo2 {
public static void main(String args[]) throws IOException {
MulticastSocket sendSocket = new MulticastSocket();
MulticastSocket receSocket = new MulticastSocket(6789);
new Thread(new Send2(sendSocket)).start();
new Thread(new Rece2(receSocket)).start();
}
}

说明

还是两天机器,都运行上面的代码。都加入一个 ip 为 228.5.6.7 的组,在 6789 端口接受发数据。

效果图

Mac 端的 IDEA 控制台

 

Windows 端的 IDEA 控制台

 

广播

ChatDemo3.java
package udp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
*
*
* 编写一个聊天的工具
* 有收数据部分,和发数据部分
* 这两个不部分同时执行
* 就需要多线程技术
* 一个线程控制收,一个线程控制发
*
* 因为收和发动作是不一致的,所以要定义两个方法
* 而且这两个方法要封装到不同的类中
*
* @author 言曌
* @date 2017/12/6 下午8:25
*/
class Send3 implements Runnable {
private DatagramSocket socket;
public Send3(DatagramSocket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while((line = bufferedReader.readLine()) != null) {
byte[] buff = line.getBytes();
DatagramPacket packet = new DatagramPacket(buff,buff.length, InetAddress.getByName("192.168.168.255"),10001);
socket.send(packet);
if("bye".equals(line)) {
break;
}
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Rece3 implements Runnable {
private  DatagramSocket socket;
public Rece3(DatagramSocket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
while(true) {
byte[] buff = new byte[1024];
DatagramPacket packet = new DatagramPacket(buff,buff.length);
socket.receive(packet);
String ip = packet.getAddress().getHostAddress();
String data = new String(packet.getData(),0,packet.getLength());
if("bye".equals(data)) {
System.out.println(ip+"离开了");
}
System.out.println(ip+"说"+data);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class ChatDemo3 {
public static void main(String args[]) throws SocketException {
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket reveSocket = new DatagramSocket(10001);
new Thread(new Send3(sendSocket)).start();
new Thread(new Rece3(reveSocket)).start();
}
}
 

说明

广播比较好理解,我这里的局域网网络地址是 192.168.168.0

所以 ip 为 192.168.168.1 - 192.168.168.254 是一个本局域网内的有效 ip 地址

192.168.168.255 被称为广播地址,只要给广播地址发消息,该局域网内的所有人都能收到。

 

运行结果

Mac 版 IDEA 的控制台

 

Windows 版 IDEA 的控制台

 

来源:https://liuyanzhao.com/6965.html