mehrmoudi
mehrmoudi

Reputation: 1106

Java network game: How to list available servers?

I'm working on a game that uses local area network. Like most of multiplayer games, there is a server-client system. Computer A runs an instance of program, creates a server and wait; Computer B do the same thing. Now Computer C runs the program, what I want is that he can see computer A and B listed there as game servers. How can I do this?
In order to list all of the servers available, a simple solution might be this: I need to check all of the IP addresses in a particular range and see if they respond via my specific port or not. If yes, therefor an instance of game is running on it and should be listed in the servers list.
Is the solution described above a good one? I've searched and get this piece of code:

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");
            }
    }
}

but is takes so much time and is useless. If it's not the right solution, what are some other ways?

Upvotes: 6

Views: 4204

Answers (4)

user177800
user177800

Reputation:

The best way to do this is with something like ZeroConf ( also known as Bonjour ). This is what Apple uses for all its network discovery in iTunes and iOS devices so that they can find each other.

I have implemented it Linux, Windows and OSX in server side applications with great success.

And there is great support in all the major relevant languages as well.

There is no need to re-invent this wheel.

Upvotes: 1

Tom
Tom

Reputation: 4180

you could use udp for this; send out a broadcast if a server is up and let al nodes listen for udp packets.

As requested, here is some example code on utp; theses are 2 classes, one is the heart (wich beats) and the other is the listener.

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;
}

}

Upvotes: 0

Mattias Isegran Bergander
Mattias Isegran Bergander

Reputation: 11909

Send a discover message using either:

  1. a multicast (use java.netMulticast socket)
  2. broadcast (use java.net.DatagramSocket) to the networks broadcast address

Have all servers listen for that and reply saying "I'm here" and possibly more information for further connection setup (server name, version, use port x, udp or tcp etc)

Upvotes: 2

Marcelo
Marcelo

Reputation: 4608

If you are running on a local network, your method might take a huge amount of time and is definitely not the best solution.

You can solve it by having your servers periodically broadcast their addresses in the network, and have all the clients listen for it. A good example can be found in the Java Tutorials.

Upvotes: 3

Related Questions