Модераторы: javastic
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> работа с Socket, socket, tcp/ip 
:(
    Опции темы
Dapo
Дата 30.7.2012, 09:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 417
Регистрация: 18.4.2002

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



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

public class ClientSocket {

    private Handler handler = new Handler();

    private static String mHostIpAddress;
    private static int mPort;

    private Thread mServiceThread;
    private Runnable mServiceRunnable;

    private Socket mSocket;

    private boolean isConnected = false;
    private int mSocketStatus = 0;// 0-disconnected, 1- connection, 2-
                                    // io_pending, 3 - write out, 4 - read in
    private String mOutString = "";

    public ClientSocket(String pHostIp, int pPort) {
        mHostIpAddress = pHostIp;
        mPort = pPort;

    }

    public void connect() {
        if (mSocketStatus != 0) {
            return;
        }
        mServiceRunnable = new Runnable() {
            @Override
            public void run() {
                mSocketStatus = 1;
                // connect
                try {
                    mSocket = new Socket(mHostIpAddress, mPort);
                    isConnected = true;
                    onConnect();
                    mSocketStatus = 2;
                    char[] inBuff = new char[1024];
                    while (isConnected) {
                        if (!mOutString.equals("")) {
                            PrintWriter out = new PrintWriter(
                                    new BufferedWriter(new OutputStreamWriter(
                                            mSocket.getOutputStream())), true);
                            mSocketStatus = 3;
                            out.println(mOutString);
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    onSendData();
                                }
                            });
                            
                            mOutString = "";
                        }
                        BufferedReader in = new BufferedReader(
                                new InputStreamReader(mSocket.getInputStream()));
                        String line = null;
                        if (in.ready()) {                        
                            int length = in.read(inBuff);
                            while ((length) != -1) {
                                for (int i = 0; i < length; i++) {
                                    line = line + inBuff[i];
                                }
                                length = -1;
                                if (in.ready()){
                                    length = in.read(inBuff);
                                }
                            }    
                            mSocketStatus = 4;
                            final String inStr = line;
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    onReadData(inStr);
                                }
                            });
                        }
                    }
                    mSocket.close();
                    onDisconnect();
                    mSocketStatus = 0;

                } catch (UnknownHostException e) {
                    e.printStackTrace();
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            onErrorConnection();
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            onErrorConnection();
                        }
                    });
                }
            }
        };
        mServiceThread = new Thread(mServiceRunnable);
        mServiceThread.start();
    }

    public void disconnect() {
        isConnected = false;
    }

    public void send(String pStr) {
        mOutString = pStr;

    }

    public void onConnect() {
        Log.v("game", "Connection success");
    }

    public void onDisconnect() {
        Log.v("game", "Disconnect ");
    }

    public void onErrorConnection() {
        Log.v("game", "Connection error");
    }

    public void onReadData(String pDataString) {
        Log.v("game", "Read " + pDataString);
    }

    public void onSendData() {
        Log.v("game", "Sended ");
    }



Использование:
Код


//создание и пример привязки к TextView
cs = new ClientSocket(serverIpAddress, 9203){
         @Override
         public void onReadData(String pStr){
             textView.setText(textView.getText()+pStr);
         }
        };
......

//активация сокета
cs.connect();
......

//посылка сообщения
cs.send("trololo"); //cs.send(outText.getText().toString());
......

//разрыв соединения
cs.disconnect();
.....



Чтение из сокета будет происходить автоматически, соединение, посылка, разрыв вызываются из основного потока приложения. На onReadData легко можно привязать свой протокол (чего собственно и добиваюсь).
Чувствую, что от недопонимания потоковой схемы андроида, я тут наворочал быдлокод, хотя оно работает и не глюкает. Может есть уже готовые устойчиво работающие обертки и я изобретаю велосипед? 
Помогите отшлифовать - это ведь нужная вещь. Спасибо!
PM MAIL   Вверх
Frees
Дата 30.7.2012, 11:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Dapo, какие статьи по Runnable, Thread  и Handler читал, поделись ссылками.


