TFTP服务端客户端(Java编写)-TFTP上传下载

TFTP 服务端、客户端如下:

比较直接,原理自己搜,基于UDP,直接复制就行,记得把IP地址换了。

TFTP Server端:

TFTPack.java:

import java.util.Arrays;

public final class TFTPack extends TFTPpacket {

    protected TFTPack() {
    }

    public TFTPack(int blockNumber) {
        this.length = 4;
        this.message = new byte[this.length];
        System.out.println("TFTPack ack packet created:" +blockNumber);
        put(0, (short) 4);
        put(2, (short) blockNumber);
        System.out.println("Service receive data: " + Arrays.toString(message));
    }

    public int blockNumber() {
        return get(2);
    }
}

TFTPdata.java:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public final class TFTPdata extends TFTPpacket {

    protected TFTPdata() {
    }

    public TFTPdata(int blockNumber, InputStream in) throws Exception {
        System.out.println("Data packet created:" +blockNumber);
        this.message = new byte[maxTftpPakLen];
        if (in == null) {
            put(0, (short) 5); // TODO
            put(2, (short) blockNumber);
            this.length = 4;
        }
        put(0, (short) 3); // TODO
        put(2, (short) blockNumber);
        System.out.println("TFTPdata: " + message + " blockNumber: " + blockNumber);
        int len = in.read(this.message, 4, maxTftpData) + 4;
        if (len == 3)
            this.length = 4;
    }

    public int blockNumber() {
        return get(2);
    }

    public void data(byte[] buffer) {
        byte[] buffer1 = buffer; // TODO
        buffer1 = new byte[this.length - 4];

        for (int i = 0; i < this.length - 4; i++)
            buffer1[i] = this.message[(i + 4)];
    }

    public int write(OutputStream out) throws IOException {
        out.write(this.message, 4, this.length - 4);
        return this.length - 4;
    }
}

TFTPerror.java:

public final class TFTPerror extends TFTPpacket
{
  protected TFTPerror()
  {
  }

  public TFTPerror(int number, String message)
  {
    if (message != null) {
      System.out.println( "TFTP error: " + message);
      this.length = (4 + message.length() + 1);
      this.message = new byte[this.length];

      put(0, (short)5); //TODO 
      put(2, (short)number);
      put(4, message, (byte)0); //TODO 
    }
  }

  public int number()
  {
    return get(2);
  }

  public String message() {
    return get(4, (byte)0); //TODO 
  }
}

TftpException.java:

public class TftpException extends Exception {

    TftpException() {
    }

    public TftpException(String s) {
        super(s);
    }
}

TFTPpacket.java:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Arrays;

public class TFTPpacket {

    public static final int tftpPort = 69;
    public static final int maxTftpPakLen = 516;
    public static final int maxTftpData = 512;
    protected static final short tftpRRQ = 1;
    protected static final short tftpWRQ = 2;
    protected static final short tftpDATA = 3;
    protected static final short tftpACK = 4;
    protected static final short tftpERROR = 5;
    protected static final int opOffset = 0;
    protected static final int fileOffset = 2;
    protected static final int blkOffset = 2;
    protected static final int dataOffset = 4;
    protected static final int numOffset = 2;
    protected static final int msgOffset = 4;

    protected int length;
    protected byte[] message;
    protected InetAddress host;
    protected int port;

    public TFTPpacket() {
        this.message = new byte[maxTftpPakLen];
        this.length = maxTftpPakLen;
    }

    public static TFTPpacket receive(DatagramSocket sock) throws IOException {
        TFTPpacket in = new TFTPpacket();
        TFTPpacket retPak = null;
        DatagramPacket inPak = new DatagramPacket(in.message, in.length);

        sock.receive(inPak);

        switch (in.get(0)) {
            case 1:
                retPak = new TFTPread();//RRQ
                break;
            case 2:
                retPak = new TFTPwrite();//WRQ
                break;
            case 3:
                retPak = new TFTPdata();
                break;
            case 4:
                retPak = new TFTPack();
                break;
            case 5:
                retPak = new TFTPerror();
        }

        if (retPak != null) {
            retPak.message = in.message;
            retPak.length = inPak.getLength();
            retPak.host = inPak.getAddress();
            retPak.port = inPak.getPort();
            System.out.println("Data packet: " + Arrays.toString(in.message) + " length: " + inPak.getLength()
                    + " host:" + inPak.getAddress() + " port: " + inPak.getPort());
        }

        return retPak;
    }

