java - Android 应用程序中套接字 DataInputStream BufferedInputStream 的消息顺序错误

标签 java android multithreading sockets

我在接收从另一台设备发送的字节消息的不规则序列时遇到问题。

设置如下:我有一个 Android 应用程序(客户端)和带有以太网的实时系统(服务器),两者都通过路由器连接在 LAN 中,并与原始字节通信进行通信。

我从 Android 应用程序发送请求,这会导致服务器响应多条消息 - 第一条消息有 8 个字节,后面的消息有 27 个字节。我已经调试了服务器,我确信它发送的第一条消息是第 8 字节消息,然后是其他消息。

关于应用程序 - 我使用主 Activity 来处理通过套接字的数据传输,并使用附加线程来处理数据的接收。 当收到新数据时,线程通过处理程序将消息发送到主 Activity 。在这篇文章中称为解析接收到的数据的过程。

TbProtocolProcessor 是我用来处理自定义协议(protocol)的类。它可以为我创建一个字节数组作为特定功能的请求发送,并且它有一个状态机来处理来自服务器的预期响应。 InetHandler 是我仅用于处理连接的嵌套类。

我的问题是 - 为什么我的 Android 应用程序会返回第一条大小为 8 的消息,但内容类似于下一条消息?有趣的效果是,如果我只发送 8 字节消息,而不发送任何其他消息,它就会被正确接收并传递到我的应用程序。

这是代码:

public class MainActivity extends AppCompatActivity
{
private TbProtocolProcessor tbProtPrcs = null;
private InetHandler inetHandler = new InetHandler(this);
private static Handler msgHandler = new Handler();


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    tbProtPrcs = new TbProtocolProcessor(this);
}

// Implementation of InetControl interface
public void ConnectToIP(String strIP, int port)
{
    inetHandler.AttachToIP(strIP, port);
}
public void Disconnect()
{
    inetHandler.DetachFromIP();
}

public void GetFilesList()
{
    byte[] data = TbProtocolProcessor.buildFilesGetList();
    inetHandler.SendData(data, data.length);
    TbProtocolProcessor.setExpectedResult(
            TbProtocolProcessor.TB_STATE_WAIT_MUL_FILESLIST,
            data[1],
            1);
}

private class InetHandler
{
    protected static final int cTARGET_PORT_UNASSIGNED = 0xFFFF;
    protected String targetIP = null;
    protected int targetPort = cTARGET_PORT_UNASSIGNED;
    protected boolean isConnected = false;
    protected Socket socket = null;
    protected DataOutputStream sockStrmOut = null;
    protected DataInputStream sockStrmIn = null;
    protected Context context = null;

    public InetHandler(Context ctx) {
        if (ctx != null)
        {
            context = ctx;
        }
    }

    class ClientThread implements Runnable {
        byte[] indata = new byte[100];
        int inCntr;

        @Override
        public void run() {
            try {
                InetAddress serverAddr = InetAddress.getByName(targetIP);
                socket = new Socket(serverAddr, targetPort);
                socket.setKeepAlive(true);
                // DataOutputStream is used to write primitive data types to stream
                sockStrmOut = new DataOutputStream(socket.getOutputStream());
                sockStrmIn = new DataInputStream(new BufferedInputStream(socket.getInputStream()));

                if (socket.isConnected()) {
                    isConnected = true;
                    //Toast.makeText(context, "CONNECTED", Toast.LENGTH_SHORT).show();
                    //findViewById(R.id.action_connect).setBackgroundColor(0xFF60FF60);
                }
            } catch (UnknownHostException e1) {
                e1.printStackTrace();
            } catch (IOException e1) {
                e1.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }

            // TODO: 
            while (isConnected) {
                try {
                    inCntr = sockStrmIn.read(indata);
                }
                catch (IOException e) {
                    e.printStackTrace();
                }

                if (inCntr > 0) {
                    msgHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            if ( tbProtPrcs.Process(indata, inCntr) ) {
                                Toast.makeText(context, "Operation Success", Toast.LENGTH_SHORT).show();
                            }
                            else {
                                Toast.makeText(context, "Operation ERROR", Toast.LENGTH_SHORT).show();
                            }
                        }
                    });
                }
            }
        }
    }

    public void AttachToIP(String sIP, int iPort)
    {
        if ( (isIPValid(sIP)) && (iPort < cTARGET_PORT_UNASSIGNED) )
        {
            targetIP = sIP;
            targetPort = iPort;
            // Start the connection thread
            new Thread(new ClientThread()).start();
        }
    }

    public void DetachFromIP()
    {
        try {
            socket.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    public boolean SendData(byte[] data, int size)
    {
        boolean bResult = false;

        try
        {
            if ( (data != null) && (size > 0) && (sockStrmOut != null) ) {
                Toast.makeText(context, "Sending...", Toast.LENGTH_SHORT).show();
                sockStrmOut.write(data, 0, size);
                bResult = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return bResult;
    }

    public boolean isIPValid (String ip) {
        try {
            if (ip == null || ip.isEmpty()) {
                return false;
            }

            String[] parts = ip.split( "\\." );
            if ( parts.length != 4 ) {
                return false;
            }

            for ( String s : parts ) {
                int i = Integer.parseInt( s );
                if ( (i < 0) || (i > 255) ) {
                    return false;
                }
            }

            return true;
        } catch (NumberFormatException nfe) {
            return false;
        }
    }
}

}

最佳答案

您假设 read() 填充了缓冲区。没有指定这样做。请参阅 Javadoc。如果你想填充缓冲区,你必须使用readFully()

注意 isConnected() 在您测试时不可能为 false。

关于java - Android 应用程序中套接字 DataInputStream BufferedInputStream 的消息顺序错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32067724/

相关文章:

java.util.ConcurrentModificationException 没有按预期抛出

android - Xamarin.Forms。像 Telegram 一样滑动到上一页

java - 给定应用程序的多个 SingleThreadExecutor...是个好主意吗?

c - 提高 SIGINT,但卡在 c 中的线程中

java - 陷入多客户端聊天应用程序困境

java - 向图形添加功能

java - 在 Android CountDownTimer 中正确显示分钟

android - 位置选择器选择器输入小部件

java - 在 java 类中处理 JSP (Spring MVC)

java - 如何在android中检索包含特定单词的联系人姓名?