Я пишу клиент со встроенным сервером, который должен бесконечно ждать новых подключений - и обрабатывать каждое в потоке.

Я хочу обработать полученный массив байтов в общесистемном обработчике сообщений в основном потоке . Однако в настоящее время обработка явно выполняется в клиентском потоке.

Я просмотрел Futures, submit () ExecutorService, но когда я создаю свои клиентские соединения на сервере, данные будут возвращаться в поток сервера. Как я могу вернуть его оттуда в основной поток (может быть, в синхронизированном хранилище пакетов?), Чтобы обработать его, не блокируя сервер?

Моя текущая реализация выглядит так:

    public class Server extends Thread {
    private int port;
    private ExecutorService threadPool;

    public Server(int port) {
        this.port = port;
        // 50 simultaneous connections
        threadPool = Executors.newFixedThreadPool(50);
    }

    public void run() {
        try{
            ServerSocket listener = new ServerSocket(this.port);
            System.out.println("Listening on Port " + this.port);
            Socket connection;

            while(true){
                try {
                    connection = listener.accept();
                    System.out.println("Accepted client " + connection.getInetAddress());
                    connection.setSoTimeout(4000);

                    ClientHandler conn_c= new ClientHandler(connection);
                    threadPool.execute(conn_c);
                } catch (IOException e) {
                    System.out.println("IOException on connection: " + e);
                }
            }
        } catch (IOException e) {
            System.out.println("IOException on socket listen: " + e);
            e.printStackTrace();
            threadPool.shutdown();
        }
    }
}
class ClientHandler implements Runnable {
    private Socket connection;

    ClientHandler(Socket connection) {
        this.connection=connection;
    }

    @Override
    public void run() {
        try {
            // Read data from the InputStream, buffered
            int count;
            byte[] buffer = new byte[8192];

            InputStream is = connection.getInputStream();
            ByteArrayOutputStream out = new ByteArrayOutputStream();

            // While there is data in the stream, read it
            while ((count = is.read(buffer)) > 0) {
                out.write(buffer, 0, count);
            }
            is.close();
            out.close();

            System.out.println("Disconnect client " + connection.getInetAddress());
            connection.close();
            // handle the received data
            MessageHandler.handle(out.toByteArray());
        } catch (IOException e) {
            System.out.println("IOException on socket read: " + e);
            e.printStackTrace();
        }
        return;

    }
}

Обновление. Похоже, что надежный способ - это то, что предложил TomTom - вместо этого использовать более новый java.nio. Поскольку этот проект имеет ограниченное использование и больше похож на эксперимент, я хотел бы узнать, как лучше всего использовать его с java.io/java.net :)

1
oliverguenther 22 Дек 2010 в 10:00
Так почему именно вы хотите обрабатывать их в основном потоке? NIO позволяет выполнять неблокирующую обработку (а библиотеки, такие как Netty, упрощают использование), но есть ли конкретная причина избегать многопоточного подхода?
 – 
StaxMan
22 Дек 2010 в 10:21
Каждое из этих входящих сообщений может создавать новые исходящие соединения и, следовательно, новые входящие соединения на этом сервере, и я бы хотел избежать запуска всего после захвата сообщения в новых потоках.
 – 
oliverguenther
22 Дек 2010 в 11:49

2 ответа

Лучший ответ

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

По сути, ваша архитектура - это простой / прямой способ, новый способ - использовать java nio, чтобы получить больше сервера параллелизма и повысить производительность, также nio - не лучший способ ...

0
GUi 23 Дек 2010 в 12:51
Одной из идей, которая пришла мне в голову, было использование синхронизированной очереди для входящих пакетов в основном потоке, но я не был уверен, как лучше всего вернуть данные, полученные в клиентском потоке, обратно в основной поток. Я посмотрю на это поближе, спасибо! :) (И пока, поскольку это очень ограниченный проект, я бы хотел придерживаться java.io, потому что я до сих пор не использовал nio.
 – 
oliverguenther
23 Дек 2010 в 13:52

Это не масштабируется. По какой причине вы это делаете? К тому же вы тратите кучу памяти - вам не нужен поток НА РАЗЪЕМ. Используйте лучшую библиотеку ввода-вывода Java. Как тот, который доступен как часть Java около 10 лет (NGIO?). Он обрабатывает x потоков с помощью одного сокета, просто возвращая данные.

0
Joel 22 Дек 2010 в 14:12
Разве вы не имеете в виду один поток с X сокетами? Один поток на сокет не масштабируется выше 10 000 соединений, но обычно этого достаточно. Меня больше беспокоит пул потоков с фиксированным размером 50! Кэшированный пул потоков может быть более подходящим. ;)
 – 
Peter Lawrey
22 Дек 2010 в 10:30
Один поток на сокет был рекомендован в нескольких примерах многопоточного сервера. Я попробую это без потоков, но это оставляет мой вопрос о том, как постоянно возвращать данные, не блокируя поток сервера. (Пул потоков фиксированного размера был моей глупой попыткой решить эту проблему. Я переключусь на кэшированный пул потоков)
 – 
oliverguenther
22 Дек 2010 в 11:34
Что ж, вы найдете МНОГО устаревших примеров. Проверьте en.wikipedia.org/wiki/New_I/O - раздел о селекторах. По сути, у вас есть список сокетов, затем выберите те, у которых есть ожидающие данные, а затем продолжите их обработку. Нет необходимости иметь один поток, ожидающий данных на сокет. Один поток может обрабатывать 1000 или 2000 сокетов, выбирая те, которые содержат данные в цикле. Похоже, это было добавлено на таймфрейме jdk 5. Ваш пример кода, вероятно, ДРЕВНИЙ.
 – 
TomTom
22 Дек 2010 в 12:45
Старые стили снова входят в моду. ;) Неблокирующие сокеты NIO были лучшими из всех, когда он был выпущен в Java 1.4. Я не единственный, кто обнаружил, что использование NIO с блокирующими потоками может быть самым быстрым и эффективным способом управления соединениями в Java 6. ИМХО, использование диспетчерской модели добавляет много сложности при небольшой стоимости в современных системах.
 – 
Peter Lawrey
22 Дек 2010 в 12:56
Что ж, вы также обнаружите, что многие вещи, входящие «в моду», входят в моду от людей, которые на самом деле не знают, что они говорят. Примером может служить приятная фраза «Я не использую базу данных, я использую nosql» от людей, которые в большинстве своем даже не знают, ПОЧЕМУ реляционная база данных работает так, как она работает (реляционная теория). NIO + один блокирующий поток на X сокетов (1000, 2000 и т. Д.) - это хорошо. Однако это не «один поток на сокет», и вопрос также в том, дает ли выигрыш реальная выгода. Они не для меня - и у меня есть приложение, которое распределяет финансовые данные по процессинговым службам в режиме реального времени.
 – 
TomTom
22 Дек 2010 в 14:40