2017-02-22 65 views
0

Мне нужно реализовать TCP-связь между устройством IoT (обычным) и Android-приложением. Для устройства Wi-Fi у нас есть СерверSocket, в то время как в Android у меня есть AsyncTask в качестве клиентского сокета. И устройство, и смартфон подключены к одной сети.Android TCP Socket InputStrem Intermittent Read or too Slow

Вот Android Clientгнездо код для инициализации/гнездовых чтения и раструба записи:

Переменные:

static public Socket nsocket; //Network Socket 
static public DataInputStream nis; //Network Input Stream 
static private OutputStream nos; //Network Output Stream 

метод AsyncTask doInBackgroud:

@Override 
protected Boolean doInBackground(Void... params) { //This runs on a different thread 
    boolean result = false; 

    try { 
     //Init/Create Socket 
     SocketInit(IP, PORT); 

     // Socket Manager 
     SocketUpdate(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
     Log.i("AsyncTask", "doInBackground: IOException"); 
     clearCmdInStack(); 
     MainActivity.SocketDisconnectAndNetworkTaskRestart(); 
     result = true; 
    } catch (Exception e) { 
     e.printStackTrace(); 
     Log.i("AsyncTask", "doInBackground: Exception"); 
     result = true; 
    } finally { 
     try { 
      SocketDisconnect(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     Log.i("AsyncTask", "doInBackground: Finished"); 
    } 
    return result; 
} 

гнездо Initializzation:

public void SocketInit(String ip, int port) throws IOException { 
    InetAddress addr = InetAddress.getByName(ip); 
    SocketAddress sockaddr = new InetSocketAddress(addr, port); 
    nsocket = new Socket(); 
    nsocket.setReuseAddress(false);  
    nsocket.setTcpNoDelay(true);   
    nsocket.setKeepAlive(true);   
    nsocket.setSoTimeout(0); 
    nsocket.connect(sockaddr, 0);  
    StartInputStream(); 
    StartOutputStream(); 
} 

Чтение из сокета:

private void SocketUpdate() throws IOException, ClassNotFoundException { 
    int read = 0; 
    // If connected Start read 
    if (socketSingleton.isSocketConnected()) { 
     // Print "Connected!" to UI 
     setPublishType(Publish.CONNECTED); 
     publishProgress(); 

     if(mConnectingProgressDialog != null) 
      mConnectingProgressDialog.dismiss(); //End Connecting Progress Dialog Bar 

     //Set Communications Up 
     setCommunicationsUp(true); 

     Log.i("AsyncTask", "doInBackground: Socket created, streams assigned"); 
     Log.i("AsyncTask", "doInBackground: Waiting for inital data..."); 
     byte[] buffer = new byte[3]; 

     do{ 
      nis.readFully(buffer, 0, 3); 
      setPublishType(Publish.READ); 

      publishProgress(buffer); 
     }while(!isCancelled()); 
     SocketDisconnect(); 
    } 
} 

Streams инициализации:

public void StartInputStream() throws IOException{ 
    nis = new DataInputStream(nsocket.getInputStream()); 
} 

public void StartOutputStream() throws IOException{ 
    nos = nsocket.getOutputStream(); 
} 

методов чтения и записи:

public int Read(byte[] b, int off, int len) throws IOException{ 
    return nis.read(b, off, len); //This is blocking 
} 

public void Write(byte b[]) throws IOException { 
    nos.write(b); 
    nos.flush(); 
} 

public boolean sendDataToNetwork(final String cmd) 
{ 
    if (isSocketConnected()) 
    { 
     Log.i("AsyncTask", "SendDataToNetwork: Writing message to socket"); 
     new Thread(new Runnable() 
     { 
      public void run() 
      { 
       try 
       { 
        Write(cmd.getBytes()); 
       } 
       catch (Exception e) 
       { 
        e.printStackTrace(); 
        Log.i("AsyncTask", "SendDataToNetwork: Message send failed. Caught an exception"); 
       } 
      } 
     }).start(); 

     return true; 
    } 

    Log.i("AsyncTask", "SendDataToNetwork: Cannot send message. Socket is closed"); 
    return false; 
} 

Приложение очень простое, андроид приложение посылает команду (с помощью метода sendDataToNetwork) к устройству IoT и последний посылает обратно «ACK» Командная строка.


Проблема

Проблема заключается в том, что в то время как устройство IoT всегда принимает команду, смартфон редко получает ACK обратно. Иногда я получаю что-то вроде «ACKACKACKACK». Отлаживая устройство IoT, я уверен, что он успешно отправляет ACK, поэтому проблема заключается в методе readStream read(), который не сразу извлекает строку.

Можно ли сразу удалить буфер InputStream, чтобы я получал строку «ACK» с устройства IoT каждый раз, когда отправляю команду?


Update

Я обновил сокет конфигурацию, так что нет больше ограничений буфера и я заменил чтение() метода с readFully. Это значительно улучшилось, но все-таки допустило некоторые ошибки. Для istance один из 2-3 раз не получает ack, и я получаю 2 ack на следующий ход. Возможно, это вычислительный предел устройства IoT? Или есть еще преимущество для лучшего подхода?

+1

'nsocket.setReceiveBufferSize (20); '. Почему вы возитесь с размерами буфера? – greenapps

+1

Если вы ожидаете только строку из трех символов, таких как ACK, тогда используйте функцию чтения размером 3 в вашей функции чтения. – greenapps

+0

@greenapps, установив nsocket.setReceiveBufferSize (20); это немного улучшило показатель успеха. Я попытался вызвать read() с длиной 3, но ничего не улучшилось. – Starivore

ответ

1

проблема заключается в методе readStream read(), который сразу не освобождает буфер.

Я не знаю, что означает «пустой буфер», но InputStream.read() указан для возврата, как только был передан хотя бы один байт.

Есть ли способ сбросить буфер InputStream сразу, так что я получаю строку «ACK» с устройства IoT каждый раз, когда я отправляю команду?

Реальная проблема заключается в том, что вы могли бы больше читать чем один ACK в то время. И есть другие.

  1. Если вы пытаетесь прочитать ровно три байта, вы должны использовать DataInputStream.readFully() с байтовым массивом из трех байтов.
  2. Это также избавит вас от необходимости в следующей копии массива.
  3. Вам не следует путаться с размерами буфера сокета, кроме как увеличить их. 20 и 700 являются смехотворно малыми значениями и не будут действительными значениями, поскольку платформа может отрегулировать поставляемое значение. Ваше утверждение о том, что это улучшило ситуацию, не заслуживает доверия.
  4. Вы не должны вращаться, а available() равно нулю. Это буквально пустая трата времени. В вашем комментарии говорится, что вы заблокированы при следующем вызове на чтение. Вы не, хотя и должны быть. Вы вращаетесь здесь. Удали это.
+0

спасибо! Я обновил код, и он значительно улучшился, но все же допустил некоторые ошибки. Для istance один из 2-3 раз, no ack получен, и я получаю 2 ack в следующий ход. Возможно, это вычислительный предел устройства IoT? Или есть еще преимущество для лучшего подхода? – Starivore

+0

Вы не можете получить ACK или два ACK, если вы использовали все предложения в этом ответе. Вы можете получить только три байта за раз, если вы читаете только три байта за раз, и вы не можете получить меньше, если используете 'readFully()'. – EJP

+0

Код, который я использую сейчас, указан на моем посте, пожалуйста, скажите мне, есть ли что-то неправильно. И да, я не могу получить меньше 3 байтов с readFully(), но иногда я ничего не получаю. Когда я ничего не получаю, следующий отправленный cmd получает два ACK один за другим (readFully получает два «ACK», а не «ACKACK»). – Starivore