Java 网络游戏:如何列出可用服务器?

6
我正在开发一个使用本地局域网的游戏。和大多数多人游戏一样,这里有一个服务器-客户端系统。电脑A运行程序实例,创建一个服务器并等待;电脑B也做同样的事情。现在电脑C运行程序,我希望他能看到电脑A和B作为游戏服务器列在那里。我该怎么做呢?
为了列出所有可用的服务器,一个简单的解决方案可能是这样的:我需要检查特定范围内的所有IP地址,并查看它们是否通过我的特定端口响应。如果是,则表示游戏实例正在其上运行,并应列在服务器列表中。
上述解决方案好吗? 我已经搜索并得到了以下代码:
public void checkHosts(String subnet){
    int timeout=1000;
    for (int i=1;i<254;i++){
        String host=subnet + "." + i;
            if (InetAddress.getByName(host).isReachable(timeout)){
                System.out.println(host + " is reachable");
            }
    }
}

但是这需要很长时间,而且没有用处。如果这不是正确的解决方案,有哪些其他方法?

4个回答

3

如果您正在本地网络上运行,您的方法可能需要大量时间,而且绝对不是最佳解决方案。

您可以通过让服务器定期在网络中广播其地址,并使所有客户端都侦听它来解决此问题。一个很好的例子可以在Java教程中找到。


2

使用以下方式之一发送发现消息:

  1. 多播(使用java.net.Multicast socket)
  2. 广播(使用java.net.DatagramSocket)到网络广播地址

让所有服务器都监听该消息并回复“我在这里”,可能还会提供更多信息以进行进一步的连接设置(服务器名称、版本、使用端口x、udp或tcp等)。


1

最好的方法是使用ZeroConf(也称为Bonjour)。

这是苹果在iTunes和iOS设备中用于所有网络发现的方法,以便它们可以找到彼此。

我已经成功地在Linux、Windows和OSX上实现了它,并应用在服务端应用程序中。

而且,在所有主要相关语言中都有很好的支持。

没有必要重新发明轮子。


0

您可以使用UDP完成此操作;如果服务器处于运行状态并让所有节点侦听UDP数据包,则发送广播。

根据要求,以下是有关utp的示例代码;这些是两个类,一个是心脏(它会跳动),另一个是监听器。

public class Heart extends Observable implements Runnable {

private String groupName = "229.5.38.17";
private int port = 4567;
MulticastSocket multicastSocket;
DatagramPacket datagramPacket;

public Heart(int connectionListenerPort, Observer...observers) {
    for(Observer observer : observers) {
        this.addObserver(observer);
    }
    try {
        multicastSocket = new MulticastSocket();
        InetAddress group = InetAddress.getByName(groupName);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(new Beat(connectionListenerPort));
        objectOutputStream.flush();
        objectOutputStream.close();
        byte[] buf = byteArrayOutputStream.toByteArray();
        datagramPacket = new DatagramPacket(buf, buf.length, group, port);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

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

private void beat() {
    try {
        multicastSocket.send(datagramPacket);
        message(new Message(TYPE.INFO, KEY.MESSAGE, "Heart beat sent."));
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private void message(Message message) {
    setChanged();
    notifyObservers(message);
}

}

public class BeatListener extends Observable implements Runnable {

private boolean run = true;
private String groupName = "229.5.38.17";
MulticastSocket multicastSocket;
private Network network;

public BeatListener(Network network, Observer... observers) {
    for(Observer observer : observers) {
        addObserver(observer);
    }
    try {
        multicastSocket = new MulticastSocket(4567);
        multicastSocket.joinGroup(InetAddress.getByName(groupName));
    } catch (IOException e) {
        error(e);
        e.printStackTrace();
    }
    this.network = network;
}

@Override
public void run() {
    while(run) {
        DatagramPacket datagramPacket = new DatagramPacket(new byte[1500], 1500);
        try {
            multicastSocket.receive(datagramPacket);
            if(!isLocalhost(datagramPacket.getAddress().getHostAddress())) {
                Beat beat = getBeat(datagramPacket);
                if(beat != null) {
                    network.setPeer(new Peer(datagramPacket.getAddress(), beat.getConnectionListenerPort()));
                    message(new Message(TYPE.NETWORK, KEY.NETWORK, network));
                }
            }
        } catch (IOException e) {
            error(e);
            e.printStackTrace();
        }
    }
}

private void message(Message message) {
    setChanged();
    notifyObservers(message);
}

private void error(Exception e) {
    message(new Message(TYPE.ERROR, KEY.MESSAGE, e.getClass().getSimpleName()));
}

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) {
        error(e);
        e.printStackTrace();
    }
    return isLocalhost;
}

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

}

我添加了两个类作为示例代码;我认为你不能仅仅将其作为示例运行,因为你可能会缺少其他必要的类,但它展示了UDP广播。 - Tom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接