Цитата(Dapo @  30.7.2012,  12:24 Найти цитируемый пост)
  if (!mOutString.equals("")) {

а это будет потоко безопасно? mOutString заполняешь в одном потоке а читаешь в потоке mServiceThread. 


--------------------
Кольцов Виктор Владимирович
PM MAIL ICQ   Вверх
Dapo
Дата 30.7.2012, 13:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 417
Регистрация: 18.4.2002

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



Да читал что нашел в интернетах. Если честно, ничего такого не читал, чтобы тема была раскрыта целиком и полностью. Это с миру по нитке + собственные изыскания. Так например, я нигде не нашел метода isReady(), а так же способа вычитывать через буфер как это реализовано у меня. Везде бодро читают сразу стрингом, и без всякой проверки на готовность данных в буфере. Но это не работает (получаем запор на несколько минут, а потом варнинг). Frees, твое замечание я думаю верное, точно также меня смущает и обращение к переменной isConnected. Этот момент хотелось чтобы кто-нибудь объяснил. По hendler-у вроде не так все сложно. Имхо, не важно где он расположен, главное что в моем случае класс создается в основном потоке и хендлер отрабатывает в основном. Т.е. если убрать хендлер.пост и сразу вызвать например onReadData, а в onReadData обратиться к какой-нибудь вьюшке, то получим MainThreadException (так кажется, щас негде посмотреть)
PM MAIL   Вверх
Dapo
Дата 30.7.2012, 17:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 417
Регистрация: 18.4.2002

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



вообще хитрый класс получается. сам он создается в основном потоке. его методы тоже смотрят в основной поток через тот самы handler, но внутри класса создается паралельный поток переменные внутри которого должны принимать значения из основного потока. Думаю это и есть ключ к пониманию, но мне его пока не хватает smile Повторюсь, что использование handler-а в приведенном выше классе, это тоже мое имхо. Даже более того, скажу: нужны методы синхронизированного чтения/записи в mOutString и isConnected, тогда корабль должен стать непотопляем. тожеж имхо.
PM MAIL   Вверх
Frees
Дата 30.7.2012, 18:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Меня еще смущает что send - void то есть если будут ошибки при отправке об этом сразу и не узнаешь, в одном месте отправил а ошибку в другом видишь...имхо неудобно, но зависит от задачи


--------------------
Кольцов Виктор Владимирович
PM MAIL ICQ   Вверх
Dapo
Дата 30.7.2012, 19:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 417
Регистрация: 18.4.2002

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



...стер за ненадобностью... сплошные эмоции  smile

Это сообщение отредактировал(а) Dapo - 31.7.2012, 08:26
PM MAIL   Вверх
Dapo
Дата 31.7.2012, 05:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 417
Регистрация: 18.4.2002

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



Вот тут кое-что нашел по обращению к "неуловимым" переменным: http://www.sql.ru/forum/actualthread.aspx?tid=909618 и на основе поста пересобрал класс.
Версия 2.
Код

public class ClientSocket {

    private Handler handler = new Handler();

    private static String mHostIpAddress;
    private static int mPort;

    private Thread mServiceThread;
    private Runnable mServiceRunnable;

    private Socket mSocket;

    private volatile boolean isConnected = false;
    private int mSocketStatus = 0;// 0-disconnected, 1- connection, 2-
                                    // io_pending, 3 - write out, 4 - read in, -1 - unknown state
    private volatile String mOutString = "";
    
    private volatile boolean mIsNewData;

    public ClientSocket(String pHostIp, int pPort) {
        mHostIpAddress = pHostIp;
        mPort = pPort;

    }

    public void connect(final int pTimeoutMillisecs) {
        if (mSocketStatus != 0) {
            return;
        }
        mServiceRunnable = new Runnable() {
            @Override
            public void run() {
                mSocketStatus = 1; //соединяемся
                // connect
                try {
                    mSocket = new Socket();
                    mSocket.connect(new InetSocketAddress(mHostIpAddress,mPort), pTimeoutMillisecs);
                    setConnected(true);
                    onConnect();
                    mSocketStatus = 2;//уходим в прослушку сокета
                    char[] inBuff = new char[1024];
                    while (isConnected()) {
                        String tmpSendData = getSendData(); 
                        if (!tmpSendData.equals("")) {
                            mSocketStatus = 3;//пишем в сокет
                            PrintWriter out = new PrintWriter(
                                    new BufferedWriter(new OutputStreamWriter(
                                            mSocket.getOutputStream())), true);
                            
                            out.println(tmpSendData);
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    onSendData();
                                }
                            });
                            
                            clearSendData();    
                        }
                        mSocketStatus = 4;//читаем из сокета
                        BufferedReader in = new BufferedReader(
                                new InputStreamReader(mSocket.getInputStream()));
                        String line = "";
                        if (in.ready()) {                        
                            int length = in.read(inBuff);
                            while ((length) != -1) {
                                for (int i = 0; i < length; i++) {
                                    line = line + inBuff[i];
                                }
                                length = -1;
                                if (in.ready()){
                                    length = in.read(inBuff);
                                }
                            }    
                            final String inStr = line;
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    onReadData(inStr);
                                }
                            });
                        }
                    }
                    mSocketStatus = 0;//отсоединяемся
                    mSocket.close();
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            onDisconnect();
                        }
                    });
                    

                } catch (UnknownHostException e) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            mSocketStatus = -1;
                            onSocketError(mSocketStatus);
                            mSocketStatus = 0;
                        }
                    });
                    e.printStackTrace();
                } catch (Exception e) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            onSocketError(mSocketStatus);
                            mSocketStatus = 0;
                        }
                    });
                    e.printStackTrace();
                }
            }
        };
        mServiceThread = new Thread(mServiceRunnable);
        mServiceThread.start();
    }
    
    //эти методы вызываются из UI-потока
    public void disconnect() {
        setConnected(false);
    }


    public void onConnect() {
        Log.v("game", "Connection success");
    }

    public void onDisconnect() {
        Log.v("game", "Disconnect ");
        setConnected(false);
    }

    public void onSocketError(int pSocketStatus) {
        Log.v("game", "Connection error");
    }

    public void onReadData(String pDataString) {
        Log.v("game", "Read " + pDataString);
    }

    public void onSendData() {
        Log.v("game", "Sended ");
    }

    //синхронизированные методы чтения/записи
    protected synchronized boolean isConnected(){
        return this.isConnected;
    }
    
    private synchronized boolean setConnected(boolean pIsConnected){
        this.isConnected = pIsConnected;
        return this.isConnected;
    }
    //этот метод вызывается из UI-потока
    protected synchronized void send(String pOutString){
        mOutString = pOutString;
        mIsNewData = true;
    }
    
    private synchronized String getSendData(){
        mIsNewData = false;
        return mOutString;
    }
    
    private synchronized void clearSendData(){
        if (!mIsNewData)
            mOutString = "";
    }
}


Использование такое же как и в первом посте. Необходимость что-то отправлять идет в проверке string.equals(""). Если во время отправки, основной поток опять сделал send(...), то мы не можем просто очистить исходящую строку в clearSendData потому, что команда потеряется, поэтому я добавил флаг mIsNewData для проверки был ли новый send() или нет. Возможно стоит поставить цикл while (!isNewData()) doSocketWrite;, но я думаю это вопрос конкретной задачи. Что скажет коммунити? smile

PS Чтобы не плодить портянки я здесь еще добавил сброс mSocketStatus в 0 при возникновения исключения. Плюс добавил управляемый таймаут на подключение, думаю это нужная штука. Тогда запуск сокета будет например такой: socket.connect(5000);// даем 5 секунд на установку соединения, если возник таймаут то ловим его в onSocketError по mSocketStatus = 1;

Это сообщение отредактировал(а) Dapo - 31.7.2012, 07:35
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Android | Следующая тема »


 




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


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

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