    public void send(InetAddress ip, DatagramSocket sock) throws IOException {
        sock.send(new DatagramPacket(this.message, this.length, ip, tftpPort));
    }

    public void send(InetAddress ip, int p, DatagramSocket s) throws IOException {
        s.send(new DatagramPacket(this.message, this.length, ip, p));
    }

    public InetAddress getAddress() {
        return this.host;
    }

    public int getPort() {
        return this.port;
    }

    public int getLength() {
        return this.length;
    }

    protected void put(int at, short value) {
        int atLocal = at;
        this.message[(atLocal++)] = (byte) (value >>> 8);
        this.message[atLocal] = (byte) (value % 256);
    }

    protected void put(int at, String value, byte del) {
        System.arraycopy(value.getBytes(), 0, this.message, at, value.length());
        this.message[(at + value.length())] = del;
    }

    protected int get(int at) {
        return (this.message[at] & 0xFF) << 8 | this.message[(at + 1)] & 0xFF;
    }

    protected String get(int at, byte del) {
        int atLocal = at;
        StringBuffer result = new StringBuffer();

        while (this.message[atLocal] != del)
            result.append((char) this.message[(atLocal++)]);

        return result.toString();
    }

    public String fileName() {
        return null;
    }

}

TFTPread.java(读操作,这个不是实际的读):

public final class TFTPread extends TFTPpacket {
    byte zero = 0;
    short opcode = 1;

    private void initialize(String filename, String reqType) {
        this.length = (2 + filename.length() + 1 + reqType.length() + 1);
        this.message = new byte[this.length];

        put(0, opcode);
        put(2, filename, zero);
        put(2 + filename.length() + 1, reqType, zero);
    }

    protected TFTPread() {
    }

    public TFTPread(String filename) {
        initialize(filename, "octet");
    }

    public TFTPread(String filename, String reqType) {
        initialize(filename, reqType);
    }

    public String fileName() {
        return get(2, (byte) 0); //TODO
    }

    public String requestType() {
        String fname = fileName();
        return get(2 + fname.length() + 1, (byte) 0); //TODO
    }
}

TFTPwrite.java(同样不是实际的写):

public final class TFTPwrite extends TFTPpacket {

    private void initialize(String filename, String reqType) {
        this.length = (2 + filename.length() + 1 + reqType.length() + 1);
        this.message = new byte[this.length];

        put(0, (short) 2);
        put(2, filename, (byte) 0);
        put(2 + filename.length() + 1, reqType, (byte) 0);
    }

    protected TFTPwrite() {
    }

    public TFTPwrite(String filename) {
        initialize(filename, "octet");
    }

    public TFTPwrite(String filename, String reqType) {
        initialize(filename, reqType);
    }

    public String fileName() {
        return get(2, (byte) 0);
    }

    public String requestType() {
        String fname = fileName();
        return get(2 + fname.length() + 1, (byte) 0);
    }
}

接下来是Server端实际的操作

TFTPServiceHandler.java:(重点服务端读写操作类)

import xxx.TFTPack;
import xxx.TFTPdata;
import xxx.TFTPerror;
import xxx.TFTPpacket;
import xxx.TFTPread;
import xxx.TFTPwrite;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;

/**
 * TFTP Server handler处理类
 *
 * @author: zhangyongtao
 * @date: 2022/7/10
 */
public class TFTPServiceHandler implements Runnable {

    protected DatagramSocket socket;
    protected InetAddress ipAddress;
    protected int port;
    protected FileInputStream inputStream;
    protected FileOutputStream outputStream;
    protected String rootPath = "./";
    private TFTPpacket request;
    private int fileIsExists = -1;

    public TFTPServiceHandler(TFTPread request, String tftpRootPath) {
        try {
            this.rootPath = tftpRootPath;
            this.socket = new DatagramSocket();
            this.socket.setSoTimeout(300000);//300s
            this.ipAddress = request.getAddress();
            this.port = request.getPort();
            this.request = request;
        } catch (Exception e) {
            e.printStackTrace();
            TFTPerror ePak = new TFTPerror(1, e.getMessage());
            try {
                ePak.send(this.ipAddress, this.port, this.socket);
            } catch (Exception localException1) {

            }
        }
    }

