【问题标题】:Socket / InputStream Dropping DataSocket / InputStream 丢弃数据
【发布时间】:2020-12-31 09:47:36
【问题描述】:

我一直在尝试使用 Java 的 ServerSocket、Socket 和 InputStream 设置一个基本服务器。在读取 InputStream 时,预期的结果是一系列重复的字节 0x0b 和 10 个字节的关联数据(0x0b-data-0x0b-data 重复)。问题是Java应用程序中的某个地方完全丢弃了少量字节,在某些数据包中只留下了9个字节的数据(在使用Wireshark检查后,这些字节存在于原始数据包中,而不是InputStream的输出)。

发生这种情况的背景是为了响应某些行为而快速连续发送大约一百个数据包的序列。我相信这仅仅是因为有更多的字节有机会被丢弃,而不是接收它的速度。

经过一番搜索,我在Java Socket InputStream read missing bytes 发现了同样的问题,但该线程因要求提供更多信息而死掉(因此没有有用的答案)。

导致此问题的全部代码如下。最重要的部分是 while true 循环和 readData 函数(不包括 else if 链)。

为了澄清,问题是这种奇怪行为的原因。

package com.kevycat.minerria;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;

public class Minerria {

    private static Socket client;

    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket(7777);
        System.out.println("Listening");

        client = socket.accept();
        InputStream stream = client.getInputStream();

        System.out.println("Connected");

        byte[] extraData = new byte[0];
        while (true) {
            int available = stream.available();
            byte[] data = new byte[available + extraData.length];
            stream.read(data, extraData.length, available);

            if (extraData.length > 0) {
                for (int i = 0; i < extraData.length; i++) {
                    data[i] = extraData[i];
                }
            }

            if (data.length > 0) {
                for (int i = 0; i < data.length; i++) {
                    System.out.print(data[i] + " ");
                }
                System.out.println(" ");
            }

            if (data.length > 0) {
                extraData = readData(data);
            }
        }

    }

    private static byte[] readData(byte[] data) throws IOException {

        if (data.length < 3) {
            return data;
        }

        int length = data[0] + data[1] * 256;
        int type = data[2];

        String payload = new String(Arrays.copyOfRange(data, 4, length));

        System.out.println(length + " " + type + " " + payload);

        if (type == 1) {
            client.getOutputStream().write(new byte[] { 5, 0, 3, 0, 0 });
        } else if (type == 4) {
            client.getOutputStream().write(data);
        } else if (type == 5) {
            client.getOutputStream().write(data);
        } else if (type == 68) {
            client.getOutputStream().write(data);
        } else if (type == 16) {
            client.getOutputStream().write(data);
        } else if (type == 42) {
            client.getOutputStream().write(data);
        } else if (type == 50) {
            client.getOutputStream().write(data);
        } else if (type == 6) {
            byte[] b = new byte[80];
            b[0] = 80;
            b[2] = 7;
            client.getOutputStream().write(b);
        } else if (type == 8) {
            client.getOutputStream().write(new byte[] { 11, 0, 9, 0, 1, 0, 0, 0, 'e', 'e', 'e' });
        }

        return data.length > length ? Arrays.copyOfRange(data, length, data.length - 1) : new byte[0];
    }

}

【问题讨论】:

    标签: java


    【解决方案1】:

    int available = stream.available();

    不要这样做。 available() 没有任何用处。如果您不相信我,我将引用 javadoc:

    返回对可以从此输入流读取(或跳过)的字节数的估计值,而不会被下一次为此输入流的方法调用阻塞。

    '估计'。那是程序员的行话。用简单的英语翻译为“几乎没用”。

    stream.read(data, extraData.length, available);

    这是你的错误。您不能忽略 read 调用的返回值。阅读 javadoc:该 read 调用将保证:

    1. 它至少读取 1 个字节,除非流已关闭/结束(然后它什么也不读取,并返回 -1)。
    2. 它的阅读量永远不会超过available
    3. 但这就是它结束的地方。这种方法只读取available 的一半是完全合法的。
    4. ACTUAL 返回读取的字节数,除非它什么都不读取(只有在流关闭时才可能),然后它返回 -1。

    之所以如此复杂,是为了尽快将其发送给您。如果数据包以 6 个字节到达您的网卡,而您要求 10 个,它会给您 6 个。

    如果您只想读取 X 个字节(例如,10 个字节,在您的协议中听起来很有用),请使用 .readFully(),并要求流等待所需的时间(具体而言,仅返回到流结束是读取所有 10 个字节)。

    对于您的协议,我看到了两个简单的选择:

    1. 将流包装成BufferedInputStream,并仅调用read(),即无参数的。这是一个更简单的调用:如果流结束它返回 -1,否则返回一个字节,这很容易。它会根据需要等待,直到有数据或流关闭为止。
    2. 或者,使用.readFully。如果您知道数据每次都以精确的 11 个块到达,那也同样有效。虽然,在非缓冲流上调用“短”读取(11 个字节非常短)可能相当低效。取决于底层流。

    1 号门不那么凌乱。它绝对不会因为一次请求的字节太少而导致效率低下,而且很难弄乱你的代码。

    【讨论】:

      猜你喜欢
      • 2015-07-11
      • 1970-01-01
      • 1970-01-01
      • 2020-03-17
      • 1970-01-01
      • 1970-01-01
      • 2012-07-15
      • 2017-08-08
      • 2012-08-27
      相关资源
      最近更新 更多