基于tftp传输文件demo

tftp报文格式

RRQ 和WRQ包(代码分别为1和2)的格式:

2 bytes    string   1 byte string 1 byte

------------------------------------------------

| Opcode | Filename | 0 | Mode | 0 |


 

OP码为3:从第五个字节开始才是数据

2 bytes   2 bytes   n bytes

----------------------------------

| Opcode | Block # | Data |

 

 其他基本信息就不废话,直接贴码

服务端:


package com;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * @author zhaixx
 * @version 1.0
 * @category com
 */
public class ServiceAgint extends Thread
{

    // over the parent's run methord
    public void run()
    {
        try
        {
            DatagramSocket tftpd = new DatagramSocket(69); // tftp server socket
            byte[] buf = new byte[516]; // a buffer for UDP packet
            DatagramPacket dp = new DatagramPacket(buf, 516); // a UDP packet
            DataInputStream din = null;
            ClientAgent newClient = null;
            short tftp_opcode = 0; // opcode: the 2 bytes in the front of tftp packet
            String tftp_filename = null;
            String tftp_mode = null;
            while (true)
            {
                System.out.println("启动了");
                tftpd.receive(dp); // wait for a client
                int i = dp.getLength(); // 接收数据
                if (i > 0)
                { // 指定接收到数据的长度,可使接收数据正常显示,开始时很容易忽略这一点
                    buf = dp.getData(); // get the UDP packet data
                    // 处理数据
                    cleandata(buf, dp.getAddress(), dp.getPort());
                    i = 0;// 循环接收
                }
                din = new DataInputStream(new ByteArrayInputStream(buf));
                tftp_opcode = din.readShort(); // get the opcode
                { // get the filename
                    int fnoffset = 2;
                    int fnlen = 0;
                    while (din.readByte() != 0)
                    {
                        fnlen++; // filename will end with a null char('\0')
                    }
                    tftp_filename = new String(buf, fnoffset, fnlen);
                    // get the mode
                    int mdnoffset = fnoffset + fnlen + 2;
                    int mdnlen = 0;
                    while (din.readByte() != 0)
                    {
                        mdnlen++; // filename will end with a null char('\0')
                    }
                    tftp_mode = new String(buf, mdnoffset, mdnlen);
                }
                switch (tftp_opcode)
                {
                    case 1:
                        // RRQ
                    case 2:
                        // WRQ
                        newClient = new ClientAgent(dp.getAddress(), dp.getPort(),
                                tftp_opcode, tftp_filename, tftp_mode);
                        newClient.setDaemon(true);
                        newClient.start();
                        // System.out.println("debug: Main.main() --> a RRQ thread start ....");
                        break;
                }
            }
        }
        catch (Exception e)
        {
        }
    }

    /**
     * 处理数据
     * 
     * @param buf
     * @param address
     * @param port
     */
    private void cleandata(byte[] receiveByte, InetAddress address, int port)
    {
        System.out.println("开始了");
        try
        {
            DataInputStream din = null;
            ClientAgent newClient = null;
            short tftp_opcode = 0; // opcode: 每个数据包都有一个opcode(2个字节),表示包的类型
            String tftp_filename = null;// 文件名
            String tftp_mode = null;// 传输模式:netASCII 模式即 8 位 网络ASCII码
                                    // ;octet,即八位组模式(替代了以前版本的二进制模式),如原始八位字节
            din = new DataInputStream(new ByteArrayInputStream(receiveByte));
            System.out.println("tftp_opcode:"+din.readShort());
            tftp_opcode = din.readShort();
            short i = 2;
            if (1 == tftp_opcode)
            {
                System.out.println("[{}]端口[{}]暂不支持文件下载!" + address.toString() + "----"
                        + port);
            }
            else if ( i == tftp_opcode)
            {
                System.out.println("[{}]端口[{}]文件上传!"+address.toString()+"--"+port);
            //WRQ
            /*获取文件名,只有opcode=1,2才能获取到文件名,现在支持2上传文件
             * RRQ/WRQ包:
                --------------------------------------------------------------
                | Opcode | Filename |  0  |  Mode  |  0  |
                ---------------------------------------------------------------
                2 bytes   string    1 byte   string    1 byte
                RRQ和WRQ包(代码分别为1和2)的格式如上所示。文件名是NETASCII码字符,
                以0结束。 而MODE域包括了字符串"netascii"或"octet",名称不分大小写。
                接收到NETASCII格式数据的主机必须将数据转换为本地格式。OCTET模式用于传输文件,
                这种文件在源机上以8位格式存储。
             */
            int fnoffset = 2;//去除Opcode从2开始
            int fnlen = 0;//文件名的长度
            while (din.readByte() != 0) {
                fnlen++; //filename will end with a null char('\0')
            }
            tftp_filename = new String(receiveByte, fnoffset, fnlen);

            //get the mode
            int mdnoffset = fnoffset + fnlen + 2;
            int mdnlen = 0;
            while (din.readByte() != 0) {
                mdnlen++; //mode will end with a null char('\0')
            }
            tftp_mode = new String(receiveByte, mdnoffset, mdnlen);
            
            System.out.println("[{}]端口[{}]文件上传[{}]模式[{}]"+address.toString()+"---"+port+"---"+tftp_filename+"---"+tftp_mode);
            
            newClient = new ClientAgent(address, port, tftp_opcode, tftp_filename, tftp_mode);
            newClient.run();
        }else{
            System.out.println("暂不支持!:"+     String.valueOf(receiveByte));
            System.out.println("tftp_opcode:"+din.readShort());
        }
        }
        catch (Exception e)
        {
        }
    }
    
    
    public static void main(String[] args) {
        ServiceAgint agetn  = new ServiceAgint();
        agetn.run();
    }
}
 

 

 

