java网络编程之TFTP(二)

java网络编程之TFTP(二)

今天在这里贴一个TFTP客户端实现的实例,希望可以帮助到有需要的人。【标准服务器端大家自行下载,推荐tftpd32】

首先构建一个TftpSocket类(基于UDP通信):

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
class TftpSocket {
      private static final String Server_IP ="127.0.0.1";   //指明服务器IP
      private static final int Server_port =69;            //指明服务器监听端口
      private static final byte RRQ = 1;                  //请求读
      private static final byte DAT = 3;                  //文件数据
      private static final byte ACK = 4;                  //确认,继续进行传输
      private static final byte ERROR = 5;                //发生错误
      private static final int PACKET_SIZE = 516;         //数据为0~512字节,加上2字节的操作码和2字节的块编号
      private DatagramSocket datagramSocket = null;       //基于UDP,所以使用DatagramSocket
      private InetAddress address = null;                 
      private byte[] requestArray;                       //数据包数组
      private byte[] buf;                                //数据缓存区
      private DatagramPacket outDatagramPacket;          //发出数据包
      private DatagramPacket inDatagramPacket;           //接收数据包

      //该方法设置为public属性,作为与主程序的接口,通过传入文件名进行获取文件
      public void get(String fileName) throws Exception {
        address = InetAddress.getByName(Server_IP);     //使用InetAddress的静态方法getByName(String host)得到服务器的IP地址    
        datagramSocket = new DatagramSocket();        //基于UDP数据报,所以用到DatagramSocket
        requestArray = createRequest(RRQ, fileName, "octet"); //通过createRequest(RRQ, fileName, "octet")方法得到一个请求读报文
        outDatagramPacket = new DatagramPacket(requestArray,requestArray.length, address, Server_port); //发到服务器的数据包
        datagramSocket.send(outDatagramPacket);    
        ByteArrayOutputStream byteOut = receiveFile();  //利用receiveFile()从服务器接收文件
        writeFile(byteOut, fileName);   //利用writeFile()把文件写到当地磁盘
    } 

      //该方法用于从服务器接收文件,保存到一个字符数组并返回
      private ByteArrayOutputStream receiveFile() throws Exception{
        ByteArrayOutputStream byteOutOS = new ByteArrayOutputStream();
        int block = 1;
        do {
            System.out.println("接收到第"+block+"个数据包");   //block最为一个计数器,计算收到的数据包
            block++;
            buf = new byte[PACKET_SIZE];   //设置数据缓冲区
            inDatagramPacket = new DatagramPacket(buf,buf.length, address,datagramSocket.getLocalPort()); 
            datagramSocket.receive(inDatagramPacket);
            byte[] opCode = { buf[0], buf[1] };   //获取接收报文中前两个字节的操作码
            if (opCode[1] == ERROR) {
                reportError();
            } else if (opCode[1] == DAT) {
                byte[] blockNumber = { buf[2], buf[3] };   //获取接收报文中操作码之后的两个字节的块编号
                DataOutputStream dos = new DataOutputStream(byteOutOS);
                dos.write(inDatagramPacket.getData(), 4,inDatagramPacket.getLength() - 4);
                sendAcknowledgment(blockNumber);    //发送ACK,确认收到该块编号的报文
            }
        } while (!isLastPacket(inDatagramPacket));
        System.out.println("文件接收完毕!!");
        return byteOutOS;
    }

    //该方法是发送ACK数据包,用于确认收到该块编号的数据包
    private void sendAcknowledgment(byte[] blockNumber){
        byte[] ACKArray = { 0, ACK, blockNumber[0], blockNumber[1] };  
        DatagramPacket ack = new DatagramPacket(ACKArray, ACKArray.length,address,
                inDatagramPacket.getPort());    //ACK数据包
        try {
            datagramSocket.send(ack);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    //该方法用于将接收到的文件写入到本地磁盘中
    private void writeFile(ByteArrayOutputStream b, String fileName){
            try {
                OutputStream outputStream = new FileOutputStream(fileName);
                b.writeTo(outputStream);  // 将此 byte 数组输出流的全部内容写入到指定的输出流参数中
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }
    private boolean isLastPacket(DatagramPacket datagramPacket) {  //判断是否是最后一个数据包
        if (datagramPacket.getLength() < 512)
            return true;
        else
            return false;
    }

    //该方法为构造请求读/写数据包(包括操作码、文件名、模式串)
    private byte[] createRequest(final byte opCode, final String fileName,
            final String mode) {
        byte zeroByte = 0;  
        int ByteLength = 2 + fileName.length() + 1 + mode.length() + 1; //文件名和模式串都是以0字节终止
        byte[] ByteArray = new byte[ByteLength];
        int position = 0;      
        ByteArray[position] = zeroByte;
        position++;
        ByteArray[position] = opCode;            //设置操作码(读或写)
        position++;
        for (int i = 0; i < fileName.length(); i++) {
            ByteArray[position] = (byte) fileName.charAt(i);  //返回指定索引处的 char 值,强转为byte类型

            position++;
        }
        ByteArray[position] = zeroByte;       //文件名以0字节作为终止
        position++;
        for (int i = 0; i < mode.length(); i++) {
            ByteArray[position] = (byte) mode.charAt(i);  //返回指定索引处的 char 值,强转为byte类型
            position++;
        }
        ByteArray[position] = zeroByte;       //模式以0字节作为终止
        return ByteArray;
    }
    //该方法为显示差错码和差错信息
     private void reportError() {
        String errorCode = new String(buf, 3, 1);                 //获取差错码
        String errorText = new String(buf, 4,inDatagramPacket.getLength() - 4);  //获取差错信息
        System.err.println("Error: " + errorCode + " " + errorText);
    }
}

这个是一个简单的测试方法,可以从服务器端指定的位置下载到需要的文件:


public class TftpTest {
    public static void main(String[] args) {
        String FileName ="doc1.txt";
        TftpSocket s= new TftpSocket();
        try {
            s.get(FileName);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值