From b0ea680f133575617f6432542f34699ac0867125 Mon Sep 17 00:00:00 2001 From: Gerald Unterrainer Date: Thu, 9 Sep 2021 13:54:39 +0200 Subject: [PATCH] add broadcast and UDP-sender --- pom.xml | 2 +- .../commons/udpobserver/UdpSender.java | 117 ++++++++++++++++++ .../exceptions/UdpSendException.java | 11 ++ .../udpobserver/scripts/TestObserver.java | 5 +- .../scripts/TestObserverSender.java | 29 +++++ .../scripts/TestObserverShelly.java | 35 ++++++ start_test_observer.bat | 1 + start_test_observer.sh | 1 + 8 files changed, 196 insertions(+), 5 deletions(-) create mode 100644 src/main/java/info/unterrainer/commons/udpobserver/UdpSender.java create mode 100644 src/main/java/info/unterrainer/commons/udpobserver/exceptions/UdpSendException.java create mode 100644 src/main/java/info/unterrainer/commons/udpobserver/scripts/TestObserverSender.java create mode 100644 src/main/java/info/unterrainer/commons/udpobserver/scripts/TestObserverShelly.java create mode 100644 start_test_observer.bat create mode 100644 start_test_observer.sh diff --git a/pom.xml b/pom.xml index 5d7ea6b..a416f62 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ 4.0.0 info.unterrainer.commons.udp-observer udp-observer - 0.1.9 + 0.1.10 UdpObserver jar diff --git a/src/main/java/info/unterrainer/commons/udpobserver/UdpSender.java b/src/main/java/info/unterrainer/commons/udpobserver/UdpSender.java new file mode 100644 index 0000000..99ff0db --- /dev/null +++ b/src/main/java/info/unterrainer/commons/udpobserver/UdpSender.java @@ -0,0 +1,117 @@ +package info.unterrainer.commons.udpobserver; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Objects; + +import info.unterrainer.commons.udpobserver.exceptions.UdpSendException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RequiredArgsConstructor +public class UdpSender { + + private final int senderPort; + private DatagramSocket sendSocket; + private List broadcastAddresses; + + protected List getBroadcastAddresses() { + if (broadcastAddresses != null) + return broadcastAddresses; + + try { + broadcastAddresses = getAllBroadcastAddresses(); + } catch (SocketException e) { + return List.of(); + } + return broadcastAddresses; + } + + protected DatagramSocket getSendSocket() { + if (sendSocket == null) { + try { + sendSocket = new DatagramSocket(senderPort, InetAddress.getLocalHost()); + } catch (SocketException | UnknownHostException e) { + log.error("Error retrieving socket.", e); + throw new UdpSendException(e); + } + try { + sendSocket.setBroadcast(true); + } catch (SocketException e) { + log.error("Error setting broadcast-mode.", e); + throw new UdpSendException(e); + } + } + return sendSocket; + } + + public void broadcast(final int targetPort, final String content) { + broadcast(targetPort, content.getBytes(StandardCharsets.UTF_8)); + } + + public void broadcast(final int targetPort, final byte[] bytes) { + for (InetAddress address : getBroadcastAddresses()) + try { + send(address, targetPort, bytes); + } catch (Exception e) { + // NOOP + } + } + + public void send(final InetAddress targetAddress, final int targetPort, final String content) { + send(targetAddress.getHostAddress(), targetPort, content); + } + + public void send(final InetAddress targetAddress, final int targetPort, final byte[] bytes) { + send(targetAddress.getHostAddress(), targetPort, bytes); + } + + public void send(final String targetAddress, final int targetPort, final String content) { + send(targetAddress, targetPort, content.getBytes(StandardCharsets.UTF_8)); + } + + public void send(final String targetAddress, final int targetPort, final byte[] bytes) { + DatagramPacket packet; + try { + packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName(targetAddress), targetPort); + } catch (UnknownHostException e) { + log.error("Error creating new packet.", e); + throw new UdpSendException(e); + } + try { + getSendSocket().send(packet); + } catch (IOException e) { + log.error("Error sending packet.", e); + throw new UdpSendException(e); + } + } + + public List getAllBroadcastAddresses() throws SocketException { + List broadcastList = new ArrayList<>(); + Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); + while (interfaces.hasMoreElements()) { + NetworkInterface networkInterface = interfaces.nextElement(); + + if (networkInterface.isLoopback() || !networkInterface.isUp()) + continue; + + networkInterface.getInterfaceAddresses() + .stream() + .map(InterfaceAddress::getBroadcast) + .filter(Objects::nonNull) + .forEach(broadcastList::add); + } + return broadcastList; + } +} diff --git a/src/main/java/info/unterrainer/commons/udpobserver/exceptions/UdpSendException.java b/src/main/java/info/unterrainer/commons/udpobserver/exceptions/UdpSendException.java new file mode 100644 index 0000000..c24094c --- /dev/null +++ b/src/main/java/info/unterrainer/commons/udpobserver/exceptions/UdpSendException.java @@ -0,0 +1,11 @@ +package info.unterrainer.commons.udpobserver.exceptions; + +public class UdpSendException extends RuntimeException { + + private static final long serialVersionUID = 2243163797766428122L; + + public UdpSendException(final Throwable cause) { + super(cause); + } + +} diff --git a/src/main/java/info/unterrainer/commons/udpobserver/scripts/TestObserver.java b/src/main/java/info/unterrainer/commons/udpobserver/scripts/TestObserver.java index c3026d4..a96f809 100644 --- a/src/main/java/info/unterrainer/commons/udpobserver/scripts/TestObserver.java +++ b/src/main/java/info/unterrainer/commons/udpobserver/scripts/TestObserver.java @@ -9,13 +9,10 @@ public class TestObserver { - /** - * STA:{"type":"ENOCEAN","adr":"fef2b30d","data":"eltako_button4","vendor":"eltako","state":{"BI":"pressed","BO":"released","AO":"released","AI":"released"}} - */ private static DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME; public static void main(final String[] args) throws InterruptedException, IOException { - UdpObserver observer = new UdpObserver(1901, 256, 1000); + UdpObserver observer = new UdpObserver(8001, 256, 1000); observer.start(); System.out.println("listening for UDP packets..."); System.out.println("press to stop."); diff --git a/src/main/java/info/unterrainer/commons/udpobserver/scripts/TestObserverSender.java b/src/main/java/info/unterrainer/commons/udpobserver/scripts/TestObserverSender.java new file mode 100644 index 0000000..7432153 --- /dev/null +++ b/src/main/java/info/unterrainer/commons/udpobserver/scripts/TestObserverSender.java @@ -0,0 +1,29 @@ +package info.unterrainer.commons.udpobserver.scripts; + +import java.io.IOException; +import java.net.InetAddress; +import java.util.List; + +import info.unterrainer.commons.udpobserver.UdpSender; + +public class TestObserverSender { + + public static void main(final String[] args) throws InterruptedException, IOException { + UdpSender sender = new UdpSender(8002); + + List broadcastAddresses = sender.getAllBroadcastAddresses(); + System.out.println("found broadcast addresses:"); + for (InetAddress address : broadcastAddresses) + System.out.println(" - " + address.getHostAddress()); + + System.out.println("sending UDP packets to port 8001..."); + System.out.println("press to stop."); + while (System.in.available() == 0) { + sender.broadcast(8001, "TestPacket!"); + + System.out.println("Packet sent."); + Thread.sleep(1000L); + } + System.out.println("done."); + } +} diff --git a/src/main/java/info/unterrainer/commons/udpobserver/scripts/TestObserverShelly.java b/src/main/java/info/unterrainer/commons/udpobserver/scripts/TestObserverShelly.java new file mode 100644 index 0000000..fb5d2be --- /dev/null +++ b/src/main/java/info/unterrainer/commons/udpobserver/scripts/TestObserverShelly.java @@ -0,0 +1,35 @@ +package info.unterrainer.commons.udpobserver.scripts; + +import java.io.IOException; +import java.time.format.DateTimeFormatter; +import java.util.Collection; + +import info.unterrainer.commons.udpobserver.UdpDatagram; +import info.unterrainer.commons.udpobserver.UdpObserver; + +public class TestObserverShelly { + + /** + * STA:{"type":"ENOCEAN","adr":"fef2b30d","data":"eltako_button4","vendor":"eltako","state":{"BI":"pressed","BO":"released","AO":"released","AI":"released"}} + */ + private static DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME; + + public static void main(final String[] args) throws InterruptedException, IOException { + UdpObserver observer = new UdpObserver(1901, 256, 1000); + observer.start(); + System.out.println("listening for UDP packets..."); + System.out.println("press to stop."); + while (System.in.available() == 0) { + Thread.sleep(100L); + outputList(observer.getReceivedDatagrams()); + } + observer.close(); + System.out.println("done."); + } + + private static void outputList(final Collection list) { + for (UdpDatagram data : list) + System.out.println( + String.format("[%s] %s", data.getMetaData().getTimestamp().format(formatter), data.getContent())); + } +} diff --git a/start_test_observer.bat b/start_test_observer.bat new file mode 100644 index 0000000..1b108b1 --- /dev/null +++ b/start_test_observer.bat @@ -0,0 +1 @@ +java -cp "target/libs/*;target/classes" info.unterrainer.commons.udpobserver.scripts.TestObserver \ No newline at end of file diff --git a/start_test_observer.sh b/start_test_observer.sh new file mode 100644 index 0000000..1b108b1 --- /dev/null +++ b/start_test_observer.sh @@ -0,0 +1 @@ +java -cp "target/libs/*;target/classes" info.unterrainer.commons.udpobserver.scripts.TestObserver \ No newline at end of file