通过TFTP传输的文件与主机上的文件大小不同

时间:2019-10-29 10:52:21

标签: java android tftp

很长一段时间以来,我一直在努力处理Android应用程序中的TFTP协议。其主要功能是从托管TFTP服务器的定制设计设备下载文件。

我正在浏览互联网,希望找到一些好的,已经写好的实现。首先,我尝试使用TFTP库,该库是Apache Commons的一部分。不幸的是,没有运气-持续的超时甚至完全冻结。经过进一步研究,我在github-please take a look上找到了一些代码。我在Android上采用了代码,经过一些调整后,终于设法收到了一些文件。

所述设备的创建者,该块大小应恰好为1015字节。因此,我将包大小增加到1015,并更新了创建读取请求数据包的方法:

DatagramPacket createReadRequestPacket(String strFileName) {
    byte[] filename = strFileName.getBytes();
    byte[] mode = currentMode.getBytes();
    int len = rOpCode.length + filename.length + mode.length + 2;
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream(len);
    try {
        outputStream.write(rOpCode);
        outputStream.write(filename);
        byte term = 0;
        outputStream.write(term);
        outputStream.write(mode); // "octet"
        outputStream.write(term);
        outputStream.write("blksize".getBytes());
        outputStream.write(term);
        outputStream.write("1015".getBytes());
        outputStream.write(term);
    } catch (IOException e) {
        e.printStackTrace();
    }

    byte[] readPacketArray = outputStream.toByteArray();
    return new DatagramPacket(readPacketArray, readPacketArray.length, serverAddr, port);
}

正在下载块,但有一个主要问题-我正在下载的文件是部分存储的,每个文件512kB(最后一个除外),而我在Android设备上收到的每个文件都大了约0.5kB。似乎每次增加一个字节,或者增加一个整数。显然我不完全理解,我缺少了一些东西。

这是我的文件接收方法:

    byte previousBlockNumber = (byte) -1;
    try {
        PktFactory pktFactory;
        DatagramSocket clientSocket;
        byte[] buf;
        DatagramPacket sendingPkt;
        DatagramPacket receivedPkt;

        System.out.print(ftpHandle);

        if (isConnected) {
            System.out.println("You're already connected to " + hostname.getCanonicalHostName());
        }
        try {
            hostname = InetAddress.getByName(host);
            if (!hostname.isReachable(4000)) {
                System.out.println("Hostname you provided is not responding. Try again.");
                return false;
            }
        } catch (UnknownHostException e) {
            System.out.println("tftp: nodename nor servname provided, or not known");
            return false;
        }
        clientSocket = new DatagramSocket();
        pktFactory = new PktFactory(PKT_LENGTH + 4, hostname, TFTP_PORT);
        System.out.println("Connecting " +
                hostname.getCanonicalHostName() + " at the port number " + TFTP_PORT);
        isConnected = true;
        ftpHandle = "tftp@" + hostname.getCanonicalHostName() + "> ";

        System.out.println("mode " + PktFactory.currentMode);

        if (!isConnected) {
            System.out.println("You must be connected first!");
        }
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        buf = new byte[PKT_LENGTH + 4];

        /* Sending the reading request with the filename to the server. **/
        try {
            /* Sending a RRQ with the filename. **/
            System.out.println("Sending request to server.");
            sendingPkt = pktFactory.createReadRequestPacket(filename);
            clientSocket.setSoTimeout(4500);
            clientSocket.send(sendingPkt);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Connection with server failed");
        }
        boolean receivingMessage = true;

        while (true) {
            try {
                receivedPkt = new DatagramPacket(buf, buf.length);
                clientSocket.setSoTimeout(10000);
                clientSocket.receive(receivedPkt);

                byte[] dPkt = receivedPkt.getData();
                byte[] ropCode = pktFactory.getOpCode(dPkt);

                /* rPkt either a DATA or an ERROR pkt. If an error then print the error message and
                 * terminate the program finish get command. **/
                if (ropCode[1] == 5) {
                    String errorMsg = pktFactory.getErrorMessage(dPkt);
                    System.out.println(errorMsg);
                    return false;
                }

                if (receivedPkt.getLength() < PKT_LENGTH + 4 && ropCode[1] == 3) {
                    byte[] fileDataBytes = pktFactory.getDataBytes(dPkt);
                    outputStream.write(fileDataBytes);
                    if (isListFile) {
                        listBytes = outputStream.toByteArray();
                    } else {
                        FileOutputStream fstream = new FileOutputStream(Constants.EEG_DATA_PATH.concat("file.bin"), true);
                        // Let's get the last data pkt for the current transfering file.
                        fstream.write(outputStream.toByteArray());
                        fstream.close();
                    }

                    // It's time to send the last ACK message before Normal termination.
                    byte[] bNum = pktFactory.getBlockNum(dPkt);
                    DatagramPacket sPkt = pktFactory.createAckPacket(bNum, receivedPkt.getPort());
                    clientSocket.send(sPkt);

                    disconnect();
                    return true;
                }

                if (ropCode[1] == 3) {
                    if (receivingMessage) {
                        System.out.println("Receiving the file now..");
                        receivingMessage = false;
                    }
                    byte[] bNum = pktFactory.getBlockNum(dPkt);

                    //I've added this if and it reduces file size a little (it was more than 0,5kB bigger)
                    if (previousBlockNumber != bNum[1]) {
                        byte[] fileDataBytes = pktFactory.getDataBytes(dPkt);
                        previousBlockNumber = bNum[1];
                        outputStream.write(fileDataBytes);
                    }

                    /* For each received DATA pkt we need to send ACK pkt back. **/
                    DatagramPacket sPkt = pktFactory.createAckPacket(bNum, receivedPkt.getPort());
                    clientSocket.send(sPkt);
                }
            } catch (SocketTimeoutException e) {
                disconnect();
                System.out.println("Server didn't respond and timeout occured.");
                return false;
            }
        }
    } catch (Exception e) {
        System.out.println(e.getMessage());
        return false;
    }

1 个答案:

答案 0 :(得分:0)

我知道出了什么问题。收到最后一个数据包时,此行导致了这种奇怪的行为:

byte[] fileDataBytes = pktFactory.getDataBytes(dPkt);

即使接收的数据较小,返回的数组大小也始终等于指定的数据包长度。在我的情况下,最后一个数据包为0字节(tftp为+4字节),但即使这样,额外的512字节也被添加到输出流中。

为解决这个问题,我用额外的参数重载了上述方法-当接收的数据大小大于4字节且小于指定的数据包大小(512字节)时,接收的数据包的实际大小。此更改导致最后一个数据包的数组大小正确,因此接收到的文件在操作结束时具有正确的大小。