【问题标题】:Android TCP Socket InputStrem Intermittent Read or too SlowAndroid TCP Socket InputStrem 间歇性读取或太慢
【发布时间】:2017-07-12 15:01:36
【问题描述】:

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

这是用于初始化/socket-read 和 socket-write 的 Android Client Socket 代码:

变量

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”命令字符串。


问题

问题在于,虽然物联网设备总是收到命令,但智能手机很少收到 ACK。有时我会得到类似“ACKACKACKACK”的东西。通过调试物联网设备,我确信它成功发回了 ACK,所以问题在于 InputStream read() 方法没有立即检索到字符串。

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


更新

我已经更新了套接字配置,因此不再有缓冲区限制,并且我已经用 readFully 替换了 read() 方法。它大大改善了,但仍然犯了一些错误。例如,2-3 次中的一次没有收到 ack,下一轮我得到 2 个 ack。这可能是物联网设备的计算极限吗?还是有更好的方法的余地?

【问题讨论】:

  • nsocket.setReceiveBufferSize(20); 。你为什么要搞乱缓冲区大小?
  • 如果您只期望一个由三个字符组成的字符串(如 ACK),那么在您的读取函数中使用 3 的读取大小。
  • @greenapps 通过设置 nsocket.setReceiveBufferSize(20);它提高了一点成功率。我尝试使用长度为 3 的 read() 调用,但没有任何改善。
  • 让设备只发送更多数据。
  • @greenapps 我试过了,如果你发送更多的 ACK 字符串,它只会改进一点。但仍然不是解决方案,而且您也失去了 sincronism,因此您将不知道该 ACK 何时发送!

标签: android sockets tcp inputstream iot


【解决方案1】:

问题在于 InputStream read() 方法没有立即清空缓冲区。

我不知道这里的“清空缓冲区”是什么意思,但InputStream.read() 被指定为在传输一个字节后立即返回。

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

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

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

【讨论】:

  • 谢谢!我已经更新了代码,它有了很大的改进,但仍然犯了一些错误。例如,2-3 次中的一次,没有收到 ack,下一轮我得到 2 个 ack。这可能是物联网设备的计算极限吗?还是有更好的方法的余地?
  • 如果您已使用此答案中的所有建议,您不可能没有收到任何 ACK 或两个 ACK​​。如果你一次只读取三个字节,你一次只能得到三个字节,如果你使用readFully(),你不可能得到更少。
  • 我现在使用的代码在我的帖子中列出,如果有什么问题请告诉我。是的,我不能用 readFully() 得到少于 3 个字节,但有时我什么也得不到。当我一无所获时,下一个发送的 cmd 一个接一个地收到两个 ACK​​(readFully 得到两个“ACK”,而不是“ACKACK”)。
  • 你不能从 readFully() 得到任何东西,除非它抛出某种形式的 IOException。坦率地说,不清楚你到底在说什么。 readFully() 不返回计数,因此 while (read != -1) 条件是无效的。当EOFException 被抛出时,循环应该终止。 isConnected() 在对等方断开连接时不会神奇地变为假,因此测试也是徒劳的。我也不知道您为什么将线程用于简单的请求/ACK 协议。
  • 我对@9​​87654331@ 一无所知,我打算保持这种状态,但您应该在同一个线程中发送请求并读取 ACK。在旧请求得到确认之前,您不应发送新请求。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-02
  • 1970-01-01
  • 1970-01-01
  • 2014-11-01
  • 2018-09-24
  • 2011-01-28
相关资源
最近更新 更多