diff --git a/examples/src/UdpExamples/MulticastUdpChatKeyboardSender.java b/examples/src/UdpExamples/MulticastUdpChatKeyboardSender.java
new file mode 100644
index 0000000000000000000000000000000000000000..79ad6716309c91b67c5e3dd64c79d48309b51cd9
--- /dev/null
+++ b/examples/src/UdpExamples/MulticastUdpChatKeyboardSender.java
@@ -0,0 +1,155 @@
+package UdpExamples;
+
+import static UdpExamples.MulticastUdpReceiver.BUFFER_LENGTH;
+import edu.nps.moves.dis7.utilities.DisThreadedNetworkInterface;
+import java.io.*;
+import java.net.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Get keyboard input to send string packets on a UDP multicast group.
+ * The source looks a lot like MulticastUdpSender.  
+ * TODO: Start this after launching MulticastUdpReceiver.
+ * Multicast must be enabled for the (wired or wireless) LAN that the system is connected too.
+ * Note that these paired programs can communicate within a remote LAN (such as a wireless 
+ * home network) even if one or both machines are connected via a 
+ * <a href="https://en.wikipedia.org/wiki/Virtual_private_network" target="_blank">Virtual Private Network (VPN)</a>.
+ * 
+ * Privacy note: this sender advertises your user name as part of the multicast packet message
+ * 
+ * @author mcgredo
+ * @author brutzman@nps.edu
+ */
+public class MulticastUdpChatKeyboardSender
+{
+    /** Default constructor */
+    public MulticastUdpChatKeyboardSender()
+    {
+        // default constructor
+    }
+    // reserved range for all IPv4 multicast: 224.0.0.0 through 239.255.255.255
+    // https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml
+
+    /** Default multicast group address for send and receive connections.
+     * @see <a href="https://en.wikipedia.org/wiki/Multicast_address" target="_blank">https://en.wikipedia.org/wiki/Multicast_address</a> */
+    public static final String MULTICAST_ADDRESS = "239.1.2.15"; // within reserved multicast address range
+    /** Default socket port used, matches Wireshark DIS capture default
+     * @see <a href="https://en.wikipedia.org/wiki/Port_(computer_networking)" target="_blank">https://en.wikipedia.org/wiki/Port_(computer_networking)</a> */
+    public static final int     DESTINATION_PORT = 1718;
+    
+    /** Time to live: how many router-decrement levels can be crossed */
+    public static final int TTL = 10;
+	
+    /** How many packets to send prior to termination */
+    public static final int MAXLOOPSIZE = 100; // or maybe 20000
+	
+    /** How many packets to send prior to termination */
+    public static final String LINE_TERMINATOR = "###;"; // do not begin with ;
+    
+    /** Receiving this message causes termination
+     * @see <a href="https://en.wikipedia.org/wiki/Sentinel_value" target="_blank">https://en.wikipedia.org/wiki/Sentinel_value</a> */
+    public static final String QUIT_SENTINEL = "QUIT QUIT QUIT!";
+    private static NetworkInterface ni;
+    
+    /**
+     * Program invocation, execution starts here.
+     * TODO:  allow setting address/port group and provide help message.
+     * @param args command-line arguments
+     * @throws java.io.IOException execution error
+     */
+    @SuppressWarnings("SleepWhileInLoop")
+    public static void main(String[] args) throws IOException 
+    {
+        MulticastSocket multicastSocket = null;
+        InetSocketAddress group = null;
+        
+        // Put together a message with binary content. "ByteArrayOutputStream"
+        // is a java.io utility that lets us put together an array of binary
+        // data, which we put into the UDP packet.
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        DataOutputStream       dos = new DataOutputStream(baos);
+        
+        try
+        {
+            System.out.println("UdpExamples.MulticastUdpChatKeyboardSender started...");
+            // https://stackoverflow.com/questions/18747134/getting-cant-assign-requested-address-java-net-socketexception-using-ehcache
+            System.setProperty("java.net.preferIPv4Stack", "true");
+            
+            // multicast group we are sending to--not a single host
+            multicastSocket = new MulticastSocket(/*DESTINATION_PORT*/);
+            multicastSocket.setTimeToLive(TTL); // time to live reduces scope of transmission
+            InetAddress multicastAddress = InetAddress.getByName(MULTICAST_ADDRESS);
+            System.out.println("Multicast address/port: " + multicastAddress.getHostAddress() + "/" + DESTINATION_PORT);
+            
+            group = new InetSocketAddress(multicastAddress, DESTINATION_PORT);
+            // Join group useful on receiving side
+            multicastSocket.joinGroup(group, ni = DisThreadedNetworkInterface.findIpv4Interface());
+            // You can join multiple groups here
+            
+            byte[] buffer = baos.toByteArray();
+            DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group/*, DESTINATION_PORT*/);
+            
+            String   userName = System.getProperty("user.name");
+            String newMessage = new String(); // from user via keyboard
+            
+            for (int messageIndex = 1; messageIndex < MAXLOOPSIZE; messageIndex++)
+            {
+                String spacer = new String(); // align console outputs
+                if (messageIndex < 10)
+                    spacer = " ";
+                
+                // Put together an updated packet to send
+                if (messageIndex < MAXLOOPSIZE)
+                {
+                    System.out.print ("type new message here:  ");
+                    newMessage = System.console().readLine(); // this line blocks, awaiting keyboard input from user
+//                  System.out.println ("[trace] console return: " + newMessage);
+
+                    // avoid overrunning receive buffer
+                    int maxStringLength = BUFFER_LENGTH - LINE_TERMINATOR.length() - userName.length() - 5;
+                    if (newMessage.length() > maxStringLength)
+                        newMessage = newMessage.substring(0, maxStringLength);
+                      
+                    // Put together an updated packet to send
+                    dos.writeChars("[" + userName + " " + spacer + messageIndex + "] ");
+                    dos.writeChars(newMessage + LINE_TERMINATOR); // string chars for readability, include ; semicolon as termination sentinel
+                }
+                else dos.writeChars(QUIT_SENTINEL + LINE_TERMINATOR); // note string must include ; semicolon as termination sentinel
+                
+                buffer = baos.toByteArray();
+                packet.setData(buffer);
+                multicastSocket.send(packet);
+                System.out.print  ("Sent packet " + spacer + (messageIndex) + " of " + MAXLOOPSIZE + ": ");
+                System.out.println("\"" + newMessage + "\"");
+                baos.reset();
+
+                // How fast does this go? Does UDP try to slow it down, or does
+                // this cause network problems? (hint: yes for an unlimited send
+                // rate, unlike TCP). How do you know on the receiving side
+                // that you haven't received a duplicate UDP packet, out of
+                // order packet, or dropped packet?  Necessary considerations.
+                Thread.sleep(1000); // only send one per second
+            }
+            System.out.println("MulticastSender complete.");
+        }
+        catch(IOException | InterruptedException e)
+        {
+            System.err.println("Problem with MulticastSender, see exception trace:");
+            System.err.println(e);
+        } finally {
+            
+            if (multicastSocket != null && !multicastSocket.isClosed()) {
+                try {
+                    multicastSocket.leaveGroup(group, ni);
+                } catch (IOException ex) {
+                    Logger.getLogger(MulticastUdpChatKeyboardSender.class.getName()).log(Level.SEVERE, null, ex);
+                }
+                multicastSocket.close();
+            }
+            
+            dos.close();
+        }
+    }
+    
+}