Как я могу реализовать многопоточный сервер на основе UDP в Java? - PullRequest
10 голосов
/ 21 апреля 2009

Как я могу реализовать многопоточный сервер на основе UDP в Java?

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

boolean listening = true;

System.out.println("Server started.");

while (listening)
    new ServerThread().start();

В этом случае сервер будет порождать новые потоки до тех пор, пока не закончится память. Вот код для ServerThread (я думаю, мне нужен здесь механизм, который останавливает создание ServerThread, пока клиент не попытается подключиться.

public ServerThread(String name) throws IOException 
{
    super(name);
    socket = new DatagramSocket();
}

Так что, отцы программирования на Java, пожалуйста, помогите.

Ответы [ 4 ]

14 голосов
/ 22 апреля 2009

Конструкция для этого в определенной степени зависит от того, требует ли каждый полный «диалог» UDP одного запроса и немедленного ответа, будь то один запрос или ответ с повторными передачами, или будет необходимость обрабатывать партии пакетов для каждого клиента.

Сервер RADIUS, который я написал, имел модель одного запроса + повторной передачи и порождал поток для каждого входящего пакета.

Когда каждый DatagramPacket был получен, он был передан новому потоку, а затем этот поток отвечал за отправку ответа. Это было связано с тем, что вычисления и доступ к базе данных, связанные с генерацией каждого ответа, могут занимать относительно много времени, и порождать поток легче, чем иметь какой-либо другой механизм для обработки новых пакетов, которые поступают, пока старые пакеты еще обрабатываются.

public class Server implements Runnable {
    public void run() {
        while (true) {
            DatagramPacket packet = socket.receive();
            new Thread(new Responder(socket, packet)).start();
        }
    }
}

public class Responder implements Runnable {

    Socket socket = null;
    DatagramPacket packet = null;

    public Responder(Socket socket, DatagramPacket packet) {
        this.socket = socket;
        this.packet = packet;
    }

    public void run() {
        byte[] data = makeResponse(); // code not shown
        DatagramPacket response = new DatagramPacket(data, data.length,
            packet.getAddress(), packet.getPort());
        socket.send(response);
    }
}
6 голосов
/ 21 апреля 2009

Поскольку UDP - это протокол без установления соединения, зачем вам создавать новый поток для каждого соединения? Когда вы получаете UDP-пакет, возможно, вам следует создать новый поток, чтобы разобраться с полученным сообщением.

Соединения UDP не похожи на соединения TCP. Они не остаются активными и таков дизайн UDP.

Метод handlePacket () этого следующего блока кода может делать с полученными данными все, что захочет. И многие клиенты могут отправлять несколько пакетов одному и тому же слушателю UDP. Может быть, это поможет вам.

public void run() {
        DatagramSocket wSocket = null;
        DatagramPacket wPacket = null;
        byte[] wBuffer = null;

        try {
            wSocket = new DatagramSocket( listenPort );
            wBuffer = new byte[ 2048 ];
            wPacket = new DatagramPacket( wBuffer, wBuffer.length );
        } catch ( SocketException e ) {
            log.fatal( "Could not open the socket: \n" + e.getMessage() );
            System.exit( 1 );
        }

        while ( isRunning ) {
            try {
                wSocket.receive( wPacket );
                handlePacket( wPacket, wBuffer );
            } catch ( Exception e ) {
                log.error( e.getMessage() );
            }
        }
    }
3 голосов
/ 21 апреля 2009

Вы смотрели на проект Apache Mina ? Я полагаю, что даже один из его примеров поможет вам настроить сервер на основе UDP. Если бы это был настоящий продукт, я бы не советовал пытаться придумать собственную реализацию с нуля. Для этого вам понадобится библиотека, поэтому вы не используете один поток на соединение, а скорее пул потоков.

1 голос
/ 21 апреля 2009

Я действительно не вижу необходимости.

Это школа, верно?

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

В этом случае Вы должны быть в состоянии выяснить, с какого клиента было отправлено сообщение. (используя информацию из сообщения.) Вы можете держать клиентов на карте.

Наиболее эффективным способом, вероятно, является выполнение всей обработки в главном потоке, если только то, что необходимо сделать, не может "заблокировать" ожидание внешних событий (или если некоторые вещи, которые должны произойти, могут занять много времени, а некоторые - очень короткий.)

public class Client {

    public void handleMessage(Message m) {
    // do stuff here.
    }

}

Возможно, клиентский объект может начать новый поток в handleMessage (), если это необходимо.

Вы не должны запускать несколько потоков сервера.

Серверный поток может сделать:

while(running) {
  socket.receive(DatagramPacket p);
  client = figureOutClient(p);
  client.handleMessage(p);
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...