Простейший чат

 
 
 
Сообщения:1240
import java.net.ServerSocket;
import java.net.Socket;
import java.io.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * Класс сервера. Сидит тихо на порту, принимает сообщение, создает SocketProcessor на каждое сообщение
 */
public class ChatServer {
    private ServerSocket ss; // сам сервер-сокет
    private Thread serverThread; // главная нить обработки сервер-сокета
    private int port; // порт сервер сокета.
    //очередь, где храняться все SocketProcessorы для рассылки
    BlockingQueue<SocketProcessor> q = new LinkedBlockingQueue<SocketProcessor>();

    /**
     * Конструктор объекта сервера
     * @param port Порт, где будем слушать входящие сообщения.
     * @throws IOException Если не удасться создать сервер-сокет, вылетит по эксепшену, объект Сервера не будет создан
     */
    public ChatServer(int port) throws IOException {
        ss = new ServerSocket(port); // создаем сервер-сокет
        this.port = port; // сохраняем порт.
    }

    /**
     * главный цикл прослушивания/ожидания коннекта.
     */
    void run() {
        serverThread = Thread.currentThread(); // со старта сохраняем нить (чтобы можно ее было interrupt())
        while (true) { //бесконечный цикл, типа...
            Socket s = getNewConn(); // получить новое соединение или фейк-соедиение
            if (serverThread.isInterrupted()) { // если это фейк-соединение, то наша нить была interrupted(),
                // надо прерваться
                break;
            } else if (s != null){ // "только если коннект успешно создан"...
                try {
                    final SocketProcessor processor = new SocketProcessor(s); // создаем сокет-процессор
                    final Thread thread = new Thread(processor); // создаем отдельную асинхронную нить чтения из сокета
                    thread.setDaemon(true); //ставим ее в демона (чтобы не ожидать ее закрытия)
                    thread.start(); //запускаем
                    q.offer(processor); //добавляем в список активных сокет-процессоров
                } //тут прикол в замысле. Если попытка создать (new SocketProcessor()) безуспешна,
                // то остальные строки обойдем, нить запускать не будем, в список не сохраним
                catch (IOException ignored) {}  // само же исключение создания коннекта нам не интересно.
            }
        }
    }

    /**
     * Ожидает новое подключение.
     * @return Сокет нового подключения
     */
    private Socket getNewConn() {
        Socket s = null;
        try {
            s = ss.accept();
        } catch (IOException e) {
            shutdownServer(); // если ошибка в момент приема - "гасим" сервер
        }
        return s;
    }

    /**
     * метод "глушения" сервера
     */
    private synchronized void shutdownServer() {
        // обрабатываем список рабочих коннектов, закрываем каждый
        for (SocketProcessor s: q) {
            s.close();
        }
        if (!ss.isClosed()) {
            try {
                ss.close();
            } catch (IOException ignored) {}
        }
    }

    /**
     * входная точка программы
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        new ChatServer(45000).run(); // если сервер не создался, программа
        // вылетит по эксепшену, и метод run() не запуститься
    }

    /**
     * вложенный класс асинхронной обработки одного коннекта.
     */
    private class SocketProcessor implements Runnable{
        Socket s; // наш сокет
        BufferedReader br; // буферизировнный читатель сокета
        BufferedWriter bw; // буферизированный писатель в сокет

