Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java ME (J2ME) > Реализация сетевого соединения http?


Автор: Kalisnik 3.3.2012, 18:08
Добрался до сетевого соединения. Был бы благодарен за правильный рабочий пример передачи и приема данных по протоколу http с подробными комментариями, что бы по ним можно было понять суть и механизм процесса. Спасибо!

Автор: oxigen 5.3.2012, 11:47
Читаем по http картинку и показываем ее
Код

  protected void startApp() throws MIDletStateChangeException {
            HttpConnection conn = null;
            DataInputStream is = null;
                try {
                    conn = (HttpConnection) Connector.open("http://av.li.ru/227/2374227_8677578.jpg", Connector.READ, false); //создали connection.
                                                                        // Connector.open - умный. Создаст тип в зависимости от преффикса адреса. Здесь HTTP
                    conn.setRequestMethod( HttpConnection.GET);  // Используем Get запрос

                    int rc = conn.getResponseCode();                       //!!! Именно здесь происходит отсылка запроса и получение ответа

                    if (rc != HttpConnection.HTTP_OK) {                   // Если от сервера пришел код ответа != 200, то значит что-то пошло не так. 
                        throw new Exception("ResponseCode = " + rc);   // Нужно конечно как-то более разумно обработать ошибку.
                    }

                    is = conn.openDataInputStream();                    //ответ получен. И находится в connection. Достаем его 
                    byte []b = new byte[(int) conn.getLength()];    //в виде массив байт
                    is.readFully(b);

                    Image img = Image.createImage(b, 0, b.length);    // Все. Делаем с получеными байтами что хотим. Здесь хотим картинку.
                    Form f = new Form("test");                                      // Был бы к примеру текст - читали бы в строку. [I]new String(b)[/I]
                    f.append(img);
                    Display.getDisplay(this).setCurrent(f);

                } catch (Exception e) {

                } finally {
                   if(conn != null)
                         conn.close();
                    if(is != null)
                         is.close();
                }

    }


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

Автор: Kalisnik 6.3.2012, 22:47
1. HttpConnection - это интерфейс или класс? В api написанно что интерфейс, а используется он как класс. Не поняно. Разве переменная(данные) может иметь тип интерфейса?

2. В чем различия InputStream и DataInputStream? В каких случаях использовать тот или этот?

3. Касательно Connector.open, что меняет второй параметр Connector.READ? Соединение только для чтения? Ставил Connector.WRITE - ни чего не изменилось. Почему не ставить сразу универсально Connector.READ_WRITE? Что делает 3-й параметр? Что-то с таймаутом связанное вроде - выбрасывать ли исключение по таймауту?

Автор: oxigen 7.3.2012, 11:51
1. HttpConnection - это интерфейс или класс? В api написанно что интерфейс, а используется он как класс. Не поняно. Разве переменная(данные) может иметь тип интерфейса?
Конечно может иметь тип интерфейса. Интерфейсы для того и придуманы
Код

class A extends Cl1 implements I1, I2, I3 {}

//Использование
A a = new A();
Cl1 с = new A();
I1 i1 = new A();
I2 i2 = new A();

Все это корректно и все эти переменные физически будут ссылаться на объект класса A
Разница в том, что для a мы сможем вызавать все методы, а для i1 только те, которые описаны в интерфейсе I1. Но это все равно будут одни и те же методы.

conn = (HttpConnection) Connector.open(...);
Эта строка означает, что Connector возвращает какой-то объект. Нам совершенно не нужно знать, какого он конкретно класса. Все что мы про него знаем - в нем есть методы, описаные в интерфейсе HttpConnection. Этого достаточно.


2. В чем различия InputStream и DataInputStream? В каких случаях использовать тот или этот?
Использовал DataInputStream ради его метода readFully. Проще.
Можно использовать и InputStream во всяком случае в документации именно для него пример.
http://docs.oracle.com/javame/config/cldc/ref-impl/midp2.0/jsr118/index.html

