Сетевая игра Java: как составить список доступных серверов?

Я работаю над игрой, которая использует локальную сеть. Как и в большинстве многопользовательских игр, здесь есть серверно-клиентская система. Компьютер А запускает экземпляр программы, создает сервер и ждет; Компьютер 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");
            }
    }
}

но это занимает так много времени и бесполезно. Если это не правильное решение, какие есть другие способы?


person mehrmoudi    schedule 23.03.2012    source источник


Ответы (4)


Отправьте сообщение об обнаружении, используя:

  1. многоадресная рассылка (используйте сокет java.netMulticast)
  2. широковещательная рассылка (используйте java.net.DatagramSocket) на широковещательный адрес сети

Пусть все серверы прослушают это и ответят: «Я здесь» и, возможно, дополнительную информацию для дальнейшей настройки соединения (имя сервера, версия, использование порта x, udp или tcp и т. д.).

person Mattias Isegran Bergander    schedule 23.03.2012

Если вы работаете в локальной сети, ваш метод может занять много времени и определенно не является лучшим решением.

Вы можете решить эту проблему, если ваши серверы будут периодически транслировать свои адреса в сеть, а все клиенты будут их прослушивать. Хороший пример можно найти в учебниках по Java.

person Marcelo    schedule 23.03.2012

Лучше всего это сделать с помощью чего-то вроде ZeroConf (также известного как Bonjour). Это то, что Apple использует для всех своих сетевых обнаружений в устройствах iTunes и iOS, чтобы они могли найти друг друга.

Я с большим успехом реализовал его для Linux, Windows и OSX в серверных приложениях.

И есть отличная поддержка на всех основных соответствующих языках.

Нет необходимости заново изобретать это колесо.

person Community    schedule 23.03.2012

вы можете использовать для этого udp; отправить широковещательную рассылку, если сервер работает, и позволить всем узлам прослушивать пакеты udp.

По запросу вот пример кода на utp; тезисы 2 класса, один - сердце (которое бьется), а другой - слушатель.

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

}
person Tom    schedule 23.03.2012
comment
Я добавил 2 класса в качестве примера кода; Я не думаю, что вы можете просто запустить его в качестве примера, потому что вы, вероятно, пропустите некоторые другие необходимые классы, но он показывает вещание udp. - person Tom; 23.03.2012