        /**
         * Сохраняем сокет, пробуем создать читателя и писателя. Если не получается - вылетаем без создания объекта
         * @param socketParam сокет
         * @throws IOException Если ошибка в создании br || bw
         */
        SocketProcessor(Socket socketParam) throws IOException {
            s = socketParam;
            br = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
            bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream(), "UTF-8") );
        }

        /**
         * Главный цикл чтения сообщений/рассылки
         */
        public void run() {
            while (!s.isClosed()) { // пока сокет не закрыт...
                String line = null;
                try {
                    line = br.readLine(); // пробуем прочесть.
                } catch (IOException e) {
                    close(); // если не получилось - закрываем сокет.
                }

                if (line == null) { // если строка null - клиент отключился в штатном режиме.
                    close(); // то закрываем сокет
                } else if ("shutdown".equals(line)) { // если поступила команда "погасить сервер", то...
                    serverThread.interrupt(); // сначала возводим флаг у северной нити о необходимости прерваться.
                    try {
                        new Socket("localhost", port); // создаем фейк-коннект (чтобы выйти из .accept())
                    } catch (IOException ignored) { //ошибки неинтересны
                    } finally {
                        shutdownServer(); // а затем глушим сервер вызовом его метода shutdownServer().
                    }
                } else { // иначе - банальная рассылка по списку сокет-процессоров
                    for (SocketProcessor sp:q) {
                        sp.send(line);
                    }
                }
            }
        }

        /**
         * Метод посылает в сокет полученную строку
         * @param line строка на отсылку
         */
        public synchronized void send(String line) {
            try {
                bw.write(line); // пишем строку
                bw.write("\n"); // пишем перевод строки
                bw.flush(); // отправляем
            } catch (IOException e) {
                close(); //если глюк в момент отправки - закрываем данный сокет.
            }
        }

        /**
         * метод аккуратно закрывает сокет и убирает его со списка активных сокетов
         */
        public synchronized void close() {
            q.remove(this); //убираем из списка
            if (!s.isClosed()) {
                try {
                    s.close(); // закрываем
                } catch (IOException ignored) {}
            }
        }

        /**
         * финализатор просто на всякий случай.
         * @throws Throwable
         */
        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            close();
        }
    }
}


import java.net.Socket;
import java.io.*;

/**
 * Класс-клиент чат-сервера. Работает в консоли. Командой с консоли shutdown посылаем сервер в оффлайн
 */
public class ChatClient {
    final Socket s;  // это будет сокет для сервера
    final BufferedReader socketReader; // буферизированный читатель с сервера
    final BufferedWriter socketWriter; // буферизированный писатель на сервер
    final BufferedReader userInput; // буферизированный читатель пользовательского ввода с консоли
    /**
     * Конструктор объекта клиента
     * @param host - IP адрес или localhost или доменное имя
     * @param port - порт, на котором висит сервер
     * @throws java.io.IOException - если не смогли приконнектиться, кидается исключение, чтобы
     * предотвратить создание объекта
     */
    public ChatClient(String host, int port) throws IOException {
        s = new Socket(host, port); // создаем сокет
        // создаем читателя и писателя в сокет с дефолной кодировкой UTF-8
        socketReader = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
        socketWriter = new BufferedWriter(new OutputStreamWriter(s.getOutputStream(), "UTF-8"));
        // создаем читателя с консоли (от пользователя)
        userInput = new BufferedReader(new InputStreamReader(System.in));
        new Thread(new Receiver()).start();// создаем и запускаем нить асинхронного чтения из сокета
    }

    /**
     * метод, где происходит главный цикл чтения сообщений с консоли и отправки на сервер
     */
    public void run() {
        System.out.println("Type phrase(s) (hit Enter to exit):");
        while (true) {
            String userString = null;
            try {
                userString = userInput.readLine(); // читаем строку от пользователя
            } catch (IOException ignored) {} // с консоли эксепшена не может быть в принципе, игнорируем
            //если что-то не так или пользователь просто нажал Enter...
            if (userString == null || userString.length() == 0 || s.isClosed()) {
                close(); // ...закрываем коннект.
                break; // до этого break мы не дойдем, но стоит он, чтобы компилятор не ругался
            } else { //...иначе...
                try {
                    socketWriter.write(userString); //пишем строку пользователя
                    socketWriter.write("\n"); //добавляем "новою строку", дабы readLine() сервера сработал
                    socketWriter.flush(); // отправляем
                } catch (IOException e) {
                    close(); // в любой ошибке - закрываем.
                }
            }
        }
    }

    /**
     * метод закрывает коннект и выходит из
     * программы (это единственный  выход прервать работу BufferedReader.readLine(), на ожидании пользователя)
     */
    public synchronized void close() {//метод синхронизирован, чтобы исключить двойное закрытие.
        if (!s.isClosed()) { // проверяем, что сокет не закрыт...
            try {
                s.close(); // закрываем...
                System.exit(0); // выходим!
            } catch (IOException ignored) {
                ignored.printStackTrace();
            }
        }
    }

    public static void main(String[] args)  { // входная точка программы
        try {
            new ChatClient("localhost", 45000).run(); // Пробуем приконнетиться...
        } catch (IOException e) { // если объект не создан...
            System.out.println("Unable to connect. Server not running?"); // сообщаем...
        }
    }

