Android TCP Socket InputStrem Intermittent Read或太慢

时间:2017-02-22 13:59:59

标签: android sockets tcp inputstream iot

我需要在物联网设备(自定义)和Android应用之间实现TCP通信。 对于Wifi设备,我们有一个服务器 套接字,而在Android中我有一个AsyncTask作为客户端套接字。设备和智能手机都连接到同一网络。

以下是初始化/套接字读取和套接字写入的Android 客户端 套接字代码:

变量

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;
}

套接字初始化

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();
}

从Socket读取

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 init

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;
}

应用程序非常简单,android应用程序向IoT设备发送命令(通过sendDataToNetwork方法),后者发回“ACK”命令字符串。

问题

问题在于,虽然IoT设备始终接收命令,但智能手机很少会收到ACK。有时我得到类似“ACKACKACKACK”的东西。通过调试IoT设备,我确信它成功发送回ACK,因此问题在于InputStream read()方法,它不会立即检索字符串。

有没有办法立即清空InputStream缓冲区,这样每次发送命令时我都会从IoT设备返回一个“ACK”字符串?

更新

我已经更新了套接字配置,因此没有更多的缓冲区限制,我用readFully替换了read()方法。它大大改善了,但仍然犯了一些错误。对于2-3次中的一次没有收到ack,我在下一回合得到2 ack。这可能是物联网设备的计算限制吗?或者是否还有更好的方法?

1 个答案:

答案 0 :(得分:1)

  

问题在于InputStream read()方法,它不会立即清空缓冲区。

我不知道什么'清空缓冲区'这意味着,但DataInputStream.readFully()被指定为一旦传输了一个字节就返回。

  

有没有办法立即清空InputStream缓冲区,以便我得到一个" ACK"每次发送命令时从IoT设备返回字符串?

实际问题是您一次只能读取更多而不是一个ACK。还有其他人。

  1. 如果您尝试读取三个字节,则应使用available(),其字节数组为三个字节。
  2. 这也将消除对以下数组副本的需要。
  3. 除了增加套接字缓冲区大小之外,不应该使用它们。 20和700都是非常小的值,并且不是所使用的实际值,因为平台可以调整所提供的值。你声称这种改进的东西是不可信的。
  4. {{1}}为零时,不应旋转循环。这实际上是浪费时间。您的评论说您在以下阅读电话中被阻止。虽然你应该这样,但你不是。你在这里旋转。删除它。