3. Касательно Connector.open
Действительно для Http соединения достаточно было только одного параметра. Издержки бездумного копипаста :(
Connector.open("http://av.li.ru/227/2374227_8677578.jpg");
Третий параметр задает поведение при таймауте соединения. Если true - будет выброшен Exception

Автор: Kalisnik 7.3.2012, 12:05
oxigen, по поводу интерфейсов. Видимо я не доканца понимаю что это такое. Можете объяснить своими словами, с основными приемами их использования?

З.Ы. Код выше для меня оказался совершенно новым. Такого еще не видел.

Автор: oxigen 7.3.2012, 15:43
Интерфейсы - это множественное наследование в java

Зачем нужно множественное наследование?
Вот в вашем предыдущем примере был слушатель нажатий на меню
Код

public class Stra extends Form implements CommandListener {
...
setCommandListener(this);
...
public void commandAction(Command c, Displayable d) {}
}

Как все это работает? Вы листенером делали и мидлет и форму и просто Object. И оно  работало.
Как сделать подобное?

Внутри класса Displayable должен быть примерно такой код
Код

private CommandListener listener

public void setCommandListener(CommandListener l) {
    listener = l
}

//если нажали на пункт меню, то выполняется
  listener.commandAction(command, this);


То есть слушателем у нас может кто угодно. Абсолютно любой класс. Нашему Displayable безразлично, кто именно его слушает.
Единственное, что он требует, чтоб у слушателя был метод commandAction(Command c, Displayable d)
И для этого требования и сделан интерфейс CommandListener. 
В нем описан только один метод commandAction(Command c, Displayable d)

Метод setCommandListener(CommandListener l) принимает любой класс, который implements CommandListener.
А раз он implements CommandListener, то значит ему можно сообщить о нажатии на меню через  listener.commandAction(command, this);

Не знаю, может сумбурно написал. Если непонятно, то конкретная задача - как изменить ваш Stra, чтоб любой класс мог узнавать о том, какая кнопка была нажата. Для ее решения понадобится создать интерфейс и добавить код как выше.

Автор: Kalisnik 10.3.2012, 17:49
1. Да. Оказывается понимал я все правильно. Только практического опыта не хватает и момент с переменными типа-интерфейс я как-то упустил, от чего и возникло недоумение. По сути интерфейс это некий "протокол", правила взаимосвязи самостоятельных звеньев системы. Если какой-либо класс использует интерфейс, то это означает что он "кровью" подписывается реализовать все методы этого интерфеса и выполнять их по первому требованию в любое время дня и ночи. smile Скажем, речь на которой общаются все участники сего форума также является интерфейсом (тип: "Русский язык"): слова - абстрактные методы, а их смысловое содержание - конкретная реализация в каждом индивиде. smile) Касательно переменных типа интерфейс. Как Вы и говорили, они просто ссылаются на методы какого-либо класса, который использует интерфейс такого же типа. По сути, тип этих переменных есть не "интерфейс", а "Класс", который использует этот интефейс, но с фильтром методов. Вроде так получается. )
    Далее Вы говорите о множественном наследовании - видимо как о практической ценности интерфейсов. "Не знаю, может сумбурно написал. Если непонятно, то конкретная задача - как изменить ваш Stra, чтоб любой класс мог узнавать о том, какая кнопка была нажата. Для ее решения понадобится создать интерфейс и добавить код как выше." - вообще-то я действительно недопонял. )) Может рабочий примерчик, что бы я мог его откомпилировать, запустить и посмотреть как все работает?

2. И еще в догонку вопросик. http работает по принципу "отправил запрос - получил ответ". А если клиентскому приложению нужно отслеживать изменения на сервере? Значит нужно систематично отправлять запросы и проверять изменения. А если клиентов 1000 или 10 000 и все разом отправят запрос? Так и сервер положить недолго. )) Значит либо делать сложные костыли вроде распределенных во времени запросов всей сети клиентов, либо... искать альтернативы http. Есть ли эти самые альтернативы? Т.е. можно ли сделать так, что бы приложение слушало сервер без запроса и как только сервер отсылает конкретному клиенту данные, клиент соответсвенно их обрабатывает?