    /**
     * Вложенный приватный класс асинхронного чтения
     */
    private class Receiver implements Runnable{
        /**
         * run() вызовется после запуска нити из конструктора клиента чата.
         */
        public void run() {
            while (!s.isClosed()) { //сходу проверяем коннект.
                String line = null;
                try {
                    line = socketReader.readLine(); // пробуем прочесть
                } catch (IOException e) { // если в момент чтения ошибка, то...
                    // проверим, что это не банальное штатное закрытие сокета сервером
                    if ("Socket closed".equals(e.getMessage())) {
                        break;
                    }
                    System.out.println("Connection lost"); // а сюда мы попадем в случае ошибок сети.
                    close(); // ну и закрываем сокет (кстати, вызвается метод класса ChatClient, есть доступ)
                }
                if (line == null) {  // строка будет null если сервер прикрыл коннект по своей инициативе, сеть работает
                    System.out.println("Server has closed connection");
                    close(); // ...закрываемся
                } else { // иначе печатаем то, что прислал сервер.
                    System.out.println("Server:" + line);
                }
            }
        }
    }
}

 
 
Сообщения:1240
Использование элементарно - компилируем, запускаем сервер. Потом запускаем несколько клиентов. Печатаем сообщения, жмем Ente, видим, как они рассылаются по всем клиентам. Нажав Enter в клиенте без ввода строки, закрываем клиент. Набрав команду shutdown - гасим сервер.
 
 
Сообщения:1240
Вариант сервера чата, построенного на NIO и работающего в одной нити от запуска до окончания. Показано, как прослушивать серверный сокет, как принимать коннекты, рассылать, отключать клиентов, как парсить пришедшую строку декодером и как глушить сервер.

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.*;
import java.nio.channels.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;

/**
 * Класс сервера. Делает все в одной нити.
 */
public class ChatServerNIO implements Runnable{
    private final Selector selector;
    private final ServerSocketChannel ssc;
    private byte[] buffer = new byte[2048];
    private CharBuffer cb = CharBuffer.allocate(2048);
    private Charset ch = Charset.forName("UTF-8");
    private CharsetDecoder decoder = ch.newDecoder();
    //Мапа, где хранятся все SelectionKey и связанные с ним ByteBuffer для рассылки
    Map<SelectionKey, ByteBuffer> connections = new HashMap<SelectionKey, ByteBuffer>();

    /**
     * Конструктор объекта сервера
     * @param port Порт, где будем слушать входящие сообщения.
     * @throws IOException Если не удасться создать сервер-сокет, вылетит по эксепшену, объект Сервера не будет создан
     */
    public ChatServerNIO(int port) throws IOException {
        ssc = ServerSocketChannel.open(); // создаем серверСокет канал
        ssc.configureBlocking(false); // отключаем режим блокирования в ожидании
        ssc.socket().bind(new InetSocketAddress(port)); // получаем обычный серверсокет, который биндиться на нужный порт
        selector = Selector.open(); // создаем селектор прослушки
        ssc.register(selector, SelectionKey.OP_ACCEPT); // регистрируемся на селекторе на сервер-канал.
    }