客户端:


package com;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;

/**
 * @author zhaixx
 * @version 1.0
 * @category com
 */
public class ClientAgent extends Thread
{

    private InetAddress m_ClientAddress; // ip of the client
    private int m_ClientPort; // port of the client
    private DatagramSocket m_so_tftp; // the socket object send or get message
    private short m_curopcode; // the current opcode( wrq/rrq )
    private String m_filename;
    private String m_mode;
    private final int m_MAX_nTimeOut = 5;

    public ClientAgent(InetAddress ip, int port, short opcode, String fname, String mode)
    {
        this.m_ClientAddress = ip;
        this.m_ClientPort = port;
        this.m_curopcode = opcode;
        this.m_filename = fname;
        this.m_mode = mode;
    }

    /**
     * 创建客户端发送给服务端的tftp的udp连接
     */
    private boolean createTftp()
    {
        // 如果端口被占用或者超时,允许重试10次,重新找10个端口
        int nfail = 10;
        while (nfail-- > 0)
        {
            int port = 9191;
            try
            {
                if (port == 0)
                {
                }
                else
                {
                    this.m_so_tftp = new DatagramSocket(port);
                }
                break; // get a random port number
            }
            catch (SocketException e)
            {
            }
            catch (IllegalArgumentException e1)
            {
                System.out.println("[{}]端口[{}]文件[{}]服务器没有可用端口[{}]了" + m_ClientAddress
                        + "--" + m_ClientPort + "--" + m_filename + "--" + port);
                e1.printStackTrace();
            }
        }
        if (this.m_so_tftp == null)
        {
            System.out.println("kong");
            return false;
        }
        return true;
    }

    public void run()
    {
        if (!createTftp())
        {
            return;
        }
        switch (this.m_curopcode)
        {
            case 1:
                // RRQ
                // this.RRQ();
                break;
            case 2:
                // WRQ
                this.WRQ();
                // System.out.println("debug: tftpClientAgent.run() --> a WRQ ended ...");
                break;
        }
        this.m_so_tftp.close();
    }

    // wait for data packet
    private DatagramPacket waitForData()
    {
        int ntimeout = 1;
        DatagramPacket dp = null;
        byte[] buf = new byte[516];
        this.initZeroByteArray(buf);
        dp = new DatagramPacket(buf, 516);
        while (ntimeout > 0)
        {
            try
            {
                this.m_so_tftp.setSoTimeout(1000);
                this.m_so_tftp.receive(dp);
                System.out.println("dp.getLength()" + dp.getLength());
                break;
            }
            catch (SocketTimeoutException e1)
            {
                ntimeout--;
                e1.printStackTrace();
            }
            catch (IOException e2)
            {
                ntimeout--;
                e2.printStackTrace();
            }
        }
        return (ntimeout > 0 ? dp : null);
    }

    // init a byte[] with 0s
    private void initZeroByteArray(byte[] buf)
    {
        for (int i = 0; i < buf.length; i++)
        {
            buf[i] = 0;
        }
    }

