工具类
- 创建广播消息和回复消息的工具类;
public class MessageCreator {
private static final String SN_HEADER = "收到暗号,我是(SN):";
private static final String PORT_HEADER = "这是暗号,请回电端口(Port):";
public static String buildWithPort(int port) {
return PORT_HEADER + port;
}
public static int parsePort(String data) {
if (data.startsWith(PORT_HEADER)) {
return Integer.parseInt(data.substring(PORT_HEADER.length()));
}
return -1;
}
public static String buildWithSn(String sn) {
return SN_HEADER + sn;
}
public static String parseSn(String data) {
if (data.startsWith(SN_HEADER)) {
return data.substring(SN_HEADER.length());
}
return null;
}
}
广播接收者
- 在局域网中的多台设备上,运行该代码,则都可以收到来自发送者的广播;
- 监听在 20000 端口;
- 从接收到的消息中得到发送者的地址,端口号,并据此发送回复消息给发送方;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.UUID;
/**
* UDP 提供者,用于提供服务
*/
public class UDPProvider {
public static void main(String[] args) throws IOException {
// 生成唯一标识
String sn = UUID.randomUUID().toString();
Provider provider = new Provider(sn);
provider.start();
// 读取任意键盘信息后可以退出
//noinspection ResultOfMethodCallIgnored
System.in.read();
provider.exit();
}
private static class Provider extends Thread {
private final String sn;
private boolean done = false;
private DatagramSocket ds = null;
public Provider(String sn) {
super();
this.sn = sn;
}
@Override
public void run() {
super.run();
System.out.println("UDPProvider Started.");
try {
// 监听20000 端口
ds = new DatagramSocket(20000);
while (!done) {
// 构建接收实体
final byte[] buf = new byte[512];
DatagramPacket receivePack = new DatagramPacket(buf, buf.length);
// 接收
ds.receive(receivePack);
/**
* 打印发送者的 IP,port,data
*/
String ip = receivePack.getAddress().getHostAddress();
int port = receivePack.getPort();
int dataLen = receivePack.getLength();
String data = new String(receivePack.getData(), 0, dataLen);
System.out.println("UDPProvider receive form ip:" + ip
+ "\tport:" + port + "\tdata:" + data);
// 解析发送者用于等待回复的端口号
int responsePort = MessageCreator.parsePort(data);
if (responsePort != -1) {
// 构建一份回送数据
String responseData = MessageCreator.buildWithSn(sn);
byte[] responseDataBytes = responseData.getBytes();
// 直接根据发送者构建一份回送信息
DatagramPacket responsePacket = new DatagramPacket(
responseDataBytes,
responseDataBytes.length,
receivePack.getAddress(),
responsePort);
ds.send(responsePacket);
}
}
} catch (Exception ignored) {
} finally {
close();
}
System.out.println("UDPProvider Finished.");
}
private void close() {
if (ds != null) {
ds.close();
ds = null;
}
}
void exit() {
done = true;
close();
}
}
}
广播发送者
- 先起一个监听线程,用来监听广播接受者回复的消息;
- 监听线程运行起来后,在发送广播信息,广播地址为255.255.255.255;
- 监听线程收到回复消息后,构建成一个设备对象,存于设备集合中;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* UDP 搜索者,用于搜索服务支持方
*/
public class UDPSearcher {
private static final int LISTEN_PORT = 30000;
public static void main(String[] args) throws IOException, InterruptedException {
System.out.println("UDPSearcher Started.");
// 启动监听线程
Listener listener = listen();
// 发送广播
sendBroadcast();
/**
* 不想监听了,退出,并打印监听收到的广播回复消息;
*/
System.in.read();
List<Device> devices = listener.getDevicesAndClose();
for (Device device : devices) {
System.out.println("Device:" + device.toString());
}
System.out.println("UDPSearcher Finished.");
}
/**
* 创建监听线程
* 创建一个监听广播回复的线程,该线程启动后,再广播,确定可以收到广播回复的消息;
* 该线程中存有广播回复回来的消息;
* @return
* @throws InterruptedException
*/
private static Listener listen() throws InterruptedException {
System.out.println("UDPSearcher start listen.");
CountDownLatch countDownLatch = new CountDownLatch(1);
Listener listener = new Listener(LISTEN_PORT, countDownLatch);
listener.start();
countDownLatch.await();
return listener;
}
/**
* 发送广播消息
* @throws IOException
*/
private static void sendBroadcast() throws IOException {
System.out.println("UDPSearcher sendBroadcast started.");
// 作为搜索方,让系统自动分配端口
DatagramSocket ds = new DatagramSocket();
/**
* 1.构建请求数据
*/
// 构建发送消息
String requestData = MessageCreator.buildWithPort(LISTEN_PORT);
byte[] requestDataBytes = requestData.getBytes();
// 构建发送包
DatagramPacket requestPacket = new DatagramPacket(
requestDataBytes,
requestDataBytes.length);
// 设置广播地址
requestPacket.setAddress(InetAddress.getByName("255.255.255.255"));
// 设置接收方的监听端口
requestPacket.setPort(20000);
/**
* 2.发送广播
* 广播完成后,用于发送广播的 DatagramSocket 就关闭;
*/
ds.send(requestPacket);
ds.close();
System.out.println("UDPSearcher sendBroadcast finished.");
}
/**
* 设备信息类
*/
private static class Device {
final int port;
final String ip;
final String sn;
private Device(int port, String ip, String sn) {
this.port = port;
this.ip = ip;
this.sn = sn;
}
@Override
public String toString() {
return "Device{" +
"port=" + port +
", ip='" + ip + '\'' +
", sn='" + sn + '\'' +
'}';
}
}
/**
* 监听广播回复线程,监听在 30000 端口
*/
private static class Listener extends Thread {
private final int listenPort;
private final CountDownLatch countDownLatch;
private final List<Device> devices = new ArrayList<>();
private boolean done = false;
private DatagramSocket ds = null;
public Listener(int listenPort, CountDownLatch countDownLatch) {
super();
this.listenPort = listenPort;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
super.run();
// 通知已启动
countDownLatch.countDown();
try {
// 监听回送端口
ds = new DatagramSocket(listenPort);
// 收到一条广播回复消息就打印出来,并封装成一个设备,装入设备集合;
while (!done) {
// 构建接收包
final byte[] buf = new byte[512];
DatagramPacket receivePack = new DatagramPacket(buf, buf.length);
// 接收
ds.receive(receivePack);
// 打印接收到的信息与发送者的信息
String ip = receivePack.getAddress().getHostAddress();
int port = receivePack.getPort();
int dataLen = receivePack.getLength();
String data = new String(receivePack.getData(), 0, dataLen);
System.out.println("UDPSearcher receive form ip:" + ip
+ "\tport:" + port + "\tdata:" + data);
String sn = MessageCreator.parseSn(data);
if (sn != null) {
Device device = new Device(port, ip, sn);
devices.add(device);
}
}
} catch (Exception ignored) {
} finally {
close();
}
System.out.println("UDPSearcher listener finished.");
}
private void close() {
if (ds != null) {
ds.close();
ds = null;
}
}
List<Device> getDevicesAndClose() {
done = true;
close();
return devices;
}
}
}
运行结果
接收者
UDPProvider Started.
UDPProvider receive form ip:192.168.31.100 port:53637 data:这是暗号,请回电端口(Port):30000
q
UDPProvider Finished.
发送者
UDPSearcher Started.
UDPSearcher start listen.
UDPSearcher sendBroadcast started.
UDPSearcher sendBroadcast finished.
UDPSearcher receive form ip:192.168.31.100 port:20000 data:收到暗号,我是(SN):be2d531b-d787-4293-bd69-b144c1e23f5f
UDPSearcher receive form ip:192.168.31.196 port:20000 data:收到暗号,我是(SN):e7644093-dbc6-4cf9-90fc-0b62d95acfd9
a
UDPSearcher listener finished.
Device:Device{port=20000, ip='192.168.31.100', sn='be2d531b-d787-4293-bd69-b144c1e23f5f'}
Device:Device{port=20000, ip='192.168.31.196', sn='e7644093-dbc6-4cf9-90fc-0b62d95acfd9'}
UDPSearcher Finished.
本文详细介绍了一种基于UDP协议的局域网广播通信机制,包括广播消息的发送与接收,以及如何通过广播进行设备发现与响应。文章展示了如何创建广播消息和回复消息的工具类,以及广播接收者和发送者的具体实现。
1329

被折叠的 条评论
为什么被折叠?



