简体   繁体   中英

Java - UDP Multicast Only Working on Localhost

I recently was implementing multicast into my multiplayer game to locate games running on the player's network. I created two classes, Heart and Listener . The problem I am having is that the listener only hears the heart beat through localhost , not if I'm running one part on another computer.

Heart:

package net.jibini.networking.udp;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import net.jibini.networking.packets.Packet;

public class Heart implements Runnable 
{
    private String groupName = "229.5.38.17";
    private int port = 4567;
    MulticastSocket multicastSocket;
    DatagramPacket datagramPacket;
    public boolean beating = true;
    public Packet toSend;

    public Heart(int connectionListenerPort, Packet toSend) 
    {
        try 
        {
            this.toSend = toSend;
            multicastSocket = new MulticastSocket();
            multicastSocket.setReuseAddress(true);
            InetAddress group = InetAddress.getByName(groupName);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(toSend);
            objectOutputStream.flush();
            objectOutputStream.close();
            byte[] buf = byteArrayOutputStream.toByteArray();
            datagramPacket = new DatagramPacket(buf, buf.length, group, port);
            new Thread(this).start();
        } 
        catch (IOException e) 
        {
            e.printStackTrace();
        }
    }

    @Override
    public void run() 
    {
        while (beating) 
        {
            beat();
            try 
            {
                Thread.sleep(1000);
            }
            catch (InterruptedException e) 
            {
                e.printStackTrace();
            }
        }
    }

    private void beat() {
        try 
        {
            multicastSocket.send(datagramPacket);
        } 
        catch (IOException e) 
        {
            e.printStackTrace();
        }
    }
}

Listener:

package net.jibini.networking.udp;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import net.jibini.networking.packets.Packet;

public class Listener implements Runnable 
{
    private boolean run = true;
    private String groupName = "229.5.38.17";
    MulticastSocket multicastSocket;
    public OnFound onFound;

    public Listener(OnFound onFound) 
    {
        try 
        {
            multicastSocket = new MulticastSocket(4567);
            multicastSocket.setReuseAddress(true);
            multicastSocket.joinGroup(InetAddress.getByName(groupName));
            this.onFound = onFound;
            new Thread(this).start();
        } 
        catch (IOException e) 
        {
            e.printStackTrace();
        }
    }

    @Override
    public void run() 
    {
        while (run) 
        {
            DatagramPacket datagramPacket = new DatagramPacket(new byte[1500], 1500);
            try 
            {
                multicastSocket.receive(datagramPacket);
                Packet beat = getBeat(datagramPacket);
                if (beat != null) 
                {
                    onFound.onFound(datagramPacket.getAddress(), beat);
                }
            } 
            catch (IOException e) 
            {
                e.printStackTrace();
            }
        }
    }

    public void stop() 
    {
        run = false;
    }

    /*private boolean isLocalhost(String hostAddress) 
    {
        boolean isLocalhost = false;
        Enumeration<NetworkInterface> networkInterfaces;
        try 
        {
            networkInterfaces = NetworkInterface.getNetworkInterfaces();
            if (networkInterfaces != null) 
            {
                OUTER:
                while (networkInterfaces.hasMoreElements()) 
                {
                    NetworkInterface networkInterface = networkInterfaces.nextElement();
                    Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
                    if (inetAddresses != null) 
                    {
                        while (inetAddresses.hasMoreElements()) 
                        {
                            InetAddress inetAddress = inetAddresses.nextElement();
                            if (hostAddress.equals(inetAddress.getHostAddress())) 
                            {
                                isLocalhost = true;
                                break OUTER;
                            }
                        }
                    }
                }
            }
        } 
        catch (SocketException e) 
        {
            e.printStackTrace();
        }
        return isLocalhost;
    }*/

    private Packet getBeat(DatagramPacket datagramPacket) 
    {
        Packet beat = null;
        byte[] data = datagramPacket.getData();
        if (data != null) 
        {
            try 
            {
                ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(data));
                beat = (Packet) objectInputStream.readObject();
            } 
            catch (IOException e) 
            {
                e.printStackTrace();
            } 
            catch (ClassNotFoundException e) 
            {
                e.printStackTrace();
            }
        }
        return beat;
    }

    public static class OnFound 
    {
        public void onFound(InetAddress inet, Packet beat) {}
    }
}

How can I make it so that the listener hears the beat even from another computer?

EDIT: Finds local IPv4 address.

public static InetAddress localAddress()
{
    String ip;
    try {
        Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
        while (interfaces.hasMoreElements()) {
            NetworkInterface iface = interfaces.nextElement();
            if (iface.isLoopback() || !iface.isUp())
                continue;

            Enumeration<InetAddress> addresses = iface.getInetAddresses();
            while(addresses.hasMoreElements()) {
                InetAddress addr = addresses.nextElement();
                ip = addr.getHostAddress();
                if (ip.startsWith("10.") || ip.startsWith("172.31.") || ip.startsWith("192.168"))
                    return addr;
            }
        }
    } catch (SocketException e) {
        throw new RuntimeException(e);
    }
    return null;
}

You need to make sure your listener has joined the multicast group on the proper interface(s), and that your sender is sending on the proper interface.

In both cases, you can do this via the setInterface or setNetworkInterface methods.

Suppose your sender has IP addresses 192.168.1.1 and 192.168.2.1, and your receiver has addresses 192.168.1.2 and 192.168.2.2. If you want your sender to send from 192.168.1.1, you would call:

multicastSocket.setInterface(InetAddress.getByName("192.168.1.1"));

Your receiver would then need to receive on 192.168.1.2:

multicastSocket.setInterface(InetAddress.getByName("192.168.1.2"));
multicastSocket.joinGroup(InetAddress.getByName(groupName));

If you want your receiver to receive on multiple interfaces, call joinGroup multiple times, first calling setInterface :

multicastSocket.setInterface(InetAddress.getByName("192.168.1.2"));
multicastSocket.joinGroup(InetAddress.getByName(groupName));
multicastSocket.setInterface(InetAddress.getByName("192.168.2.2"));
multicastSocket.joinGroup(InetAddress.getByName(groupName));

Edit:

If you don't know your local IP address, you can use InetAddress.getLocalHost() to get the IP associated with the computer's host name. If you have more than one IP on your system, you can call NetworkInterface.getNetworkInterfaces() to get a list of network interfaces, then call getInetAddresses() on each one to get the IP addresses:

for (NetworkInterface intf: NetworkInterface.getNetworkInterfaces()) {
    for (InetAddress addr: intf.getInetAddresses()) {
        System.out.println("interface " + intf + ": address " + addr);
    }
}

Edit 2:

To send on multiple interfaces: In Heart.beat() :

    Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
    while (interfaces.hasMoreElements()) {
        NetworkInterface iface = interfaces.nextElement();
        if (iface.isLoopback() || !iface.isUp())
            continue;

        Enumeration<InetAddress> addresses = iface.getInetAddresses();
        while(addresses.hasMoreElements()) {
            InetAddress addr = addresses.nextElement();
            multicastSocket.setInterface(addr);
            multicastSocket.send(datagramPacket);
        }
    }

To receive on multiple interfaces: In the constructor for Listener :

    Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
    while (interfaces.hasMoreElements()) {
        NetworkInterface iface = interfaces.nextElement();
        if (iface.isLoopback() || !iface.isUp())
            continue;

        Enumeration<InetAddress> addresses = iface.getInetAddresses();
        while(addresses.hasMoreElements()) {
            InetAddress addr = addresses.nextElement();
            multicastSocket.setInterface(addr);
            multicastSocket.joinGroup(InetAddress.getByName(groupName));
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM