java网络编程之TFTP(二)

来源:互联网 发布:java redis作用 编辑:程序博客网 时间:2024/04/29 01:00

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();        }    }}
0 0
原创粉丝点击