Android TCP 套接字输入流间歇性读取或太慢

Android TCP Socket InputStrem Intermittent Read or too Slow

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

这里是 Android Client Socket initialization/socket-read 和 socket-write 的代码:

变量:

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

从套接字读取:

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

流初始化:

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 应用程序向物联网设备发送命令(通过 sendDataToNetwork 方法),后者发回 "ACK" 命令字符串。


问题

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

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


更新

我更新了套接字配置,不再有缓冲区限制,并且我用 readFully 替换了 read() 方法。它有了很大的改进,但仍然会犯一些错误。例如,有 2-3 次中有 1 次没有收到 ack,我在下一回合收到 2 次 ack。这可能是 IoT 设备的计算限制吗?还是还有改进方法的余地?

the problem lies in the InputStream read() method which doesn't empty the buffer right away.

我不知道这里的'empty the buffer'是什么意思,但是只要传输了一个字节,InputStream.read()就被指定为return。

Is there a way to empty the InputStream buffer right away, so that i get an "ACK" string back from the IoT device every time i send a command?

实际问题是您可能一次读取 个 ACK​​。还有其他的。

  1. 如果您正试图读取三个字节,您应该使用 DataInputStream.readFully() 和一个包含三个字节的字节数组。
  2. 这也将摆脱对以下数组副本的需要。
  3. 你不应该弄乱套接字缓冲区大小,除非增加它们。 20 和 700 都是非常小的值,不会是实际使用的值,因为平台可以调整提供的值。你声称这种改进的东西是不可信的。
  4. available() 为零时,您不应该自旋循环。这实际上是在浪费时间。您的评论说您在以下阅读电话中被阻止。你不是,尽管你应该是。你在这里旋转。删除这个。