套接字和输入流读取缓慢

3

我在我的应用程序中使用java.net.Socket来接收消息。但是接收速度非常慢,接收250 kB的数据需要超过7秒的时间。我该怎么做才能加速这个过程呢?7秒实在太长了...

InputStream is = null;
byte[] arr = new byte[8192];
try {
    is = client.getInputStream();
} catch (IOException e) {
    e.printStackTrace();
}

int bytesNumber;
StringBuffer sb = new StringBuffer();

try {
    while ((bytesNumber = is.read(arr)) >= 0) {
        if (bytesNumber == 8192) {
            sb.append(arr);
            continue;
        } else {
            for (int i = 0; i < bytesNumber; i++) {
                sb.append(arr[i]);
            }
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

什么是tab?你有is.read(tab) - MadConan
1
首先,你的代码在追加内容的方式上完全不正确。其次,通常情况下,代码并不是数据接收速度受限的瓶颈 - 客户端上传速度和服务器下载速度通常是限制速度的原因。 - tinker
那么快速阅读数据的最佳方式是什么? - karoluch
3个回答

0

这是因为如果你读取的长度小于缓冲区(arr)的长度,你会一个字符一个字符地添加。相反,应该附加整个缓冲区内容,不需要使用if语句:

sb.append(arr,0,butesNumber);

好的,我们将把它转换为字符串。然而问题在于逐个添加您的数组内容。 - Zielu
1
我真的怀疑这个循环会对执行时间产生任何影响,即使它不是正确的方式。 - PeterMmm
只有一种方法可以确定:)可能服务器部分也是逐个发送字节... - Zielu

0
正如Tinker所提到的,通常最大的限制是客户端和服务器网络。但是你可以大大简化你所拥有的东西。此外,我认为你的8K缓冲区大小太小了。除非你有严重的内存限制,否则请使用1M或更大的缓冲区。
public String readClient(Client client) throws IOException{
    final int bufferSize = 1024 << 9;

    BufferedInputStream in = new BufferedInputStream(client.getInputStream(),bufferSize);
    byte[] bytes = new byte[bufferSize];
    int bytesRead;
    StringBuilder sb = new StringBuilder(bufferSize);
    try {
        while ((bytesRead = in.read(bytes)) != -1) {
            sb.append(new String(bytes, 0, bytesRead));
        }
    }finally {
        in.close();
    }
    return sb.toString();
}

为什么要对字符串缓冲区预先分配 bufferSize/2 的空间? - S. Pauk
因为保持一致太无聊了。 - MadConan
他为什么需要一个1M的缓冲区来处理250k的文件?应用程序缓冲区应该与底层套接字的发送/接收缓冲区匹配。 - erickson
谁说它一定会是250K?我不认为需要有限制。JDK会读取流缓冲区,直到被套接字阻塞。使用较小的缓冲区可能无法阻塞套接字。 - MadConan

0

性能这么差很可能不是由于你的代码。你在同一系统之间使用curl或类似工具时,得到了什么样的性能?

更大的问题是,你的代码很可能无法实现你想要的功能。目前,你正在将字节数组的表示附加到字符串上,或者将一个字节的十进制表示附加到该字符串上。如果服务器发送“Hello, World!”,你最终会得到一个字符串,上面写着“7210110810811144328711111410810033”。对于更长的文件,你会看到类似于“[B@76c5a2f7”的东西弹出来,这就是字节数组的打印方式。

如果你接收到的文本是用UTF-8编码的,我会这样写:

CharArrayWriter str = new CharArrayWriter();
try (InputStream is = client.getInputStream()) {
  Reader r = new InputStreamReader(is, StandardCharsets.UTF_8);
  char[] buffer = new char[8192];
  while (true) {
    int n = r.read(buffer);
    if (n < 0)
      break;
    str.write(buffer, 0, n);
  }
}
System.out.println(str);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接