【问题标题】:Java reading files via FIOJava 通过 FIO 读取文件
【发布时间】:2012-03-07 00:04:49
【问题描述】:

我正在制作一个 TFTP 应用程序。根据 RFC 协议,所有数据必须以最大 512 字节的块发送。所以每个数据包可以是

我将每个文件读入一个字节[]输出数据=新字节[512];数组,我将它发送给客户端,但是当它得到一个总大小小于 512 字节的文件(如 ascii 文件或 .ini、.css、.html 等)时,似乎出现了问题。

奇怪的是,对于 UDP 协议,每次高达 3mb 的传输都过去了,没有大的损失。似乎发生的唯一损失是读取文件的最后一块小于 512 字节时。

private void sendData() throws Exception
{
    DatagramPacket data = new DatagramPacket(outgoingData, outgoingData.length, clientAddress, clientPort);
    InputStream fis = new FileInputStream(responseData);

    int a;
    while((a = fis.read(outgoingData,0,512)) != -1)
    {
        serverSocket.send(data);
        Thread.sleep(5);
    }
}

既然这是关于读取文件的问题,我该如何修复文件末尾的丢失以及它不读取小于 512 的文件的问题

客户:

private void receiveData() throws Exception
{
    DatagramPacket receiveData = new DatagramPacket(incomingData, incomingData.length);
    OutputStream fos = new FileOutputStream(new File("1"+data));
    while(true)
    {
        clientSocket.receive(receiveData);
        if(receiveData.getLength() == 512)
        {
            fos.write(incomingData);xx
        } else {
            fos.write(incomingData);
            fos.close();
            break;
        }
    }
    clientSocket.close();
}

【问题讨论】:

标签: java sockets networking udp


【解决方案1】:

您的数据包 (data) 现在的固定长度为 outgoingData.length (512?)。所以当你调用serverSocket.send(data)时,它会发送outgoingData数组中的所有数据。

问题是fis.read(outgoingData, 0, 512) 有时读取的数据少于 512 字节。这很可能发生在文件输入流的末尾,当时根本没有那么多数据可供读取。但它也可能更早发生(在实践中不太可能,但您仍应检查以确保安全)。

您已经在a 中存储了实际读取的字节数。只需将此号码传递给setLength,以便DatagramSocket 知道只发送这么多数据。

这应该可以解决问题:

private void sendData() throws Exception
{
    DatagramPacket data = new DatagramPacket(outgoingData, outgoingData.length, clientAddress, clientPort);
    InputStream fis = new FileInputStream(responseData);

    int a;
    while((a = fis.read(outgoingData,0,512)) != -1)
    {
        data.setLength(a);
        // or this
        //data = new DatagramPacket(outgoingData, a, clientAddress, clientPort);

        serverSocket.send(data);
        Thread.sleep(5);
    }
}

接收:

private void receiveData() throws Exception {
    DatagramPacket receiveData = new DatagramPacket(new byte[512], 512);
    OutputStream fos = new FileOutputStream(new File("1"+data));
    while (true) {
        clientSocket.receive(receiveData);
        if (receiveData.getLength() == 512) {
            fos.write(receiveData.getData());
        } else {
            fos.write(receiveData.getData(), receiveData.getOffset(), receiveData.getLength());
            break;
        }
    }
    fos.close();
    clientSocket.close();
}

【讨论】:

  • 你说我应该把 DatagramPacket 放在 while 循环的下面,而不是调用 outgoingData.length 我应该调用一个?
  • @kellax 是的,如果我理解你的话,那也应该有效。 (你的意思是在 while 循环中构造一个新的 DatagramPacket,对吗?)我会保持 data 不变,并在 while 循环中调用 setLength。但无论哪种方式都应该没问题。
  • 是的,但是在这个方法中我不能把它放在 data = new..(..,data.setLenth(a)) 中,因为 a 还没有被初始化。你能在我的例子中展示一下吗
  • 嗯,我一定是误会你了。我添加了一些类似于您的示例的代码,清楚吗?
  • 我是这样写的。 iv 将 data.setLength(a) 放在 while 循环中,然后放在另一边 .. 客户端收到此消息,如下面的第二个答案所示,但是小于 512 的文件仍未通过。
【解决方案2】:
    private void sendData() throws Exception
{
    DatagramPacket data = new DatagramPacket(outgoingData, outgoingData.length, clientAddress, clientPort);
    InputStream fis = new FileInputStream(responseData);

    int a;
    while((a = fis.read(outgoingData,0,512)) != -1)
    {
        data.setLength(a); //'a' is the number of bytes read setLength(int) lets you set the length of bytes you want to sent
        serverSocket.send(data);
        Thread.sleep(5);
    }
}

setLength(int) Method DatagramPacket 现在在接收端,你可以这样做:

byte[]buffer = new byte[512];
DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
while(socket.isBound() && !socket.isClosed()){
    socket.receive(packet);
    system.out.println("Packet Length: "+packet.getLength());
    //code here
}

修复: IN Server.java

private void sendResponse(String res) throws Exception
        {
            if(res.equals("Y"))
            {
                // Send ACK -> Send File
                DatagramPacket x = new DatagramPacket(outgoingData, outgoingData.length, clientAddress, clientPort);
                serverSocket.send(x); //<<SEND ACK
                sendData();
            } else {
                String error = "ERROR: The file you requested does not exist.";
                outgoingData = error.getBytes();
                DatagramPacket err = new DatagramPacket(outgoingData, outgoingData.length, clientAddress, clientPort);

                serverSocket.send(err);
            }
        }

我注意到您没有发送确认并且客户正在期待它,这似乎是您唯一的问题。第一个数据包目前对您没有任何作用。现在您只需将 ack 数据包设置为您想要的。我刚刚发送了它,但它不包含字符串“ERROR”。

【讨论】:

  • 这正是我编写它的方式,而 sys.out 是一个输出流。但是,我猜任何小于 512 的块都没有通过。
  • 尝试打印出packet.getLength(),我想看看它说了什么。
  • 说 512 即使它通过的只有大约 20 个字节。我的代码copypastecode.com/180187 copypastecode.com/180191
  • 认为我找到了您的问题。在此方法中:receiveResponse 在 Client.java 中,您正在创建一个新字符串,但使用整个缓冲区。而不是尝试这个:String rsp = new String(response.getData(),0,response.getLength());
  • nvm 看起来不像你真的用那个字符串做任何事情,但无论如何你应该修复它
猜你喜欢
  • 2011-05-03
  • 1970-01-01
  • 2012-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-28
相关资源
最近更新 更多