Автор: oxigen 12.3.2012, 11:52
1. Собственно у слова интерфейс есть два значения. 
Первое - интерфейс класса. Это все публичные методы класса. Через которые другие классы могут к нему обращаться.
Второе  - интерфейс, который ключевое слово interface. Это способ описать некоторое подмножество первого.

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

class Kalisnik implemens Voiceable, Writable
И у вас есть public методы для обработки письменных и устных слов. Это Ваш интерфейс, через который окружающие могут обратиться к вам.

Однако для форума Вы
Writable kalisnik;
То есть я могу обращаться к Вам только через методы, которые описаны в interface Writable (письменная речь)

Каой-то небольшой пример, где будет видна необходимость интерфейсов, не так легко сочинить.
Почитайте лучше какую-то хорошую книгу именно по Java (не по мобильной). Философия JAVA Брюса Эккеля к примеру. Он понятнее, чем я объясняет нюансы наследования и необходимость интерфейсов.

2. HTTP действительно работает так, что одна сторона посылает запросы, а вторая - отсылает на них ответы.
Если надо, чтоб процесс был симметричным, то больше подходит соединение через сокет
conn = (SocketConnection) Connector.open("socket://your_url:port"); 
Пример в доке http://docs.oracle.com/javame/config/cldc/ref-impl/midp2.0/jsr118/index.html
Устанавливаете соединение через сокет и все.
И тогда Вы просто кладете то, что нужно отправить на сервер в OutputStream, А то, что сервер отправил - появляется в InputStream.


Автор: Kalisnik 12.3.2012, 15:44
oxigen, спасибо smile Просто я не понял Ваш конкретный пример с CommandListener - про него говорил. Не понял как Вы хотели изменить Stra. А так в общем-то все доступно и понятно. )

Автор: oxigen 12.3.2012, 17:44
Я имел ввиду что-то вроде
Код

public class Stra extends Form implements CommandListener{
    private Vector listeners = new Vector();

    public void addListener(StraListener listener) {
       listeners.addElement(listener);
   }

    public void commandAction(Command c, Displayable d) {
         for(int i = 0; i < listener.size(); i++) {
                ((StraListener)listeners.elementAt(i)).straAction(geTitle(), c.getLabel());
        }

        if(c == ok){
            Midlet.midl.getDisplay().setCurrent(getFormByIndex(choice.getSelectedIndex(), d));
        }else if(c == back){
            back();
        }else if(c == exit){
            Midlet.midl.destroyApp(true);
        }
        
    }
    
}

interface StraListener {
   public straAction(String title, String Action);
}


И теперь если понадобится прикрутить любой класс, который должен как-то реагировать на переключения форм, не надо будет каждый раз менять Stra.
Захотели - прикрутили класс, пишущий в консоль.

Код

public class Logger implements StraListener {
    Logger() {
     stra1.addListener(this);
     stra2.addListener(this);
    }

    public straAction(String title, String action) {
        System.out.println(title + "menu item pressed " + action);
    }

}

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

Как-то так. Просто пример слегка надуманый.

Автор: Kalisnik 12.3.2012, 18:43
oxigen,  вот теперь понял. )) Да, после первого примера с переменными типа-интерфейс, я уже осознал перспективы использования этого типа (т.е. интерфейсов) в параметрах метода. Мощная штука!

З.Ы. Ошибочку увидел... на случай если кому-то тоже будет интересен этот пример:

Код

for(int i = 0; i < listeners.size(); i++) {
                ((StraListener)listeners[i]).straAction(geTitle(), c.getLabel());
        }

Автор: oxigen 13.3.2012, 11:04
Исправил. Но только  ((StraListener)listeners.elementAt(i)) Там же вектор а не массив.