    /**
     * главный цикл прослушивания/ожидания коннекта.
     */
    public void run() {
        while (true) { //бесконечный цикл, типа...
            try {
                if (selector.isOpen()) {
                    selector.select();  // останавливаемся на ожиданни события от любого из подписанных каналов.
                    Set<SelectionKey> keys = selector.selectedKeys(); // получаем набор ключей (обычно - один)
                    for (SelectionKey sk:keys) {
                        if (!sk.isValid()) {
                            continue;
                        }
                        if (sk.isAcceptable()) { // если к нам коннект...
                            ServerSocketChannel ssca = (ServerSocketChannel)sk.channel(); // (ssca == ssc, кстати)
                            SocketChannel sc = ssca.accept(); // так как точно известно, что ожидает коннект - тут мы без остановки
                            sc.configureBlocking(false); // отключаем режим блокирования
                            // подписываемся только на события прихода данных
                            SelectionKey skr = sc.register(selector, SelectionKey.OP_READ);
                            ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
                            connections.put(skr, byteBuffer);
                            //q.offer(skr); // и ложим в очередь рассылки.
                        } else if (sk.isReadable()){ // если к нам посылка...
                            SocketChannel socketChannel= (SocketChannel)sk.channel(); // хватаем канал коннекта
                            int read;
                            ByteBuffer byteBuffer = connections.get(sk);
                            byteBuffer.clear(); // очищаем байт буфер (кто не успел записаться - тот опоздал)
                            try {
                                read = socketChannel.read(byteBuffer); // пробуем заполнить буфер
                            } catch (IOException e) { // беда, коннект отпал...
                                closeChannel(sk); // закрываем нафик
                                break; // выходим с цикла
                            }
                            if (read == -1) { // коннект отвалился в штатном режиме
                                closeChannel(sk); // тоже закрываем нафик
                                break;
                            } else if (read > 0) { // если что-то прочитали из сокета и записали в буфер...
                                byteBuffer.flip(); // готовим буфер для чтения.
                                byteBuffer.mark(); // ставим метку (ибо декодер нам сломает состояние буффера)
                                if (decodeAndCheck(read, byteBuffer)) break; // если декодер сказал - "выключаем сервер", прерываем цикл
                                byteBuffer.reset(); // если не выключаем сервер - то возвращаемся на метку (кстати, это всегда 0) :)
                                final int pos = byteBuffer.position(); // запоминаем для быстрого проставления у остальных буфферов
                                final int lim = byteBuffer.limit();
                                // получаем сет наборов "ключ-его байтбуффер"
                                Set <Map.Entry<SelectionKey, ByteBuffer>> entries = connections.entrySet();
                                for (Map.Entry<SelectionKey, ByteBuffer> entry: entries) { //цикл по наборам
                                    SelectionKey selectionKey = entry.getKey(); // получаем ключ из набора
                                    selectionKey.interestOps(SelectionKey.OP_WRITE); //переключам в режим "хочу писать!"
                                    ByteBuffer entryBuffer = entry.getValue(); //получаем байт-буффер из набора
                                    entryBuffer.position(pos); // настраиваем его, чтобы правильно записать в сокет.
                                    entryBuffer.limit(lim);
                                }
                            }
                        } else if (sk.isWritable()) { // сообщение о готовности сокета для записи
                            ByteBuffer bb = connections.get(sk); // получаем байт-буффер, ассоциированный с ключем
                            SocketChannel s = (SocketChannel)sk.channel(); // выдергиваем канал
                            try {
                                int result = s.write(bb); // пробуем записать
                                if (result == -1) { // socket properly closed
                                    closeChannel(sk); // ну понятно, закрываем коннект
                                }
                            } catch (IOException e2) { // а это если отвал произошел в моментзаписи
                                closeChannel(sk); // тоже закрываем
                            }
                            if (bb.position() == bb.limit()) { 
                                sk.interestOps(SelectionKey.OP_READ); //сразу ключ переключаем в режим "хотим читать!"
                            }
                        }
                    }
                    keys.clear(); // очищаем сет ключей, мы по идее обработали все.
                } else break;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * Декодер - декодирует поток байтов и превращает в поток символов. Вообще, по сути, было проще
     * превратить команду shutdown в массив байтов и сравнивать по-байтно, но хотелось показать, как
     * декодировать.
     * @param read - сколько байтов прочитали из сокета.
     * @param ba   - ByteBuffer to decode
     * @return true - сервер выключен
     */
    private boolean decodeAndCheck(int read, ByteBuffer ba) {
        cb.clear(); // очищаем CharBuffer перед декодированием...
        // это декодирование, не полное, надо признать, ибо нас интересует лишь стартовая фраза.
        decoder.decode(ba, cb, false);
        cb.flip(); // в чар буфер свалена пришедшая строка после декодирования
        if ("shutdown\n".equals(cb.toString())) { //проверяем, что это "shutdown"
            shutdownServer(); // если да - выключаем сервер
            return true;
        }
        return false;
    }

    /**
     * Метод закрывает канал сокета, снимает со списка активных ключей и удаляет из списка рассылки
     * @param sk - ключ, связанный с каналом
     * @throws IOException - если при закрытии прошла ошибка
     */
    private void closeChannel(SelectionKey sk) throws IOException {
        connections.remove(sk); // удаляем из списка рассылки
        SocketChannel socketChannel = (SocketChannel)sk.channel();
        if (socketChannel.isConnected()) {
            socketChannel.close(); // закрываем канал
        }
        sk.cancel(); // удаляем из списка селектора
    }


    /**
     * метод "глушения" сервера
     */
    private synchronized void shutdownServer() {
        // обрабатываем список рабочих коннектов, закрываем каждый
        Set<SelectionKey> skSet = connections.keySet();
        for (SelectionKey sq:skSet) {
            SocketChannel s = (SocketChannel)sq.channel();
            if (s.isConnected()) {
                try {
                    s.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        if (ssc.isOpen()) {
            try {
                ssc.close();
                selector.close();
            } catch (IOException ignored) {}
        }
    }

    /**
     * входная точка программы
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException{
        new ChatServerNIO(45000).run(); // если сервер не создался, программа
        // вылетит по эксепшену, и метод run() не запуститься
    }

}

Пояснения по серверу. В отличии от IO, в NIO используется не-блокирующая модель. То есть все сокеты связываются с одним (несколькими разными) селектором, и будят того, если у них произошли некоторые изменения (типа коннект пришел или данные из коннекта). Это позволяет держать множество коннектов на одном процессе, не генерируя нить прослушивания на каждый сокет (без чего IO - не работает). На каждый коннект/сокет создается свой ключ, куда проставляются флаги, что нас интересует от коннекта. Для сервер-сокета - это события подключения, для установленного коннекта - это событие прихода данных.

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

В случае коннекта - мы создаем коннект и регистрируем в этом же селекторе. Селектор нам дает созданный ключ коннекта, который настраваем на режим чтения. Одновременно, создаем байт-буффер вокруг массива байт buffer. Это надо затем, чтобы не происходило дублирования информации, так как мы просто пересылаем полученное. Можно ли было использовать один единственный байт-буффер? Нет. Каждый коннект работает со своей скоростью, и может получиться, что в режиме записи некоторые коннекты запишут полученное в два-три (или более захода). Чтобы коннект точно знал, какую часть инфы он записал - у него должен быть персональный байт-буффер.

В случае получения инфы сработает селектор на OP_READ. По ключу выдергиваем канал, а из мапы - байт буффер коннекта. Читаем кусок инфы, и если все хорошо (что-то прочитали), то делаем вот что. Во-первых - декодируем, чтобы отловить команду "shutdown". Во вторых, запоминаем количество считанной информации в байт-буфере коннекта и по циклу настраеваем все ключи/буффера коннекта на это количество, одновременно, все ключи переключаем в режим OP_WRITE, подсказывая селектору, что мы хотим писать.

Как только мы возвращаемся в селектор из режима чтения, селектор понимает, что ключи были переключены в режим записи. Он формирует сет ключей, которые готовы принять информацию. И возвращается в программу. Мы по сету из селектора выдергиваем ключи, по ключам - ассоциированные с ними байт-буферы и пишем долю информации. В идеале - байт-буфер будет записан в один заход на каждом ключе. Но если это не так - оставляем ключ в режиме "хотим писать", означая, что будем ожидать, когда коннект сможет принять еще немного данных. Как только коннект "забрал" все - переключаем его в режим чтения.

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

P.S. Можно использовать с чат-клиентом из первого поста.
 
 
Сообщения:2030
У меня чат проще :wink: , правда ему все равно, получит ли каждый клиент сообщение или нет. К тому же он будет работать только в локальной сети.

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

package ru.javatalks.chat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.MulticastSocket;

/**
 * Main and start class of UDP chat client.
 * It creates and runs all needed objects and threads. 
 */
public class Client
{
    public static final int s_UDP_PORT = 4445;
    public static final int s_UDP_PORT_R = 4446;
    
    public static final int s_BUFFER_SIZE = 1024;
    
    /**
     * InetAddress for MulticastSocket. All recipients will receive messages
     * if they will join this group address.
     */
    public final InetAddress m_group = InetAddress.getByName("230.0.0.1");
    private MulticastSocket m_socket; //UDP Socket to send messages
    
    private PackageSender m_packSender;
    private PackageReader m_packReader;
    
    /**
     * User nickname
     */
    private String m_alias;
    
    public Client(String alias) throws IOException
    {
        m_socket = new MulticastSocket(s_UDP_PORT);
        m_packSender = new PackageSender(this, m_socket);
        m_packReader = new PackageReader(this);
        m_alias = alias;
    }
    
    public void run() throws IOException
    {
        Thread t = new Thread(m_packReader, "reader");
        t.setDaemon(true);
        t.start();

        String line;
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        try
        {
            while(!JesusChristsReturns)
            {
                if((line = in.readLine()) != null)
                {
                    m_packSender.send(m_alias + ": " + line);
                }
            }
        }
        finally
        {
            in.close();
        }
    }
    
    public InetAddress getGroupAddress()
    {
        return m_group;
    }
    
    public static void main(String[] args) throws IOException
    {
        if(args.length < 1)
        {
            System.out.println("usage: nickname is needed.");
            System.exit(1);
        }
        
        new Client(args[0]).run();
    }
} 



Класс для отправки сообщений в сеть.

package ru.javatalks.chat;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;

/**
 * Used to send the messages to all recipient or exactly one of them.
 */
public class PackageSender 
{
    private MulticastSocket m_socket;
    private Client m_client;
    
    public PackageSender(Client client, MulticastSocket socket)
    {
        m_client = client;
        m_socket = socket;
    }

    /**
     * Send the given string to given address
     * @param string
     * @param address
     * @return true if sending successful, false otherwise
     * @throws IOException
     */
    public boolean send(String string, InetAddress address) throws IOException
    {
        boolean res = false;

        final byte buffer[] = new byte[Client.s_BUFFER_SIZE];
        int bLength = string.length() > Client.s_BUFFER_SIZE ? Client.s_BUFFER_SIZE : string.length();
        System.arraycopy(string.getBytes(), 0, buffer, 0, bLength);

        try
        {
            if(m_socket != null && !m_socket.isClosed())
            {
                m_socket.send(new DatagramPacket(buffer, bLength, address, Client.s_UDP_PORT_R));
            }
            res = true;
        }
        catch(UnknownHostException ex)
        {
            ex.printStackTrace();
        }
        
        return res;
    }
    
    /**
     * Send the given string to all known recipients.
     * @param string
     * @return true if sending successful, false otherwise
     * @throws IOException
     */
    public boolean send(String string) throws IOException
    {
        return send(string, m_client.getGroupAddress());
    }
}


Класс чтения сетевого соединения. Все, что читает – выводит на консоль. Запускается как поток-демон.

package ru.javatalks.chat;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.MulticastSocket;

/**
 * Used to read the network and print all received messages to 
 * standard console output. Started as daemon-thread. 
 */
public class PackageReader
    implements Runnable
{
    
    private Client m_client;
    
    public PackageReader(Client client)
    {
        m_client = client;
    }
    
    public void run()
    {
        MulticastSocket socket = null;
        try
        {
            //Create the multicast socket to send messages to the net-group.
            socket = new MulticastSocket(Client.s_UDP_PORT_R);
            socket.joinGroup(m_client.getGroupAddress());
        }
        catch (IOException e)
        {
            System.out.println("Reader didn't start.");
            return;
        }
        
            
        while (true) 
        {
            try
            {
                //Read datagram, transform its data to the string and print it.
                byte[] buffer = new byte[Client.s_BUFFER_SIZE];
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                socket.receive(packet);
                
                System.out.println(new String(packet.getData(), 0, packet.getLength()));
                
                // Reset the length of the packet before reusing it.
                packet.setLength(buffer.length);
            }
            catch(IOException ex)
            {
                System.out.println("Some exception while reading the income stream");
            }
        }
    }
}

Всякое решение плодит новые проблемы
 
 
Сообщения:132
Vurn:
Использование элементарно - компилируем, запускаем сервер. Потом запускаем несколько клиентов. Печатаем сообщения, жмем Ente, видим, как они рассылаются по всем клиентам. Нажав Enter в клиенте без ввода строки, закрываем клиент. Набрав команду shutdown - гасим сервер.
Как-то странно получается - из клиента гасим сервер. Мне кажется, это неудачное решение. Просто я сейчас сталкнулся с проблемой, как на сервере параллельно с подключением клиентов и отправкой сообщений, еще принимать команды из консоли, чтобы была возможность отключать конкретного клиента или закрывать сам сервер?
 
 
Сообщения:5
Скажите а как переделать под ActiveMQ?
 
Модераторы:Нет
Сейчас эту тему просматривают:Нет