    public TFTPServiceHandler(TFTPwrite request, String tftpRootPath) {
        try {
            this.rootPath = tftpRootPath;
            this.socket = new DatagramSocket();
            this.socket.setSoTimeout(300000);
            this.ipAddress = request.getAddress();
            this.port = request.getPort();
            this.request = request;
        } catch (Exception e) {
//            e.printStackTrace();
            System.out.println("In the constructor:" + e.getMessage());
            TFTPerror ePak = new TFTPerror(1, e.getMessage());
            try {
                ePak.send(this.ipAddress, this.port, this.socket);
            } catch (Exception localException1) {
            }
        }
    }

    @Override
    public void run() {
        initialization();
        if (fileIsExists == 0) {
            System.out.println("run service...");
            service();
        }
    }

    //初始化,分离读写请求
    public void initialization() {
        String fileName = null;
        try {
            fileName = this.request.fileName();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        File path = new File(this.rootPath);
        if ((this.request instanceof TFTPread)) {
            try {
                if (!path.exists()) {
                    new File("tftpRoot").mkdir();
                    path.mkdir();
                    errorMessage("initialization Folder not found");
                    return;
                }
                String fileNameContent = new String(fileName.getBytes("GBK"), "iso-8859-1");
//                String localFilePathContent = new String(fileName.getBytes(), 0, fileName.length(), "GBK");
//                System.out.println("AAA " + fileNameContent + " - " );
                File srcFile = new File(this.rootPath + "/" + fileNameContent);
                System.out.println("initialization srcFile file path: " + srcFile.getAbsolutePath());
                if ((srcFile.exists()) && (srcFile.isFile())
                        && (srcFile.canRead())) {
                    this.inputStream = new FileInputStream(srcFile);
                    fileIsExists = 0;
                    return;
                }
                fileIsExists = 1;
                errorMessage("initialization TFTPread: File not found and" +
                        " rootPath: " + srcFile.getAbsolutePath());
            } catch (Exception e) {
                errorMessage(e);
                e.printStackTrace();
            }
        } else if ((this.request instanceof TFTPwrite))
            try {
                if (!path.exists()) {
                    new File("tftpRoot").mkdir();
                    path.mkdir();
                }
//                String fileNameContent = new String(fileName.getBytes("GBK"), "iso-8859-1");
//                System.out.println("AAA " + fileNameContent + " - " );
                File srcFile = new File(this.rootPath + "/" + fileName);
                System.out.println("Write file:" + srcFile.getAbsolutePath());
                if (srcFile.exists()) {
                    System.out.println(srcFile + " is exists!!!!");
                    errorMessage("service TFTPread: File not Found");
                    fileIsExists = 1;
                } else {
                    System.out.println(srcFile + " not exists,start write to service");
                    this.outputStream = new FileOutputStream(srcFile);
                    fileIsExists = 0;
                }
            } catch (Exception e) {
                errorMessage(e);
                e.printStackTrace();
            }
    }

    //实际读取与写入
    public void service() {
        System.out.println("service begin...");
        int bytesRead = TFTPpacket.maxTftpPakLen;
        if ((this.request instanceof TFTPread)) {
            try {
                System.out.println("service request is TFTPread");
                TFTPdata outPak = new TFTPdata(1, this.inputStream);
                bytesRead = outPak.getLength();
                outPak.send(this.ipAddress, this.port, this.socket);
                for (int blkNum = 2; bytesRead == TFTPpacket.maxTftpPakLen; blkNum++) {
                    TFTPpacket ack = TFTPpacket.receive(this.socket);
                    if ((ack instanceof TFTPerror))
                        break;
                    TFTPack tack = (TFTPack) ack;
                    int ackNumber = tack.blockNumber();
                    System.out.println("service ackNumber: " + ackNumber);
                    if (ackNumber == blkNum - 1) {
                        outPak = new TFTPdata(blkNum, this.inputStream);
                        bytesRead = outPak.getLength();
                        outPak.send(this.ipAddress, this.port, this.socket);
                    } else {
                        outPak.send(this.ipAddress, ack.getPort(), this.socket);
                        blkNum--;
                    }
                }
            } catch (NullPointerException e) {
                errorMessage("service TFTPread: File not Found");
                try {
                    this.inputStream.close();
                    this.socket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            } catch (Exception e) {
                errorMessage(e);
                try {
                    this.inputStream.close();
                    this.socket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            } finally {
                try {
                    this.inputStream.close();
                    this.socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            try {
                this.inputStream.close();
                this.socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else if ((this.request instanceof TFTPwrite)) {
            System.out.println("write request service method");
            TFTPdata p = null;
            TFTPpacket inPak = null;
            TFTPack ack = null;
            int count = 0;
            int loop = 3;
            try {
                ack = new TFTPack(0);
                ack.send(this.ipAddress, this.port, this.socket);
                int pakCount = 0;//这里需要从0开始,不是1,不然会少一帧4位
                for (int bytesOut = 512; bytesOut == 512; pakCount++) {
                    if ((inPak instanceof TFTPerror)) {
                        return;
                    }
                    if ((inPak instanceof TFTPdata)) {
                        p = (TFTPdata) inPak;
                        int blockNum = p.blockNumber();
                        if (blockNum == pakCount) {
                            count = 0;
                            bytesOut = p.write(this.outputStream);
                            ack = new TFTPack(blockNum);
                        } else {
                            if (count == 3) {
                                return;
                            }
                            count++;
                            pakCount--;
                            ack = new TFTPack(pakCount);
                        }
//                        System.out.println("write: bytesOut: " + bytesOut + " count:" + count);
                        ack.send(p.getAddress(), p.getPort(), this.socket);
                    }

                    loop = 3;
                    while (loop > 0) {
                        loop--;
                        try {
                            inPak = TFTPpacket.receive(this.socket);//超时处理
                            loop = -1;
                        } catch (Exception e) {
                            ack.send(p.getAddress(), p.getPort(), this.socket);
                        }
                    }
                }
            } catch (Exception e) {
                errorMessage(e);
            }
        }
    }

    public void start() {
        Thread serviceExecutor = new Thread(this);
        serviceExecutor.start();
    }

    public void errorMessage(String mesage) {
        TFTPerror ePak = new TFTPerror(1, mesage);
        try {
            ePak.send(this.ipAddress, this.port, this.socket);
        } catch (Exception localException) {
            System.out.println("errorMessage: " + localException);
        }
    }

    public void errorMessage(Exception e) {
        e.printStackTrace();
        TFTPerror ePak = new TFTPerror(1, e.getMessage());
        try {
            ePak.send(this.ipAddress, this.port, this.socket);
        } catch (Exception localException) {
        }
    }

    public void stop() {
        this.socket.close();
    }
}

TftpServer.java:(启动服务端)

import xxx.TFTPpacket;
import xxx.TFTPread;
import xxx.TFTPwrite;

import java.net.DatagramSocket;
import java.net.InetAddress;
/**
 * TFTP Server start
 *
 * @author: zhangyongtao
 * @date: 2022/7/10
 */
public class TftpServer implements Runnable {
    private static final String TAG = "TftpServer";
    private DatagramSocket socket;
    private int port = 69;
    private final String tftpRoot;

    public TftpServer(String tftpRootPath, int port) {
        this.tftpRoot = tftpRootPath;
        this.port = port;
    }

    public void start() {
        Thread serviceExecutor = new Thread(this);
        serviceExecutor.start();
    }

    public void stop() {
        this.socket.close();
    }

    @Override
    public void run() {
        initilize();
        service();
    }

    public void initilize() {
        try {
            this.socket = new DatagramSocket(this.port, InetAddress.getByName("192.168.xx.xx"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void service() {
        try {
            while (true) {
                TFTPpacket in = TFTPpacket.receive(this.socket);
                if (in != null) {
                    if ((in instanceof TFTPread)) {
                        System.out.println("Read request is received");
                        TFTPServiceHandler handler = new TFTPServiceHandler((TFTPread) in, this.tftpRoot);
                        handler.start();
                    }
                    if ((in instanceof TFTPwrite)) {
                        System.out.println("Write request is received");
                        TFTPServiceHandler handler = new TFTPServiceHandler((TFTPwrite) in, this.tftpRoot);
                        handler.start();
                        continue;
                    }
                } else {
                    System.out.println("null request");
                }
            }
        } catch (Exception localException) {

        }
    }

    public static void main(String[] args) {
        String path = "D:\\Program Files\\Tftpd64";//随意设置服务端目录
        int port = 169;//这里端口号设为了169,一般为69
        new TftpServer(path, port).start();
        System.out.println("TFTp Server listening in the port " + port + " \n tftp root path:" + path);
    }
}

以上就是服务端的编写,接下来是客户端Client的编写(主要是文件上传下载功能):

TFTPClient.java:

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

/**
 * TFTP Client
 *
 * @author: zhangyongtao
 * @date: 2022/7/13
 */
public class TFTPClient {
    private static final String TFTP_SERVER_ADDRESS = "192.168.xx.xx";
    private static final String TFTP_MODE = "octet";
    private static final int TFTP_SERVER_PORT = 169;
    private static final byte OP_RRQ = 1;
    private static final byte OP_WRQ = 2;
    private static final byte OP_DATA_PACKET = 3;
    private static final byte OP_ACK = 4;
    private static final byte OP_ERROR = 5;
    private static final int PACKET_SIZE = 516;

    private DatagramSocket datagramSocket;
    private InetAddress inetAddress;
    private byte[] requestByteArray;
    private byte[] bufferByteArray;
    private DatagramPacket inDatagramPacket;
    private DatagramPacket outDatagramPacket;

    /**
     * 从TFTP服务器上获取文件
     *
     * @param fileName         要获取的文件名字
     * @param writeToLocalPath 要写入的本地路径
     * @throws IOException 异常
     */
    private void get(final String fileName, final String writeToLocalPath) throws IOException {
        //准备IP
        inetAddress = InetAddress.getByName(TFTP_SERVER_ADDRESS);
        datagramSocket = new DatagramSocket();
        //创建下载请求数据包
        requestByteArray = createRequest(OP_RRQ, fileName, TFTP_MODE);
        //配置目标发送包
        outDatagramPacket = new DatagramPacket(requestByteArray, requestByteArray.length,
                inetAddress, TFTP_SERVER_PORT);
        //发送请求数据包RRQ
        datagramSocket.send(outDatagramPacket);
        //从TFTP服务端接收文件
        ByteArrayOutputStream byteArrayOutputStream = receiveFile();
        //向本地写入文件
        writeFile(byteArrayOutputStream, writeToLocalPath);
    }

    /*
     * 客户端向 TFTP 服务器发送写入请求(WRQ)。
     * 1、服务器收到客户端的 WRQ 请求后,同意该请求,返回 ACK 确认包。这里的确认包的数据编号为 0。
     * 2、客户端收到请求的确认包以后,得知服务器已经同意文件上传。客户端开始进行文件上传,向服务器发送文件信息数据。
     *    首先发送第 1 个 DATA 包,大小为 512 字节,此时包的数据编号为 1,因为步骤(2)中已经使用了数据编号 0。
     * 3、服务器收到客户端发来的数据编号为 1 的 DATA 包,并进行确认,向客户端发送数据编号为 1 的 ACK 包。
     * 4、一直到文件没有字节可读取发送
     * */
    private static String LOCAL_CHARSET = "GBK";
    // FTP协议里面,规定文件名编码为iso-8859-1
    private static String SERVER_CHARSET = "ISO-8859-1";

    private void put(final String fileName, final String localFilePath) throws IOException {
        //准备IP
        inetAddress = InetAddress.getByName(TFTP_SERVER_ADDRESS);
        datagramSocket = new DatagramSocket();

//        String fileNameContent = new String(fileName.getBytes("GBK"), "iso-8859-1");
//        new String(userName.getBytes("ISO-8859-1"), "UTF-8");
//        String fileNameContent = new String(fileName.getBytes(StandardCharsets.ISO_8859_1), "UTF-8");
//        String localFilePathContent = new String(localFilePath.getBytes("GBK"), "iso-8859-1");
//        System.out.println("" + fileNameContent + " - " + "localFilePathContent");
        //创建上传请求数据包
        requestByteArray = createRequest(OP_WRQ, fileName, TFTP_MODE);
        //配置目标发送包
        outDatagramPacket = new DatagramPacket(requestByteArray, requestByteArray.length,
                inetAddress, TFTP_SERVER_PORT);

        //发送请求数据包WRQ
        datagramSocket.send(outDatagramPacket);
        sendFile(localFilePath);
    }

    /*
     * TFTP data packet format           TFTP error packet
     * 2bytes   2bytes   max512bytes     2bytes 2bytes  string      1byte
     * OPCODE   BLOCK#    DATA           OPCODE ERRCODE ERR_message   0
     * */
    private void sendFile(String localFilePath) throws IOException {
        System.out.println("send File...");
        File file = new File(localFilePath);
        InputStream inputStream = new FileInputStream(file);
        System.out.println("send File length: " + file.length());
        DataInputStream dataInputStream = new DataInputStream(inputStream);
        int blockNum = 1;
        System.out.println("blockNum: " + blockNum);

        while (true) {
            bufferByteArray = new byte[PACKET_SIZE];
            inDatagramPacket = new DatagramPacket(bufferByteArray, bufferByteArray.length,
                    inetAddress, datagramSocket.getLocalPort());

            //从TFTP服务端接收数据包packet,接收到ACK码
            datagramSocket.receive(inDatagramPacket);
            //获取packet前4位数据OPCODE
            byte[] opCode = {bufferByteArray[0], bufferByteArray[1]};
            System.out.println("TFTP Packet bufferByteArray: " + bufferByteArray[0] +
                    ", " + bufferByteArray[1]);
//            System.out.println("BufferByteArray: " + Arrays.toString(bufferByteArray));
//            System.out.println("AAAAAAA  OP_CODE: " + opCode[1]);
            if (opCode[1] == OP_ERROR) {
                System.out.println("TFTP OP_Error");
                outputError();
            } else if (opCode[1] == OP_ACK) {
                System.out.println("TFTP OP_ACK");
                DatagramPacket datagramPacket;

                System.out.println("dataInputStream short: " + dataInputStream.available());
                bufferByteArray[0] = 0;
                bufferByteArray[1] = 3;
                bufferByteArray[2] = 0;
                put(2, (short) blockNum, bufferByteArray);
                dataInputStream.read(bufferByteArray, 4, bufferByteArray.length - 4);

                datagramPacket = new DatagramPacket(bufferByteArray, bufferByteArray.length,
                        inDatagramPacket.getAddress(), inDatagramPacket.getPort());
                System.out.println("blockNumber: " + bufferByteArray[2] + bufferByteArray[3]);
//                System.out.println("TFTP OP_ACK: " + Arrays.toString(bufferByteArray) + " length: " + bufferByteArray.length);
//                System.out.println("length: " + datagramPacket.getLength());
                datagramSocket.send(datagramPacket);

                //当流里面没有字节可读时退出
                if (dataInputStream.available() <= 0) {
                    System.out.println("dataInputStream.available() <= 0");
                    return;
                }
                blockNum++;
            }
        }

    }

    /*
     * RRQ、WRQ请求数据包格式:
     *  2bytes |  string  | 1byte | string | 1byte|
     *  OPCODE | FileName |  0    | mode   |   0  |
     * */
    private byte[] createRequest(final byte opcode, final String fileName, final String mode) {
        byte zeroByte = 0;
        //请求数据包长度
        int rrqByteLength = 2 + fileName.length() + 1 + mode.length() + 1;
        byte[] rrqByteArray = new byte[rrqByteLength];
        //开始填充
        int position = 0;
        rrqByteArray[position] = zeroByte;
        position++;
        rrqByteArray[position] = opcode;
        position++;
        for (int i = 0; i < fileName.length(); i++) {
            rrqByteArray[position] = (byte) fileName.charAt(i);
            position++;
        }
        rrqByteArray[position] = zeroByte;
        position++;
        for (int i = 0; i < mode.length(); i++) {
            rrqByteArray[position] = (byte) mode.charAt(i);
            position++;
        }
        rrqByteArray[position] = zeroByte;

        return rrqByteArray;
    }

    //写入本地
    private void writeFile(ByteArrayOutputStream byteArrayOutputStream, String writeToLocalPath) {
        try {
            OutputStream outputStream = new FileOutputStream(writeToLocalPath);
            System.out.println("TFTP Client write File path: " + writeToLocalPath);
            byteArrayOutputStream.writeTo(outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /*
     * TFTP data packet format           TFTP error packet
     * 2bytes   2bytes   max512bytes     2bytes 2bytes  string      1byte
     * OPCODE   BLOCK#    DATA           OPCODE ERRCODE ERR_message   0
     * */
    private ByteArrayOutputStream receiveFile() throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        int block = 1;
        do {
            System.out.println("TFTP Packet block: " + block);
            block++;
            bufferByteArray = new byte[PACKET_SIZE];
            inDatagramPacket = new DatagramPacket(bufferByteArray, bufferByteArray.length,
                    inetAddress, datagramSocket.getLocalPort());
            System.out.println("TFTP Packet count2: bufferByteArray length:" + bufferByteArray.length
                    + " inetAddress: " + inetAddress + " current port: " + datagramSocket.getLocalPort());

            //从TFTP服务端接收数据包packet
            datagramSocket.receive(inDatagramPacket);
            //获取packet前4位数据OPCODE
            byte[] opCode = {bufferByteArray[0], bufferByteArray[1]};
            System.out.println("TFTP Packet bufferByteArray: " + bufferByteArray[0] +
                    ", " + bufferByteArray[1]);
            if (opCode[1] == OP_ERROR) {
                System.out.println("TFTP OP_Error");
                outputError();
            } else if (opCode[1] == OP_DATA_PACKET) {
                System.out.println("TFTP Client received Data Packet");
                byte[] blockNumber = {bufferByteArray[2], bufferByteArray[3]};
                DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
                dataOutputStream.write(inDatagramPacket.getData(), 4,
                        inDatagramPacket.getLength() - 4);

                InetAddress address = inDatagramPacket.getAddress();
                int port = inDatagramPacket.getPort();
                SocketAddress socketAddress = inDatagramPacket.getSocketAddress();
                int length = inDatagramPacket.getLength();
                byte[] data = inDatagramPacket.getData();
                System.out.println("TFTP receive address: " + address + " port: " + port +
                        " socketAddress: " + socketAddress + " length: " + length + " data: " +
                        Arrays.toString(data));

                //向TFTP服务端发送确认码ACK,通知写入
                sendAcknowledgement(blockNumber);
            }
        } while (!isLastPacket(inDatagramPacket));

        return byteArrayOutputStream;
    }

    /*
     * 发送ACK确认码
     * ACK Packet: 2bytes:OPCODE  2bytes:Block number
     * */
    private void sendAcknowledgement(byte[] blockNumber) {
        byte[] ACK = {0, OP_ACK, blockNumber[0], blockNumber[1]};
        DatagramPacket datagramPacket = new DatagramPacket(ACK, ACK.length,
                inetAddress, inDatagramPacket.getPort());
        System.out.println("sendAcknowledgement: " + Arrays.toString(ACK));
        try {
            datagramSocket.send(datagramPacket);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //是否最后一个包
    private boolean isLastPacket(DatagramPacket inDatagramPacket) {
        return inDatagramPacket.getLength() < 512;
    }

    //输出错误信息
    private void outputError() {
        String code = new String(bufferByteArray, 3, 1);
        String errorText = new String(bufferByteArray, 4, inDatagramPacket.getLength() - 4);
        System.out.println("TFTP Client Error code: " + code + " text: " + errorText);
    }

    protected void put(int at, short value, byte[] bufferByteArray) {
        int atLocal = at;
        bufferByteArray[(atLocal++)] = (byte) (value >>> 8);
        //383  0001 1000 0000  256  127  0000 0001
        bufferByteArray[atLocal] = (byte) (value & 0xff);//取余
    }

    public static void main(String[] args) throws UnknownHostException {
        final String TFTP_SERVER_ADDRESS = "192.168.xx.xx";
        final String TFTP_MODE = "octet";
        final int TFTP_SERVER_PORT = 169;
        InetAddress inetAddress = InetAddress.getByName(TFTP_SERVER_ADDRESS);

        TFTPClient tftpClient = new TFTPClient();
        try {
            tftpClient.put("tiger.png", "F:\\老虎.png");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

客户端编写结束,以上,结束。代码有借鉴别人的(不全,全是片段和原理)…

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值