Java - 仅在本地主机上工作的 UDP 多播
Java - UDP Multicast Only Working on Localhost
我最近在我的多人游戏中实施多播以在玩家网络上定位游戏 运行。我创建了两个 类、Heart
和 Listener
。我遇到的问题是听众只能通过 localhost
听到心跳声,如果我是 运行 另一台计算机上的一部分则听不到。
心:
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();
}
}
}
听众:
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) {}
}
}
我怎样才能让听众即使在另一台电脑上也能听到节拍?
编辑:
查找本地 IPv4 地址。
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;
}
您需要确保您的侦听器已在正确的接口上加入多播组,并且您的发件人正在正确的接口上发送。
在这两种情况下,您都可以通过 setInterface
或 setNetworkInterface
方法执行此操作。
假设您的发件人 IP 地址为 192.168.1.1 和 192.168.2.1,您的收件人 IP 地址为 192.168.1.2 和 192.168.2.2。如果您希望发件人从 192.168.1.1 发送,您可以调用:
multicastSocket.setInterface(InetAddress.getByName("192.168.1.1"));
然后您的接收器需要在 192.168.1.2 上接收:
multicastSocket.setInterface(InetAddress.getByName("192.168.1.2"));
multicastSocket.joinGroup(InetAddress.getByName(groupName));
如果你想让你的接收器在多个接口上接收,多次调用joinGroup
,首先调用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));
编辑:
如果您不知道本地 IP 地址,可以使用 InetAddress.getLocalHost()
获取与计算机主机名关联的 IP。如果您的系统上有多个 IP,您可以调用 NetworkInterface.getNetworkInterfaces()
来获取网络接口列表,然后在每个接口上调用 getInetAddresses()
来获取 IP 地址:
for (NetworkInterface intf: NetworkInterface.getNetworkInterfaces()) {
for (InetAddress addr: intf.getInetAddresses()) {
System.out.println("interface " + intf + ": address " + addr);
}
}
编辑 2:
在多个接口上发送:
在 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);
}
}
在多个接口上接收:
在 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));
}
}
我最近在我的多人游戏中实施多播以在玩家网络上定位游戏 运行。我创建了两个 类、Heart
和 Listener
。我遇到的问题是听众只能通过 localhost
听到心跳声,如果我是 运行 另一台计算机上的一部分则听不到。
心:
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();
}
}
}
听众:
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) {}
}
}
我怎样才能让听众即使在另一台电脑上也能听到节拍?
编辑: 查找本地 IPv4 地址。
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;
}
您需要确保您的侦听器已在正确的接口上加入多播组,并且您的发件人正在正确的接口上发送。
在这两种情况下,您都可以通过 setInterface
或 setNetworkInterface
方法执行此操作。
假设您的发件人 IP 地址为 192.168.1.1 和 192.168.2.1,您的收件人 IP 地址为 192.168.1.2 和 192.168.2.2。如果您希望发件人从 192.168.1.1 发送,您可以调用:
multicastSocket.setInterface(InetAddress.getByName("192.168.1.1"));
然后您的接收器需要在 192.168.1.2 上接收:
multicastSocket.setInterface(InetAddress.getByName("192.168.1.2"));
multicastSocket.joinGroup(InetAddress.getByName(groupName));
如果你想让你的接收器在多个接口上接收,多次调用joinGroup
,首先调用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));
编辑:
如果您不知道本地 IP 地址,可以使用 InetAddress.getLocalHost()
获取与计算机主机名关联的 IP。如果您的系统上有多个 IP,您可以调用 NetworkInterface.getNetworkInterfaces()
来获取网络接口列表,然后在每个接口上调用 getInetAddresses()
来获取 IP 地址:
for (NetworkInterface intf: NetworkInterface.getNetworkInterfaces()) {
for (InetAddress addr: intf.getInetAddresses()) {
System.out.println("interface " + intf + ": address " + addr);
}
}
编辑 2:
在多个接口上发送:
在 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);
}
}
在多个接口上接收:
在 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));
}
}