Модераторы: LSD, AntonSaburov

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> MINA DemuxTest, Как бы не работает  
:(
    Опции темы
Platon
Дата 16.10.2007, 20:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1801
Регистрация: 25.4.2006

Репутация: 3
Всего: 40



Здравствуйте, уважаемые.

Решил наконец опробовать Apache Mina и не стал мелочиться, сразу стал эксперементировать с демуксами, выглядит очень заманчиво!!! Но простой тест потерпел фиаско.

Код

import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.SimpleByteBufferAllocator;
import org.apache.mina.common.IoAcceptor;
import org.apache.mina.common.IoSession;
import org.apache.mina.transport.socket.nio.SocketAcceptor;
import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
import org.apache.mina.filter.LoggingFilter;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.handler.demux.DemuxingIoHandler;
import org.apache.mina.handler.demux.MessageHandler;
import org.apache.log4j.Logger;

import java.nio.charset.Charset;
import java.net.InetSocketAddress;
import java.io.IOException;

public class DemuxTest {

    public static final int PORT = 4312;

    public static void main(String[] args) throws IOException {
        Logger.getLogger(org.apache.log4j.Logger.class);
        ByteBuffer.setUseDirectBuffers(false);
        ByteBuffer.setAllocator(new SimpleByteBufferAllocator());

        IoAcceptor acceptor = new SocketAcceptor();

        SocketAcceptorConfig cfg = new SocketAcceptorConfig();
        cfg.getSessionConfig().setReuseAddress( true );
        cfg.getFilterChain().addLast( "logger", new LoggingFilter() );
        cfg.getFilterChain().addLast( "codec", new ProtocolCodecFilter(new DemuxingProtocolCodecFactory()));
        DemuxingIoHandler d = new DemuxingIoHandler();
        d.addMessageHandler(DD.class, new MyHandler());

        acceptor.bind( new InetSocketAddress(PORT), new DemuxingIoHandler(), cfg);
        System.out.println("Initialized");
    }
}

class DD {

}

class MyHandler implements MessageHandler<DD> {

    public void messageReceived(IoSession session, DD message) throws Exception {
        System.out.println("Hello!!!");
    }
}


Код

import org.apache.mina.transport.socket.nio.SocketConnector;
import org.apache.mina.transport.socket.nio.SocketConnectorConfig;
import org.apache.mina.example.echoserver.ssl.BogusSSLContextFactory;
import org.apache.mina.filter.SSLFilter;
import org.apache.mina.common.ConnectFuture;
import org.apache.mina.common.IoSession;
import org.apache.mina.handler.demux.DemuxingIoHandler;
import org.apache.log4j.Logger;

import javax.net.ssl.SSLContext;
import java.net.SocketAddress;
import java.net.InetSocketAddress;

public class Client {

    private IoSession session;

    public boolean connect(SocketConnector connector, SocketAddress address) {
        Logger.getLogger(org.apache.log4j.Logger.class);
        if (session != null && session.isConnected()) {
            throw new IllegalStateException(
                    "Already connected. Disconnect first.");
        }

        try {

            SocketConnectorConfig config = new SocketConnectorConfig();
            config.getFilterChain().addLast( "codec", new ProtocolCodecFilter(new DemuxingProtocolCodecFactory()));
            DemuxingIoHandler d = new DemuxingIoHandler();
            d.addMessageHandler(DD.class, new MyHandler());

            ConnectFuture future1 = connector.connect(address, d, config);
            future1.join();
            if (!future1.isConnected()) {
                return false;
            }
            session = future1.getSession();
            session.write(new DD());
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public static void main(String[] args) {
        new Client().connect(new SocketConnector(), new InetSocketAddress("127.0.0.1", 4312));
    }
}


Тут 2 проблемы, как правильно оформить логгер, выдает сообщения о необходимости инициализации:
Цитата

log4j:WARN No appenders could be found for logger (org.apache.mina.handler.demux.DemuxingIoHandler).
log4j:WARN Please initialize the log4j system properly.


и почему сервер так и не выводит "Hello"?

Это сообщение отредактировал(а) Platon - 16.10.2007, 20:59
PM MAIL ICQ   Вверх
Platon
Дата 16.10.2007, 21:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1801
Регистрация: 25.4.2006

Репутация: 3
Всего: 40



Невнимательно читал, на сайте есть пример работы с демюксами, так что пока можете не помогать *CONFUSED*

Это сообщение отредактировал(а) Platon - 16.10.2007, 21:41
PM MAIL ICQ   Вверх
Platon
Дата 17.10.2007, 10:41 (ссылка) |    (голосов:3) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1801
Регистрация: 25.4.2006

Репутация: 3
Всего: 40



Ну, что ж, уважаемые. Сочту за честь поделиться накопленным за 2 дня опытом в сфере Apache MINA Demuxing
Предыстория:
Эпиграф: "Яви мне чудо!"
Началось это давно, когда я решил написать тестовую систему задачек для школьников и студентов. Взялся с ходу накатал сервер, клиент, даже архитектура была приятной, легко расширяемый протокол сервера, но 1 НО, проблема с потоками, я сделал простенькую схему 1 соединение - 1 поток. Нет, не думайте, что я лузер, и моя система полетела, нет она работала нормально, даже 500 клиентов легко держала, но меня угрызали сомнения: "Все равно не по умному сделал, расход ресурсов большой, да и 500-1000 потоков - это какое-то больное приложение". Сел делать опять же свою сетевую архитектуру. Все, теперь были worker'ы в пуле, все путем, все красиво, но так и ни разу не откомпилировав, ни разу не запустив, обратился я к его величеству "Google", с 1 только вопросом: "О, Великий Google ты знаешь всех и вся, скажи маюсь ли я дурью?", Google немного подумал, и выдал мне свой немногословный вердикт: "Маешься", любезно указав на сайт http://mina.apache.org просвещенный от беседы с гуру, я направился в святая святых - сайт проекта MINA, и нашел я там своё умиротворение.

Причины:
В MINA есть много всяких разных протоколов, которые подходят для рядовых задач(типа протокола текстовых строк или серриализации), но я привык экономить на спичках, поэтому решил выбрать самый экономный(на траффике) и гибкий протокол а именно некий Demux

Т.к. на сайте еще не было ни одной статьи по MINA (чему я был очень удивлен), я взял смелось возложить на себя такую ответственность и размочить нулевой счет.

Действие:
Я не собираюсь давать основы MINA, они довольно толково описаны на сайте http://mina.apache.org/documentation.html, повторяюсь, я лишь делюсь полученным за 2 дня опытом.

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

Введем такие классы как
Код

package platon.mina.examples.common;

public class CountMessage {
    public int a;
    public int b;
    public static final int TYPE = 1;
}

и 
Код

package platon.mina.examples.common;

public class HelloMessage {
    public String hello = "Hello";
    public static final int TYPE = 2;
}

и
Код

package platon.mina.examples.common;

public class Options {
    public static final int PORT = 4321;
}

Начнем с сервера:

Код

package platon.mina.examples.server;

import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.SimpleByteBufferAllocator;
import org.apache.mina.common.IoAcceptor;
import org.apache.mina.transport.socket.nio.SocketAcceptor;
import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
import org.apache.mina.filter.codec.demux.DemuxingProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.handler.demux.DemuxingIoHandler;

import java.net.InetSocketAddress;
import java.io.IOException;

import platon.mina.examples.common.Options;
import platon.mina.examples.common.CountMessage;
import platon.mina.examples.common.HelloMessage;
import platon.mina.examples.server.handlers.HelloHandler;
import platon.mina.examples.server.handlers.CountHandler;

public class Server {
    public static void main(String[] args) {
        ByteBuffer.setUseDirectBuffers(false);
        ByteBuffer.setAllocator(new SimpleByteBufferAllocator());

        IoAcceptor acceptor = new SocketAcceptor();
        
        DemuxingProtocolCodecFactory demuxFactory = new DemuxingProtocolCodecFactory();
        demuxFactory.register(new MyDecoder());

        SocketAcceptorConfig cfg = new SocketAcceptorConfig();
        cfg.getSessionConfig().setReuseAddress( true );
        cfg.getFilterChain().addLast( "codec", new ProtocolCodecFilter(demuxFactory));

        DemuxingIoHandler d = new DemuxingIoHandler();
        d.addMessageHandler(CountMessage.class, new CountHandler());
        d.addMessageHandler(HelloMessage.class, new HelloHandler());

        try {
            acceptor.bind( new InetSocketAddress(Options.PORT), d, cfg);
            System.out.println("Initialized");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Стандартная структура строительства сервера.

Код

DemuxingProtocolCodecFactory demuxFactory = new DemuxingProtocolCodecFactory();
demuxFactory.register(new MyDecoder());

Здесь мы инициализируем demux фабрику и регистрируем всех обработчиков входных данных
В моем случае сделан просто 1, но можно сделать и 2 (CounerDecoder и HelloDecoder) и зарегистрировать их по очереди
Код

demuxFactory.register(new (CounerDecoder());
demuxFactory.register(new HelloDecoder());

Я сделал 1 обработчик для простоты.

Еще 1 отличающий момент - это обработчик сообщений:
Код

DemuxingIoHandler d = new DemuxingIoHandler();
d.addMessageHandler(CountMessage.class, new CountHandler());
d.addMessageHandler(HelloMessage.class, new HelloHandler());

Как мы видим здесь к каждому сообщению (причем оно уже высокоуровневое, это очень приятно) мы приписываем свой обработчик, т.е. CountHandler обрабатывает все сообщения типа CountMessage. а HelloHandler обрабатывает все сообщения типа HelloMessage.

Подведем итоги:
В экземпляр класса DemuxingProtocolCodecFactory мы регистрируем обработчика низкоуровневых сообщений (буквально поток байтов), который ддолжен выдавать нам высокоуровневое сообщение в виде POJO
В экземпляре DemuxingIoHandler мы связываем тип сообщения с его обработчиком. (Не попробовал случай когда 1 сообщению привязаны 2 обработчика или наоборот, и думаю не стоит)


Слудующий обработчик низкоуровневых сообщений
Код

package platon.mina.examples.server;

import org.apache.mina.filter.codec.demux.MessageDecoder;
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.common.IoSession;
import org.apache.mina.common.ByteBuffer;
import platon.mina.examples.common.CountMessage;
import platon.mina.examples.common.HelloMessage;

import java.nio.charset.Charset;

public class MyDecoder implements MessageDecoder {


    public MessageDecoderResult decodable(IoSession session, ByteBuffer in) {
        if (in.remaining() < 4)
            return MessageDecoderResult.NEED_DATA;
        int type = in.getInt();
        if (type != CountMessage.TYPE && type != HelloMessage.TYPE)
            return MessageDecoderResult.NOT_OK;
        return MessageDecoderResult.OK;
    }

    public MessageDecoderResult decode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception {
        if (in.remaining() < 4)
            return MessageDecoderResult.NEED_DATA;
        int type = in.getInt();
        switch(type) {
            case CountMessage.TYPE : {
                if (in.remaining() < 8)
                    return MessageDecoderResult.NEED_DATA;
                CountMessage message = new CountMessage();
                message.a = in.getInt();
                message.b = in.getInt();
                out.write(message);
                break;
            }
            case HelloMessage.TYPE : {
                // Длина строки "hello" в формате UTF-8
                if (in.remaining() < 5)
                    return MessageDecoderResult.NEED_DATA;
                HelloMessage message = new HelloMessage();
                message.hello = in.getString(Charset.forName("UTF-8").newDecoder());
                out.write(message);
                break;
            }
            default: return MessageDecoderResult.NOT_OK;
        }
        return MessageDecoderResult.OK;
    }

    public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {
        // пока не знаю зачем эта вещь
    }
}


Метод decodable сообщает нам есть ли возможность прочитать данным обработчиком текущий поток данных. Во всех чтения из буфера необходимо убедиться, что данные в нем есть и их хватит на проведение операции. Если у нас не хватает данных, мы сообщаем MessageDecoderResult.NEED_DATA.
В нашем случае мы допускаем сообщения с типом счетного сообщения или сообщения приветствия. Где и как мы генерируем тип будет видно дальше по тексту в классе MyEncoder

в методе decode мы мы непосредственно декодируем полученные в наше управление данные.
извлекаем тип и по типу восстанавливаем POJO объекты, причем мы видим, что если сначала мы смотрели как бы нам хватило 4 байт чтобы узнать тип, то тепеь в каждой ветке условия мы проверяем свой индивидуальный размер, для CountMessage это 8 ибо 2 int столько весят, с сообщением сложнее, я пока не знаю как декодировать строку, поэтому сделал ее фиксированной длинны, 5 потому что "hello" в UTF-8 весит столько, но "Привет" весит 12.

С декодированием (самой сложной частью) разобрались, теперь беремся за клиента
Код

package platon.mina.examples.client;

import org.apache.mina.common.IoSession;
import org.apache.mina.common.ConnectFuture;
import org.apache.mina.transport.socket.nio.SocketConnector;
import org.apache.mina.transport.socket.nio.SocketConnectorConfig;
import org.apache.mina.filter.codec.demux.DemuxingProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.handler.demux.DemuxingIoHandler;

import java.net.SocketAddress;
import java.net.InetSocketAddress;

import platon.mina.examples.common.CountMessage;
import platon.mina.examples.common.HelloMessage;
import platon.mina.examples.common.Options;

public class Client {

    public void connect(SocketConnector connector, SocketAddress address) {
        try {
            SocketConnectorConfig config = new SocketConnectorConfig();
            DemuxingProtocolCodecFactory factory = new DemuxingProtocolCodecFactory();
            factory.register(new MyEncoder());
            config.getFilterChain().addLast( "codec", new ProtocolCodecFilter(factory));
            ConnectFuture future1 = connector.connect(address, new DemuxingIoHandler(), config);
            future1.join();
            if (!future1.isConnected()) {
                return;
            }

            CountMessage message = new CountMessage();
            message.a = 4;
            message.b = 6;
            IoSession session = future1.getSession();
            System.out.println("send count");
            session.write(message);
            System.out.println("send hello");
            session.write(new HelloMessage());


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new Client().connect(new SocketConnector(), new InetSocketAddress("127.0.0.1", Options.PORT));
    }
}

Тоже стандартная конструкция, которую я выдрал из мана.
Здесь опять пишем 
Код

DemuxingProtocolCodecFactory factory = new DemuxingProtocolCodecFactory();
factory.register(new MyEncoder());

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

Собственно и всё, здесь не предусмотренно никаких входных данных, поэтому мы с чистой совестью можем создать просто demux обработчик, не связывая в нем никаких обработчиков
Код

ConnectFuture future1 = connector.connect(address, new DemuxingIoHandler(), config);


Теперь MyEncoder
Код

package platon.mina.examples.client;

import org.apache.mina.filter.codec.demux.MessageEncoder;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.apache.mina.common.IoSession;
import org.apache.mina.common.ByteBuffer;

import java.util.Set;
import java.util.HashSet;
import java.nio.charset.Charset;

import platon.mina.examples.common.CountMessage;
import platon.mina.examples.common.HelloMessage;

public class MyEncoder implements MessageEncoder {

    private static final Set<Class<?>> TYPES = new HashSet<Class<?>>();

    static {
        TYPES.add(CountMessage.class);
        TYPES.add(HelloMessage.class);
    }

    public Set<Class<?>> getMessageTypes() {
        return TYPES;
    }

    public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
        // обратите внимание : это не java.nio.ByteBuffer, это org.apache.mina.common.ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(16);
        if (message instanceof CountMessage) {
            CountMessage countMessage = (CountMessage)message;
            // Зададим тип 1 счетному сообщению
            buffer.putInt(CountMessage.TYPE);
            buffer.putInt(countMessage.a);
            buffer.putInt(countMessage.b);
        } else if (message instanceof HelloMessage) {
            // Зададим тип 2 сообщению приветствия
            buffer.putInt(HelloMessage.TYPE);
            buffer.putString(((HelloMessage)message).hello, Charset.forName("UTF-8").newEncoder());
        }
        buffer.flip();
        out.write(buffer);
    }
}

Тут интересность состоит в private static final Set<Class<?>> TYPES. В этой коллекции содержатся все типы классов (не знаю как это правильно сказать), которые подлежат обработке именно этим обработчиком.

encode просто делает свое дело, нам надо просто диффиринцировать мух от котлет, чем мы и занимаемся, проверяя тип объекта. Далее мы видим, то что упоминалось ранее: тип мы записываем 1-м в сообщении, затем все данные по очереди, при чем ОБЯЗАТЕЛЬНО в том порядке в котором они будут считываться, или если по-русски, то наоборот.

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

package platon.mina.examples.server.handlers;

import org.apache.mina.handler.demux.MessageHandler;
import org.apache.mina.common.IoSession;
import platon.mina.examples.common.CountMessage;

public class CountHandler implements MessageHandler<CountMessage> {

    public void messageReceived(IoSession session, CountMessage message) throws Exception {
        System.out.println("message received from " + session + ". Counting " + message.a + "+" + message.b + "=" + (message.a + message.b));
    }
}


и 

Код

package platon.mina.examples.server.handlers;

import org.apache.mina.handler.demux.MessageHandler;
import org.apache.mina.common.IoSession;
import platon.mina.examples.common.HelloMessage;

public class HelloHandler implements MessageHandler<HelloMessage> {

    public void messageReceived(IoSession session, HelloMessage message) throws Exception {
        System.out.println("message received from " + session + "\"" + message.hello + "\"");
    }
}


Как мы видим, тут все просто. Мы принимаем высокоуровневое сообщение и делаем с ним что захотим с приятной возможностью отписывать в session прямо тут же.
PM MAIL ICQ   Вверх
batigoal
Дата 17.10.2007, 11:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Нелетучий Мыш
****


Профиль
Группа: Участник Клуба
Сообщений: 6423
Регистрация: 28.12.2004
Где: Санктъ-Петербургъ

Репутация: 2
Всего: 151



Однозначно в FAQ.


--------------------
"Чтобы правильно задать вопрос, нужно знать большую часть ответа" (Р. Шекли)
ЖоржЖЖ
PM WWW   Вверх
Platon
Дата 17.10.2007, 12:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1801
Регистрация: 25.4.2006

Репутация: 3
Всего: 40



Спасибо за отзыв. Я просто не могу понять, почему по MINA на этом форуме только 1 захудалая темка? Неужели людям приятней копошиться с NIO, и делать инвалидные приложения?
Тут, конечно 2 вопроса остаются:
1. У меня логгеры так и не работают, выдают варнинги.
2. Как предложите по уму передавать строки?
Простой вариант делать в начале метку длинны строки, но тоже неприятно.

PM MAIL ICQ   Вверх
COVD
Дата 17.10.2007, 16:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 26.7.2005

Репутация: 11
Всего: 43



Цитата

Спасибо за отзыв. Я просто не могу понять, почему по MINA на этом форуме только 1 захудалая темка? Неужели людям приятней копошиться с NIO, и делать инвалидные приложения?


А что, многие копошатся с НИО? Я думаю MINA относится к специфическим разделам серверного программирования, поэтому и не обсуждается широко. НИО все еще относительно новая технология и относительно низкого уровня. Реализация http серверов на nio как раз сейчас происходит. А большинство программистов осваивают готовые решения J2EE. Так сказать, согласно лозунгам, быстро и на высоком профессиональном уровне удовлетворяют потребности индустрии. Кроме того, понять и оценить то, что они там в MINA (COMET еще есть на апаче, тоже интересная штука) сваяли, можно только попробовав своими руками. Наверняка много чего другого в интернете (благодаря вашей ссылке я вышел на FIX , но некогда разбираться). Приходится делать "инвалидную", но работающую программу, а потом уже с пониманием дела находить и адаптировать готовое решение.

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

Реплика относительно обработчика низкоуровневых сообщений (другое не смотрел). Как я понял, если в буфере не хватает данных для восстановления сообщения, то возвращается код NEED_DATA. А надо бы данные из буфера забрать. Буфер имеет фиксированную длину. Если длина сообщения превышает длину буфера, то вы никогда не прочитаете это сообщение. А если будете забирать сколько есть, так и размер буфера не будет важен. Вообще непонятно, зачем какой-то код возвращать надо. Забрали из своего буфера все что есть и спасибо.  



PM MAIL   Вверх
Platon
Дата 17.10.2007, 17:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1801
Регистрация: 25.4.2006

Репутация: 3
Всего: 40



COVD, по реплике кину реплику.
На самом деле я с прицелом отметил, что ByteBuffer не NIO а MINA, т.е. он как раз и занимается забором всех принятых с сокета данных. Тем самым, MINA освобождает нас от такой работы, как создание механизма хранения принятых данных по сессиям.
PM MAIL ICQ   Вверх
COVD
Дата 17.10.2007, 17:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 26.7.2005

Репутация: 11
Всего: 43



Замечательная услуга. А я купился на название ByteBuffer. Теперь разглядел, что пакет другой.   
PM MAIL   Вверх
Platon
Дата 17.10.2007, 17:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1801
Регистрация: 25.4.2006

Репутация: 3
Всего: 40



А как осветить основы? Перевести стартовые туториалы с сайта MINA?
PM MAIL ICQ   Вверх
Platon
Дата 17.10.2007, 17:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1801
Регистрация: 25.4.2006

Репутация: 3
Всего: 40



Цитата(COVD @  17.10.2007,  16:27 Найти цитируемый пост)
думаю MINA относится к специфическим разделам серверного программирования

Вот этого я не понял. Почему же таки специфическая? Я думаю, что в сетевых играх это будет то, что надо!

J2EE в принципе понял, но не принял.
PM MAIL ICQ   Вверх
COVD
Дата 17.10.2007, 18:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 26.7.2005

Репутация: 11
Всего: 43



Цитата

Вот этого я не понял. Почему же таки специфическая? Я думаю, что в сетевых играх это будет то, что надо!


Наверное, я горячусь. Я бы сетевые игры тоже отнес к специфическим. А мэйнстрим - это все, что на application server крутится. HTML-XML туда сюда гоняется.

Кстати, одна из вечных проблем  - преодоление файерволов и прочего, так называемый http туннелинг. Понятно, что это не касается соединяющихся по http, потому он такой и популярный. Фактически, клиент всегда должен иметь в качестве запасного варианта возможность соединиться по http. Интересно, как эта проблема решается в MINA. Понятно, они там вроде пишут что любые протоколы. Но наверное это не делается автоматически? Интересовались ли вы этим вопросом?

Добавлено через 9 минут и 13 секунд
Цитата

А как осветить основы? Перевести стартовые туториалы с сайта MINA?


А фиг его знает. Переводить не надо, а лучше своими словами. Я вот примерно имею представление, а пошел смотреть их презентацию по вашей ссылке и с ходу мало что понял. Картинки красивые. У всех smile. Это конечно надо в теме быть. В общем, это искусство несколькими фразами обьяснить для чего и как и кому это может пригодиться.
PM MAIL   Вверх
Platon
Дата 17.10.2007, 19:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1801
Регистрация: 25.4.2006

Репутация: 3
Всего: 40



Честно сказать, мое плотное знакомство с MINA длится ровно 2 дня *CONFUSED* (кстати почему нет этого смайлика?) До этого я только ходил вокруг и облизывался.

Сам лично еще не одного проекта, даже учебного, не сделал.
PM MAIL ICQ   Вверх
COVD
Дата 17.10.2007, 20:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 26.7.2005

Репутация: 11
Всего: 43



Цитата

Честно сказать, мое плотное знакомство с MINA длится ровно 2 дня 


 smile 
PM MAIL   Вверх
Platon
Дата 18.10.2007, 00:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1801
Регистрация: 25.4.2006

Репутация: 3
Всего: 40



Посмотрел про HTTP, мде, так просто то и не получится.
PM MAIL ICQ   Вверх
goodday1941
Дата 11.11.2007, 23:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 709
Регистрация: 16.7.2006
Где: Украина, Киев

Репутация: нет
Всего: 9



очень интерестное апи! но тут возникли проблемки при тестировании. Как реализовать двустороннюю связь между клиентом и сервером?

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

Это сообщение отредактировал(а) goodday1941 - 13.11.2007, 13:50


--------------------
SCJP 6
PM MAIL ICQ Skype GTalk   Вверх
goodday1941
Дата 13.11.2007, 18:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 709
Регистрация: 16.7.2006
Где: Украина, Киев

Репутация: нет
Всего: 9



я так понял никто с этим вопросом не разбирался :( ладно буду сам копать, результаты раскопок выложу когда чего-то накопаю smile


--------------------
SCJP 6
PM MAIL ICQ Skype GTalk   Вверх
goodday1941
Дата 14.11.2007, 16:05 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 709
Регистрация: 16.7.2006
Где: Украина, Киев

Репутация: нет
Всего: 9



И так, представляю вашему вниманию весьма бесполезную программу, которая делает следующее:
1. Клиент соединяется  с сервером.
2. Клиент отправляет 1000 объектов серверу. Сервер, в свою очередь, приняв объект, отсылает всем существующим соединениям данный объект.
3. Клиент разрывает соединение.

Для всего этого понадобиться три библиотеки: mina-core-1.1.4.jar (собственно сам Apache MINA API), slf4j-api-1.4.3.jar, slf4j-simple-1.4.3.jar(библиотеки которые нужны для работы логгера в MINA slf4j).

И так, собственно, сам сервера Server.java:
Код

package Chat;

import java.net.InetSocketAddress;
import java.io.IOException;

import org.apache.mina.common.DefaultIoFilterChainBuilder;
import org.apache.mina.common.IoAcceptor;
import org.apache.mina.common.IoAcceptorConfig;
import org.apache.mina.filter.LoggingFilter;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.transport.socket.nio.SocketAcceptor;
import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;

public class Server {
    private static final int PORT = 1234;

    public static void main(String[] args) throws Exception {
        IoAcceptor acceptor = new SocketAcceptor();
        IoAcceptorConfig config = new SocketAcceptorConfig();
        DefaultIoFilterChainBuilder chain = config.getFilterChain();
        chain.addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
        addLogger(chain);

        try {
            acceptor.bind(new InetSocketAddress(PORT), new ServerHandler(),
                    config);
            System.out.println("Listening on port " + PORT);
        } catch (IOException e) {
            System.out.println("Can`t start server.");
        }
    }

    private static void addLogger(DefaultIoFilterChainBuilder chain)
            throws Exception {
        chain.addLast("logger", new LoggingFilter());
    }
}

Слушатель сессии на сервере ServerHandler.java:

Код

package Chat;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.apache.mina.common.IoHandlerAdapter;
import org.apache.mina.common.IoSession;

public class ServerHandler extends IoHandlerAdapter {
    private Set<IoSession> sessions = Collections
            .synchronizedSet(new HashSet<IoSession>());

    public void exceptionCaught(IoSession session, Throwable cause) {
        session.close();
    }

    public void messageReceived(IoSession session, Object message) {
        for (IoSession s:sessions){
            if (s.isConnected()){
                s.write(message);
            } else {
                sessions.remove(session);
            }
        }
    }

    public void sessionClosed(IoSession session) throws Exception {
        sessions.remove(session);
    }

    public void sessionCreated(IoSession session) throws java.lang.Exception {
        sessions.add(session);
    }
}

Теперь клиентская часть Client.java:

Код

package Chat.client;

import java.net.InetSocketAddress;

import org.apache.mina.transport.socket.nio.SocketConnector;
import Chat.TestObject;

public class Client {
    private ClientSupport client;
    private ClientHandler handler;
    private SocketConnector connector;

    public Client() {
        connector = new SocketConnector();
        if (!connect()){
            System.out.println("Could not connect to localhost:1234. ");
            return;
        }
       for (int i = 0; i< 1000; i++)
        if (!sendMessage(new TestObject(i))){
            System.out.println("Can`t send message: session closed");
        }
        disconnect();
    }

    private void disconnect(){
        try {
            if (client != null){
                client.quit();
            }
        } catch (Exception e1) {
            System.out.println("Session could not be closed.");
        }
    }

    private boolean sendMessage(Object o){
        return client.sendMessage(o);
    }

    private boolean connect(){
        handler = new ClientHandler();
        client = new ClientSupport(handler);
        return client.connect(connector, new InetSocketAddress("localhost", 1234));
    }

    public static void main(String[] args) {
        new Client();
    }
}

Далее, вспомогательный класс, в который вынесено соединение с сервером, отправка сообщений и разрыв соединения с сервером ClientSupport.java:
Код

package Chat.client;

import java.net.SocketAddress;

import org.apache.mina.common.ConnectFuture;
import org.apache.mina.common.IoHandler;
import org.apache.mina.common.IoSession;
import org.apache.mina.transport.socket.nio.SocketConnector;
import org.apache.mina.transport.socket.nio.SocketConnectorConfig;

public class ClientSupport {
    private final IoHandler handler;

    private IoSession session;

    public ClientSupport(IoHandler handler) {
        this.handler = handler;
    }

    public boolean connect(SocketConnector connector, SocketAddress address) {
        if (session != null && session.isConnected()) {
            throw new IllegalStateException(
                    "Already connected. Disconnect first.");
        }
        try {
            SocketConnectorConfig config = new SocketConnectorConfig();
            ConnectFuture future1 = connector.connect(address, handler, config);
            future1.join();
            if (!future1.isConnected()) {
                return false;
            }
            session = future1.getSession();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public boolean sendMessage(Object o) {
        if (session != null && session.isConnected()){
            session.write(o);
            return true;
        } else {
            return false;
        }
    }

    public void quit() {
        if (session != null) {
            session.close();
            if (session.isConnected()) {
                session.getCloseFuture().join();
            }
            System.out.println(session.isClosing() + " " + session.isConnected());
        }
    }
}

И слушатель сессии на клиенте ClientHandler.java:
Код

package Chat.client;

import org.apache.mina.common.IoFilter;
import org.apache.mina.common.IoHandlerAdapter;
import org.apache.mina.common.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.filter.LoggingFilter;
import Chat.TestObject;

public class ClientHandler extends IoHandlerAdapter {

    public void sessionCreated(IoSession session) throws Exception {
        IoFilter loggingFilter = new LoggingFilter();
        IoFilter codecFilter = new ProtocolCodecFilter(new ObjectSerializationCodecFactory());
        session.getFilterChain().addLast("codec", codecFilter);
        session.getFilterChain().addLast("logger", loggingFilter);
    }

    public void sessionOpened(IoSession session) throws Exception {
        System.out.println("Connected.");
    }

    public void messageReceived(IoSession session, Object message)
            throws Exception {
        if (message instanceof TestObject){
            System.out.println("TestObject resived! " + ((TestObject)message).getN());
        }
    }

    public void sessionClosed(IoSession session) throws Exception {
        System.out.println("Connection closed.");
    }

    public void exceptionCaught(IoSession ioSession, Throwable throwable) throws java.lang.Exception {
        System.out.println("Exception: " + throwable.getMessage());
    }
}

Тестовый обьект TestObject.java:
Код

package Chat;

import java.io.Serializable;

public class TestObject implements Serializable{
    private int n;

    public TestObject(int n) {
        this.n = n;
    }

    public int getN() {
        return n;
    }
}

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

 for (int i = 0; i< 1000; i++)
        if (!sendMessage(new TestObject(i))){
            System.out.println("Can`t send message: session closed");
        }
        disconnect();

в классе Client.java, далее запускаете второго клиента уже с раскомментированым кодом в Client.java. Наблюдаем, что выдает логгер в стектрейс.

В общем, подключив фантазию можно получить что-то похожее на JMS (:.

PS. Все написанное основывается на примере с сайта MINA chat


Это сообщение отредактировал(а) goodday1941 - 14.11.2007, 18:17


--------------------
SCJP 6
PM MAIL ICQ Skype GTalk   Вверх
Platon
Дата 15.11.2007, 12:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1801
Регистрация: 25.4.2006

Репутация: 3
Всего: 40



Честно сказать не понял к чему это?
PM MAIL ICQ   Вверх
goodday1941
Дата 15.11.2007, 18:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 709
Регистрация: 16.7.2006
Где: Украина, Киев

Репутация: нет
Всего: 9



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


--------------------
SCJP 6
PM MAIL ICQ Skype GTalk   Вверх
Platon
Дата 16.11.2007, 13:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1801
Регистрация: 25.4.2006

Репутация: 3
Всего: 40



Кстати, раньше я не задавался этим вопросом, но после того, как моя статья - теперь достояние общественности, почему она лежит не в факах "сети", а в "общих вопросах"?
PM MAIL ICQ   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Работа с сетью | Следующая тема »


 




[ Время генерации скрипта: 0.1230 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.