【问题标题】:DataInputStream readInt() works only onceDataInputStream readInt() 只工作一次
【发布时间】:2013-11-08 12:41:24
【问题描述】:

我正在使用 Java 套接字使两个 android 设备与同一个应用程序进行通信。通讯协议为:

1. client sends packet size S
2. client sends byte array with size S

我正在使用DataOutputStreamwriteInt() 将大小作为原始值写入流。然后服务器读取DataInputStreamreadInt() 的这个值。 问题是readInt() 仅读取第一个数据包的正确值。该方法第二次返回一个随机的int

相关代码sn-ps:

客户端:此方法在与服务器的有效 TCP 连接之上调用

public void write(byte[] packet)
{
    try
    {
        dataOutputStream.writeInt(packet.length);
        dataOutputStream.flush();

        dataOutputStream.write(packet);
        dataOutputStream.flush();        
    }
    catch (IOException e)
    {
        Log.e(ERROR_TAG, "write() failed", e);
    }
}

服务器端:这是读取数据的循环

...

int readBytes = 0;
int packetSize = 0;

while (true) {
    byte[] buffer = new byte[NET_BUFF_SIZE];

    try // first it reads the packet size from packet header
    {
        packetSize = dataInputStream.readInt();
    } catch (IOException e) {
        Log.e(ERROR_TAG, "readInt() failed", e);
        return;
    }

    while (readBytes < packetSize) {
        try {
            int readResult = dataInputStream.read(buffer);

            if (readResult != -1) {
                readBytes += readResult;
            } else {
                break;
            }
        } catch (IOException e) {
            Log.e(ERROR_TAG, "read() failed", e);
            break;
        }
    }
}

因此,当客户端调用write() 发送第二个数据包时,服务器从流中读取了错误的大小。

DataOutputStreamDataInputStream是这样初始化的:

// Server
inputStream = clientSocket.getInputStream();
dataInputStream = new DataInputStream(inputStream);

// Client
outputStream = socket.getOutputStream();
dataOutputStream = new DataOutputStream(outputStream);

我错过了什么?

【问题讨论】:

  • 丢弃读取数据包的循环并使用 readFully()。

标签: java android sockets datainputstream


【解决方案1】:

服务器读取尽可能多的可用数据。它可能读取的内容比客户端发送的数据包中包含的内容多,也可能读取的内容少。使用循环,您似乎可以处理read 返回的结果少于预期的情况,但您也应该处理它读取的内容多于数据包中包含的内容的情况。请记住,TCP 是面向流的:即使您调用 flush,也不能保证远程应用程序在对 read 的单独调用中接收到数据。

DataInput 接口定义了一个名为readFully 的方法,该方法可以读取任意数量的字节,不多也不少。这意味着您可以删除循环,将读取数据包的代码简化为:

packetSize = dataInputStream.readInt();
dataInputStream.readFully(buffer, 0, packetSize);

【讨论】:

    【解决方案2】:

    在您的服务器端,您应该在while(true) 循环之后重新初始化readBytes 变量:

    while (true) {
         readBytes = 0;
         ....
    }
    

    一切顺利。调试器可以帮助您更快地发现这个问题。

    【讨论】:

    • @Joni 的答案也应该被实现,否则它可能会在以后成为一个令人惊讶的错误......(它现在不是错误的原因可能是因为 flush() 调用客户端,但这不是可靠的情况....如果客户端发送两个非常快的数据包,您可能会遇到问题
    猜你喜欢
    • 1970-01-01
    • 2017-01-06
    • 2018-06-22
    • 2015-09-18
    • 2013-12-31
    • 1970-01-01
    • 2013-09-28
    • 2013-03-30
    • 2015-06-04
    相关资源
    最近更新 更多