Традиционный ввод-вывод на стороне клиента и NIO на сервере, как мне получить конкретное сообщение, если клиент отправляет несколько сообщений в один и тот же сокет?

Я использую Apache Mina на стороне сервера. У меня есть клиент, написанный по традиции IO. Вот код со стороны КЛИЕНТА, который отправляет данные на сервер.

class SomeClass extends Thread
{
    Socket socket;

    //Constructor
    SomeClass()
    {
        Socket socket = ...
    }

    public void run()
    {
        while (j++ & lt; 10)
        {
            System.out.println("CLIENT[" + clientNo + "] Send Message =>" + requests[clientNo][j]);
            OutputStream oStrm = socket.getOutputStream();
            byte[] byteSendBuffer = (requests[clientNo][j]).getBytes();
            oStrm.write(byteSendBuffer);
            oStrm.flush();
        }
    }
}

Вышеупомянутый поток запускается, скажем, 20 раз. Итак, создано 20 розеток. А в 1 сокет отправляется много сообщений. С сервером, написанным с использованием классов сокетов ввода-вывода, я могу отлично извлекать данные.

Проблема возникает на сервере на базе Apache Mina, который использует БУФЕР! Я не могу получать отдельные сообщения.

Как мне получать отдельные сообщения (учитывая, что я не могу ничего изменить в клиенте, И длина отдельных сообщений неизвестна)

Код на стороне сервера
Создание сокета

    public static void main(String[] args) throws IOException, SQLException {           
        System.out.println(Charset.defaultCharset().name());
        IoAcceptor acceptor = new NioSocketAcceptor();
        ProtocolCodecFilter(charset.newEncoder(),charset.newDecoder() ));
        acceptor.setHandler( new TimeServerHandler() );
        acceptor.getSessionConfig().setReadBufferSize(64 );
        acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
        acceptor.bind( new InetSocketAddress(PORT) );
    }

Код обработчика

    public void messageReceived(IoSession session, Object message) throws Exception {
            AbstractIoBuffer bf = (AbstractIoBuffer)message;
            Charset charset = Charset.forName("UTF-8");
            CharsetDecoder decoder = charset.newDecoder();
            String outString = bf.getString(decoder);

    }

person shahalpk    schedule 21.11.2012    source источник
comment
Не могли бы вы отправить важную часть кода на стороне сервера? В этом вопросе код на стороне клиента бесполезен.   -  person Uwe Plonus    schedule 21.11.2012


Ответы (2)


Как мне получать отдельные сообщения

Вы этого не сделаете. В TCP нет такого понятия, как сообщение. Это протокол байтового потока. Нет границ сообщений и нет гарантии, что одно чтение равно одной записи на другом конце.

(учитывая, что я не могу ничего изменить в клиенте, И длина отдельных сообщений неизвестна)

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

person user207421    schedule 21.11.2012

На самом деле MINA - это очень тщательно продуманный фреймворк для элегантного решения вашей проблемы. Его основная концепция - это цепочка фильтров, в которой к входящему сообщению применяется ряд фильтров.

Вы должны реализовать декодер протокола (реализующий MessageDecoder) и зарегистрировать его в цепочке фильтров MINA. Этот декодер должен анализировать байтовые буферы для представления объекта по вашему выбору.

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

person onon15    schedule 21.11.2012
comment
но в моем случае буфер содержит содержимое более чем одного сообщения. например: Msg1: Hai Tom, Msg2: Hai Peter ... когда оба отправляются в один и тот же сокет, в буфере я получаю BufferMsg: Hai TomHai Peter. Как это сделать в фильтрах? Из того, что я читал о фильтрах, это невозможно. любая помощь? - person shahalpk; 21.11.2012
comment
Возможно, вам следует пересмотреть дизайн вашего протокола. Помните, что TCP-сокет - это канал: на принимающей стороне вы получаете поток байтов, не зная, какова их предполагаемая фрагментация. Рассмотрите возможность использования в вашем канале протокола, который будет сигнализировать о границах сообщения. Обычные способы сделать это - использовать символ-разделитель, например символ '\ 0' между сообщениями, или структуру сообщения, в которой вы отправляете длину сообщения (в байтах, коротких или целых числах), а затем это количество байтов в теле сообщения. . - person onon15; 21.11.2012
comment
не out.flush () делает то, что вы сказали? - person shahalpk; 21.11.2012
comment
Короткий ответ: иногда. Вы не можете доверять ему в этом, потому что маршрутизаторы (или даже локальный стек TCP) могут объединять несколько сообщений в одно или фрагментировать длинное сообщение на несколько. Суть в том, что TCP-соединение - это двунаправленный канал; вы не можете использовать что-либо, кроме последовательности байтов, которую вы можете прочитать. - person onon15; 21.11.2012
comment
(Вы можете рассмотреть UDP, где сообщение является сообщением. Но тогда вам придется обрабатывать потерю пакетов, переупорядочение и т. Д.). - person onon15; 21.11.2012