Java DataInputStream length

5

我正在为学校作业创建一个文件服务器应用程序。目前我有一个简单的 Client 类,通过 TCP 发送图像,以及一个接收并将其写入文件的 Server 类。

这是我的客户端代码:

import java.io.*;
import java.net.*;

class Client {
    public static void main(String args[]) throws Exception {
        long start = System.currentTimeMillis();
        Socket clientSocket = new Socket("127.0.0.1", 6789);
        DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());

        File file = new File("hot.jpg");
        FileInputStream fin = new FileInputStream(file);
        byte sendData[] = new byte[(int)file.length()];
        fin.read(sendData);

        outToServer.write(sendData, 0, sendData.length);
        clientSocket.close();

        long end = System.currentTimeMillis();
        System.out.println("Took " + (end - start) + "ms");
    }
}

这是我的服务器代码。

import java.io.*;
import java.net.*;

class Server {
    public static void main(String args[]) throws Exception {
        ServerSocket serverSocket = new ServerSocket(6789);
        Socket connectionSocket = serverSocket.accept();
        DataInputStream dis = new DataInputStream(connectionSocket.getInputStream());

        byte[] receivedData = new byte[61500]; // <- THIS NUMBER

        for(int i = 0; i < receivedData.length; i++)
            receivedData[i] = dis.readByte();

        connectionSocket.close();
        serverSocket.close();

        FileOutputStream fos = new FileOutputStream("received.jpg");
        fos.write(receivedData);
        fos.close();
    }
}

我的问题是如何获取正在发送的文件的大小。如果您查看Server代码,您会发现我已经硬编码了这个数字,即61500。如何动态检索此数字?

或者,我这样做是错误的吗?是否有其他替代方案?

2个回答

7

在发送文件之前添加一个“长度字段”。(请注意,由于您将文件读取到内存中,因此文件的最大大小可能为~2GB。)


在发送文件之前写入文件的长度:

  outToServer.writeInt(sendData.length);

当接收到数据时,首先读取长度并将其用作长度:

  int dataLength = dis.readInt()
  byte[] receivedData = new byte[dataLength];

更好的方法是不要先将文件读入内存,而是直接从FileInputStream传输文件 - 这样您就可以传输更大的文件了!

我没有看到在发送文件之前将其读入内存的地方?它在哪里,我该如何避免它? - David Weng
1
@David Weng:在你使用“<- THIS NUMBER”时,你正在为整个文件分配足够的内存。你很少需要这样做,而且它不适用于甚至是中等大小的文件。 - user207421

2

如果您知道长度,使用readFully()比逐字节读取要高效得多。

在这种情况下,您不需要知道长度,可以编写循环以读取/写入尽可能多的数据。

InputStream is = connectionSocket.getInputStream();

byte[] bytes = new byte[8192];
int len;
while((len = is.read(bytes)) > 0)
    fos.write(bytes, 0, len);

您可以在读取数据时将其复制,而无需将整个文件读入内存中。

FileInputStream fis = new FileInputStream(filename);
OutputStream os = socket.getOutputStream();

byte[] bytes = new byte[8192];
int len;
while((len = fis.read(bytes)) > 0)
    os.write(bytes, 0, len);

你可以使用Apache IOUtils .copy()在两个流之间执行复制,如果你愿意的话。
这种方法的优点是文件可以是任何大小(大于2 GB)。使用数组限制为2 GB(并使用更多内存)。

@codymanix,这基本上是第一个示例的代码复制。有什么不合理的地方吗? - Peter Lawrey

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