Автор: Kalisnik 13.3.2012, 15:27
Ясно. Здесь (http://www.javaportal.ru/java/class/Vector.html) сбили с толку. Вектором пока еще не пользовался. Получается это та же коллекция, с автоматическим расширением памяти? Вообще много заметил в интернете умельцев, которые пишут статьи и сами не знают про что пишут. По ссылке выше Vector - это массив. Другие пишут что HttpConnection - это класс, хотя на самом деле это интерфейс... видимо надо учить английский и читать официальную документацию. Правильных переводов официальной документации ведь нет?

з.ы.  "s" - забыли )

Автор: oxigen 13.3.2012, 17:06
Внутри вектора действительно спрятан массив. Но благодаря инкапсуляции нам это неважно.

Насчет коллекций учитывайте одну вещь. Спецификация J2ME очень старая. И в J2ME нет интерфейса Collection.
После появления в JAVA этого интерфейса, Vector и Hashtable были сильно изменены, стали наследниками Collection и в них соответствующие методы. 
http://www.javaportal.ru/java/class/Vector.html - описание этого нового вектора.

Но в векторе из J2ME этих новых методов нет.
http://docs.oracle.com/javame/config/cldc/ref-impl/midp2.0/jsr118/index.html - J2ME вектор 


Фанатские переводы документации периодически пытаются делать. Может найдете что-то.

Автор: Kalisnik 22.3.2012, 19:55
Цитата(oxigen @  12.3.2012,  11:52 Найти цитируемый пост)
Устанавливаете соединение через сокет и все.И тогда Вы просто кладете то, что нужно отправить на сервер в OutputStream, А то, что сервер отправил - появляется в InputStream.


Это мне, получается, в телефонном приложении два сокета создавать надо? Один клиентский, другой серверный - что будет слушать "во вне"? Тогда как интересно сервер (что "во вне") будет отсылать телефону запросы, если ip-адрес телефона заранее не известен? Получается, приложение телефона должно сделать запрос на сервер, где определится его ip, и тогда сервер сможет подключиться к ServerSocketConnection телефона? Так?

Автор: oxigen 23.3.2012, 13:07
На десктопе открываете серверный сокет, на телефоне клиентский(IP адрес десктопа вы знаете)
И все.

Автор: Kalisnik 23.3.2012, 16:50
oxigen, спасибо smile

Автор: Kalisnik 29.3.2012, 13:23
Вот метод acceptAndOpen() является методом интерфеса StreamConnectionNotifier, только не понятно где этот метод переопределяется? Где, собственно, его реализация? 

Код

private ServerSocketConnection ssc;
private StreamConnection sc;

ssc = (ServerSocketConnection)Connector.open("socket://:9002");
sc = ssc.acceptAndOpen();


З.Ы. А можно как-нибудь открыть родные библиотечки java и посмотреть их реализацию?

Автор: oxigen 29.3.2012, 14:46
Если коротко - посмотреть нельзя.

Реализация в виртуальной машине JAVA (JVM).
Разные телефоны, разные JVM, разная реализация. 

Автор: Kalisnik 1.4.2012, 13:55
Цитата(oxigen @  29.3.2012,  14:46 Найти цитируемый пост)
Если коротко - посмотреть нельзя.


Можно. NB, в редакторе жмем Ctrl и щелкаем по нужному классу или методу. Откроется абстрактная конструкция класса, в котором методы будут без реализации. В этой абстрактной конструкции таким же образом через Ctrl щелкаем на любой класс или метод и открывается уже класс с реализацией и комментариями.

З.Ы. Также реализацию можно посмотреть в src.zip - там все исходники, кроме Native.

Автор: oxigen 2.4.2012, 14:12
Эм... В src.zip есть пакет javax.microedition.* ?

Автор: Kalisnik 2.4.2012, 18:26
oxigen, это да. Ну хоть посмотреть такие классы как String и т.п. )

Автор: oxigen 3.4.2012, 11:04
Просто говорили про ServerSocketConnection и Connector. А они как раз из microedition

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)