    public void WRQ()
    {
        int ntimeout = this.m_MAX_nTimeOut;
        try
        {
            short nblock = 0;
            // send the #0 block ACK to start the transfer
            this.SendACK(nblock++);
            // wait for the data packet
            while (ntimeout > 0)
            {
                DatagramPacket dp;
                dp = this.waitForData();
                System.out.println("dp.getAddress():" + dp.getAddress());
                System.out.println("dp.getPort():" + dp.getPort());
                if (dp == null)
                {
                    ntimeout--;
                }
                else
                {
                    if (!((dp.getAddress().equals(this.m_ClientAddress)) && (dp.getPort() == this.m_ClientPort)))
                    {
                        ntimeout--;
                    }
                    else
                    { // right ip and port
                        // get the opcode
                        System.out.println("开始解析");
                        DataInputStream din = new DataInputStream(
                                new ByteArrayInputStream(dp.getData()));
                        int opcode = din.readShort();
                        // System.out.println("debug: the opcode is " + opcode);
                        if (opcode != 3)
                        {
                            // 不是03号data包
                            if (opcode == 2 && nblock == 1)
                            {
                                // 是02号WRQ包,且正在等待#1号data包
                                // 此时收到WRQ,说明#0号ACK有可能失败了,重发
                                this.SendACK((short) (nblock - 1));
                            }
                            else
                            {
                                this.SendERROR((short) 4, "非法的TFTP操作");
                            }
                            ntimeout--;
                        }
                        else
                        { // data packet
                            int nblk = din.readShort();
                            // " -- the curBlock is #" + nblk);
                            if (nblk != nblock)
                            {
                                // 不是期待的块号,重发上一个包的ACK,超时计数减1
                                System.out.println("+++++++++++++++++++++++nblk:" + nblk
                                        + "=s" + String.valueOf(nblk != nblock) + nblock);
                                this.SendACK((short) (nblock - 1));
                                ntimeout--;
                            }
                            else
                                System.out.println("------------nblk:" + nblk);
                            { // the right packet waiting for
                                if (!this.SaveFile(nblock, dp, this.m_filename,
                                        this.m_mode))
                                {
                                    ntimeout = 0;
                                    this.SendERROR((short) 3, "磁盘满或超过分配的配额");
                                }
                                else
                                {
                                    // send the current block ACK
                                    this.SendACK(nblock);
                                    if (dp.getLength() - 4 >= 512)
                                    {
                                        // the transfer isn't ended
                                        nblock++; // wait for the next block
                                        ntimeout = this.m_MAX_nTimeOut; // 重置超时记数
                                    }
                                    else
                                    {
                                        // ok, there is no more data, the transfer succ
                                        ntimeout = 1;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        catch (Exception e)
        {
        }
    }

    // send an Error packet with special ErrCode and ErrMsg
    private void SendERROR(short ErrCode, String ErrMsg)
    {
        byte[] bufERROR;
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        DataOutputStream dout = new DataOutputStream(bout);
        try
        {
            dout.writeShort(5); // opcode
            dout.writeShort(ErrCode);
            dout.write(ErrMsg.getBytes());
            dout.writeByte(0); // end of the ErrMsg
            bufERROR = bout.toByteArray();
            // construct a UDP packet
            DatagramPacket dpERROR = new DatagramPacket(bufERROR, bufERROR.length,
                    this.m_ClientAddress, this.m_ClientPort);
            // construct a UDP packet and send it
            this.m_so_tftp.send(dpERROR);
        }
        catch (IOException ex)
        {
        }
    }

    /**
     * | Opcode | Block | ----------------------- 2 bytes 2 bytes
     * 
     * @param nblock
     */
    // send an ACK packet with special block number
    private void SendACK(short nblock)
    {
        System.out.println("*******nblock:" + nblock);
        byte[] bufACK;
        ByteArrayOutputStream bout = new ByteArrayOutputStream(4);
        DataOutputStream dout = new DataOutputStream(bout);
        try
        {
            dout.writeShort(4); // opcode
            dout.writeShort(nblock); // block number
            bufACK = bout.toByteArray();
            // construct a UDP packet
            DatagramPacket dpACK = new DatagramPacket(bufACK, 4, this.m_ClientAddress,
                    this.m_ClientPort);
            // construct a UDP packet and send it
            this.m_so_tftp.send(dpACK);
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
    }

    /**
     * @param nblock
     * @param dp
     * @param fpath
     * @param mode
     * @return
     */
    // save the data into the special file
    private boolean SaveFile(int nblock, DatagramPacket dp, String fpath, String mode)
    {
        // ignore the mode, use the binary mode will keep the data away from being changed
        System.out.println("fpath:" + fpath);
        boolean isSucc = false;
        if (nblock < 1)
        {
            return false;
        }

        //  传输的文件
        File f = new File("D:\\tee.xml");
        if (!f.exists())
        {
            // file not exists
            try
            {
                f.createNewFile(); // create it
            }
            catch (IOException ex)
            {
                ex.printStackTrace();
                return false;
            }
        }
        RandomAccessFile rf;
        try
        {
            rf = new RandomAccessFile(f, "rw");
        }
        catch (FileNotFoundException ex)
        {
            ex.printStackTrace();
            return false;
        }
        byte[] buf = dp.getData(); // get the data buffer
        int buflen = dp.getLength();
        try
        {
            rf.seek((nblock - 1) * 512); // move the file pointer to last write position
            rf.write(buf, 4, buflen - 4); // write the data
            rf.setLength((nblock - 1) * 512 + buflen - 4); // reset the file size
            isSucc = true;
            rf.close();
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
            return false;
        }
        return isSucc;
    }
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值