简体   繁体   English

使用Java NIO的TFTP客户端

[英]TFTP Client using Java NIO

I am trying to create a TFTP client using java NIO. 我正在尝试使用Java NIO创建TFTP客户端。 I am able to receive first 512 bytes of data from server, but not able to send acknowledgement to server for getting next block of packet. 我能够从服务器接收前512个字节的数据,但无法将确认发送到服务器以获取下一个数据包块。 I am new to java NIO and networking. 我是Java NIO和网络的新手。 Not able to find solution for it. 找不到解决方案。 So can anyone help me on this to find fix for it? 那么有人可以帮我找到解决办法吗? Thanks in advance 提前致谢

package app.sdc.business;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;

import app.sdc.business.NetworkElementPool.NetworkElement;

public class TftpNioClient {

static byte OP_ERROR = 5, OP_DATAPACKET = 3, OP_ACK = 4, OP_RRQ = 1, OP_WRQ = 2;

static final String LOCALHOST = "localhost";

static InetSocketAddress server = new InetSocketAddress(LOCALHOST, 69);

// main method
public static void main(String[] args) throws IOException {
    processDownload();
}

// Will start downloading of all files
public static void processDownload() throws IOException {
    Selector sel = Selector.open();
    for (NetworkElement ne : NetworkElementPool.getNetworkElements()) {
        DatagramChannel channel = DatagramChannel.open();
        channel.configureBlocking(false);
        channel.connect(server);
        channel.register(sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE, ne);
    }

    int counter = 0;
    while (true) {
        int n = sel.select(3000);
        if (n < 1) {
            continue;
        }

        Iterator<SelectionKey> itr = sel.selectedKeys().iterator();
        while (itr.hasNext()) {
            SelectionKey key = itr.next();
            itr.remove();

            if (key.isWritable()) {
                counter++;
                System.out.println("channel Write...");
                downloadUsingTFTPProtocol(key);
            } else if (key.isReadable()) {
                System.out.println("Channel Read");
            }

        }

        if (counter >= NetworkElementPool.getNetworkElements().size()) {
            break;
        }
    }
}

// method for downloading file
private static void downloadUsingTFTPProtocol(SelectionKey keyy) throws IOException {
    ByteArrayOutputStream byteOutOS = new ByteArrayOutputStream();
    Selector sel = keyy.selector();
    NetworkElement ne = (NetworkElement) keyy.attachment();
    String fileName = ne.getFilename();

    DatagramChannel channel = DatagramChannel.open();
    channel.configureBlocking(false);
    channel.register(sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE);

    boolean reqSent = false;
    ByteBuffer sendBuffer = null;
    ByteBuffer receivedBuffer = ByteBuffer.allocate(516);

    boolean stop = false;
    byte[] dataByte = null;
    boolean received = false;

    outer: while (true) {
        int n = sel.select();
        if (n < 1) {
            continue;
        }

        Iterator<SelectionKey> itr = sel.selectedKeys().iterator();

        while (itr.hasNext()) {
            SelectionKey key = itr.next();
            itr.remove();

            DatagramChannel dc = (DatagramChannel) key.channel();

            if (!received && key.isReadable()) {
                System.out.println("receive packet...");

                receivedBuffer.clear();
                dc.receive(receivedBuffer);
                stop = receivedBuffer.position() < 512;

                receivedBuffer.flip();
                while (receivedBuffer.hasRemaining()) {
                    System.out.print(receivedBuffer.get());
                }
                System.out.println();

                dataByte = receivedBuffer.array();
                received = true;
            }

            if (key.isWritable()) {
                if (!reqSent) {
                    System.out.println("Sending First Request....");
                    sendBuffer = createInitialReadRequest("SDCSource/" + fileName);
                    sendBuffer.flip();
                    dc.send(sendBuffer, server);
                    reqSent = true;
                } else if (received) {
                    System.out.println("Send Acknowledgement");
                    byte[] opCode = new byte[] { dataByte[0], dataByte[1] };

                    if (opCode[1] == OP_ERROR) {
                        System.out.println("Error Occured...");
                        break outer;
                    } else if (opCode[1] == OP_DATAPACKET) {
                        byte[] blockNumber = { dataByte[2], dataByte[3] };

                        sendBuffer = getAcknowledgment(blockNumber, dc, server);
                        sendBuffer.flip();
                        dc.send(sendBuffer, server);

                        DataOutputStream dos = new DataOutputStream(byteOutOS);
                        dos.write(dataByte, 4, dataByte.length - 4);
                    }
                }
                received = false;
            }

            if (stop) {
                break outer;
            }
        }
    }

    writeFile(byteOutOS, fileName);
}

// Creates request packet to send request at the beginning
private static ByteBuffer createInitialReadRequest(final String fileName) {
    String mode = "octet";
    int rrqByteLength = 2 + fileName.getBytes().length + 1 + mode.getBytes().length + 1;
    byte[] rrqByteArray = new byte[rrqByteLength];

    ByteBuffer reqBuf = ByteBuffer.allocate(rrqByteArray.length);

    reqBuf.put((byte) 0).put((byte) OP_RRQ);
    reqBuf.put(fileName.getBytes());
    reqBuf.put((byte) 0);
    reqBuf.put(mode.getBytes());
    reqBuf.put((byte) 0);

    return reqBuf;
}

// Creating acknowledgement code
private static ByteBuffer getAcknowledgment(byte[] blockNumber, DatagramChannel channel, InetSocketAddress server)
        throws IOException {

    byte[] acknowledge = { 0, OP_ACK, blockNumber[0], blockNumber[1] };
    ByteBuffer buffer = ByteBuffer.allocate(acknowledge.length);
    buffer.put(acknowledge);
    return buffer;
}

// Create file after all packets have been received
private static void writeFile(ByteArrayOutputStream baoStream, String fileName) {
    try {
        OutputStream outputStream = new FileOutputStream("SDCTarget/" + fileName);
        baoStream.writeTo(outputStream);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

}


package app.sdc.business;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class NetworkElementPool {

private static List<NetworkElement> networkElements;

// create a list of network elements by reading files downloading. It is just a hard-coded way to create network elements
static {
    networkElements = new ArrayList<NetworkElement>();
    File sourceDir = new File("C:/OpenTFTPServer/SDCSource");
    if (sourceDir.exists()) {
        for (String filename : sourceDir.list()) {
            networkElements.add(new NetworkElement("localhost", 8080, filename));
        }
    } else {
        System.err.println("Network Elements couldn't found...");
    }
}

public static List<NetworkElement> getNetworkElements() {
    return networkElements;
}

// Represents a network element
public static class NetworkElement {
    private String host;
    private int port;
    private String filename;

    public String getHost() {
        return host;
    }

    public int getPort() {
        return port;
    }

    public String getFilename() {
        return filename;
    }

    public NetworkElement() {
        super();
    }

    public NetworkElement(String host, int port, String filename) {
        super();
        this.host = host;
        this.port = port;
        this.filename = filename;
    }

    @Override
    public String toString() {
        return "NetworkElement [host=" + host + ", port=" + port + ",  filename=" + filename + "]";
    }

}
}

Note: TftpClient class contain one foreach loop to start downloading file from more than one network element. 注意:TftpClient类包含一个foreach循环,可以从多个网络元素开始下载文件。

You're not sending the acknowledgements at the correct time, after a read: you're sending them every time around the loop. 阅读后,您没有在正确的时间发送确认:每次循环时都发送确认。 You don't need to wait for OP_WRITE to do a write. 您无需等待OP_WRITE进行写操作。 Just write whenever you need to. 只需在需要时写。 You shouldn't even register for OP_WRITE most of the time. 您甚至大部分时间都不应该注册OP_WRITE。 See this answer for the correct technique. 请参阅此答案以获取正确的技术。 But in the case of TFTP it's dubious whether you need that at all. 但是对于TFTP来说,是否需要它是个疑问。 Or NIO either. 还是NIO。

In your code, if the key.isReadable, then you can read the packet. 在代码中,如果key.isReadable,则可以读取数据包。 Then immediately followed by that you can send the ACK back to TFTP server. 然后立即将ACK发送回TFTP服务器。

} else if (received) {

This block of code can go to the line immediately next to dc.receive(receivedBuffer); 该代码块可以直接转到dc.receive(receivedBuffer);旁边的行。

You can refer this example java nio tftp client 您可以参考以下示例java nio tftp client

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM