关闭

TFTP class TFTP 实现类

843人阅读 评论(0) 收藏 举报

从书上抄来的TFTP协议,终于调试到可以运行,已发现但未解决的问题:

1文件名编码装换不明确,不知是否需要转换,如过用ISO-8859-1转换中文文件名会出错.

2UDP用wireshark截包UDP校验和出现错误:提示:Checksum: 0x0020 [incorrect, should be 0x131f (maybe caused by "UDP checksum offload"?)]

代码作者写上自己名字了,不好意思了...Laughing

注:源程序摘自Al Williams的<>,中国水利水电出版社出版.ISBN 7-5084-1152-8

 

// TFTP Class TftpSocket.java -- AD_Li
package net.ad_li.network;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

/**
 * A simple class for TFTP,can be use as server and client.
 * 
 * Use UDP as lower level protocol.
 * 
 * Super class:{
@link DatagramSocket}
 * 
 * 
@author AD
 
*/

public class TftpSocket extends DatagramSocket implements Runnable {
    
/** DEBUG FLAG set it to true to print the debug information. */
    
private static final boolean DEBUG = false;
    
/** Type of TFTP packet(local code none). */
    
protected final static int NONE = 0;
    
/** Type of TFTP packet(read request). */
    
protected final static int RRQ = 1;
    
/** Type of TFTP packet(write request). */
    
protected final static int WRQ = 2;
    
/** Type of TFTP packet(data). */
    
protected final static int DAT = 3;
    
/** Type of TFTP packet(acknowledgment). */
    
protected final static int ACK = 4;
    
/** Type of TFTP packet(error). */
    
protected final static int ERR = 5;
    
/** Type of TFTP packet(local code unknown). */
    
protected final static int UNKNOWN = 6;
    
/** Type of TFTP packet(local code timeout). */
    
protected static int TIMEOUT = 7;
    
/** File name in string form. */
    
protected String m_fNameString;
    
/** File name in byte array form. */
    
protected byte[] m_fNameByte;
    
/** File type in byte array form. */
    
protected byte[] m_fType;
    
/** Host Address use in TFTP client. */
    
protected InetAddress m_hostAddr;
    
/** Port use in TFTP server. */
    
protected int m_serverPort = 69;
    
/** Data bytes for send. */
    
protected byte[] m_bytesOut = new byte[516];
    
/** Data bytes for receive. */
    
protected byte[] m_bytesIn = new byte[1024];
    
/** The sequence number of the packet. */
    
protected int m_block;
    
/** Input stream. */
    
protected FileInputStream m_fileIn;
    
/** Output stream. */
    
protected FileOutputStream m_fileOut;
    
/** Flag of end of files */
    
protected boolean m_EOF = false;
    
/** Operation request(use in TFTP server as a flag of server type). */
    
protected int m_serverCode = 0;
    
/** Error output stream */
    
protected PrintStream m_errOut = System.out;

    
/**
     * 
@param fName
     *                file name
     * 
@param fType
     *                file type
     * 
@param host
     *                host name can be URL or IP address
     
*/

    
public TftpSocket(String fName, String fType, String host)
        
throws UnknownHostException, SocketException {
    create(fName, fType, host);
    }


    
/**
     * 
     * 
@param fName
     *                file name
     * 
@param fType
     *                file type
     * 
@param host
     *                host name can be URL or IP address
     * 
@param serPort
     *                local port for server
     * 
@param serAddress
     *                local address for server
     
*/

    
public TftpSocket(String fName, String fType, String host, int serPort,
        InetAddress serAddress) 
throws UnknownHostException,
        SocketException 
{
    
super(serPort, serAddress);
    create(fName, fType, host);
    }


    
/**
     * private constructor(used by server only)
     
*/

    
private TftpSocket() throws SocketException {
    }


    
/**
     * 
     * 
@param fName
     *                file name
     * 
@param fType
     *                file type
     * 
@param host
     *                host name can be URL or IP address
     
*/

    
private void create(String fName, String fType, String host)
        
throws UnknownHostException, SocketException {
    
// try {// try get the file name by ISO-8859-1 and convert it into bytes
    
// m_fNameByte = fName.getBytes("ISO-8859-1");
    
// } catch (Exception exce) {
    
// exce.printStackTrace(m_errOut);
    
// m_fNameByte = fName.getBytes();
    
// }//TODO:here may be need some kind of code transformation
    m_fNameByte = fName.getBytes();
    
// get the file name in string form
    m_fNameString = fName;
    
// get the file type
    m_fType = fType.getBytes();

    
// set 1.5 second time-out
    setSoTimeout(1500);

    
// get the host Address
    m_hostAddr = InetAddress.getByName(host);
    }


    
/**
     * Send a specific error packet.
     * 
     * 
@param code
     *                error code
     * 
@param msg
     *                error message
     * 
@return if the error packet send successful
     
*/

    
protected boolean sendErrPacket(int code, String msg) {
    
// setup the error tag in packet
    m_bytesOut[0= (byte) ((ERR >> 8& 0xFF);
    m_bytesOut[
1= (byte) (ERR & 0xFF);
    
// setup the error code in packet
    m_bytesOut[2= (byte) ((code >> 8& 0xFF);
    m_bytesOut[
3= (byte) (code & 0xFF);
    
// setup data in packet
    System.arraycopy(msg.getBytes(), 0, m_bytesOut, 4, msg.length());
    
// setup the end in packet
    m_bytesOut[4 + msg.length()] = 0;
    
// make up the whole packet
    DatagramPacket pack = new DatagramPacket(m_bytesOut, 5 + msg.length(),
        m_hostAddr, m_serverPort);

    
// try to send the packet
    try {
        send(pack);
    }
 catch (Exception exce) {// send fail
        exce.printStackTrace(m_errOut);
        
return false;
    }

    
// send success
    return true;
    }


    
/**
     * Send a specific acknowledgment.
     * 
     * 
@param block
     *                the sequence number of the packet.
     * 
@return if the packet send successful
     
*/

    
protected boolean sendAck(int block) {
    
// setup the ACK tag in packet
    m_bytesOut[0= (byte) ((ACK >> 8& 0xFF);
    m_bytesOut[
1= (byte) (ACK & 0xFF);
    
// setup the sequence number in packet
    m_bytesOut[2= (byte) ((block >> 8& 0xFF);
    m_bytesOut[
3= (byte) (block & 0xFF);

    
// setup the whole packet
    DatagramPacket pack = new DatagramPacket(m_bytesOut, 4, m_hostAddr,
        m_serverPort);
    
// try to send the packet
    try {
        send(pack);
    }
 catch (Exception exce) {// send fail
        exce.printStackTrace(m_errOut);
        
return false;
    }

    
// send success
    return true;
    }


    
/**
     * Send a packet (generic error or current block ACK)
     * 
     * 
@param packType
     *                packet type
     * 
@return if the packet send successful
     
*/

    
protected boolean sendPacket(int packType) {
    
// packet length
    int packLen = 2;
    
// setup packet type tag in packet
    m_bytesOut[0= (byte) ((packType >> 8& 0xFF);
    m_bytesOut[
1= (byte) (packType & 0xFF);

    
// deal with each type of packet
    switch (packType) {
    
case RRQ:// deal with RRQ packet
    case WRQ:// deal with WRQ packet
        
// setup the file in packet
        System.arraycopy(m_fNameByte, 0, m_bytesOut, 2, m_fNameByte.length);
        m_bytesOut[
2 + m_fNameByte.length] = 0;
        
// setup the file type in packet
        System.arraycopy(m_fType, 0, m_bytesOut, 3 + m_fNameByte.length,
            m_fType.length);
        m_bytesOut[
3 + m_fNameByte.length + m_fType.length] = 0;
        packLen 
+= m_fNameByte.length + m_fType.length + 2;
        
break;
    
case DAT:// deal with DAT packet
    case ACK:// deal with ACK packet
        
// setup sequence number in packet
        m_bytesOut[2= (byte) ((m_block >> 8& 0xFF);
        m_bytesOut[
3= (byte) (m_block & 0xFF);
        packLen 
+= 2;
        
// more for file
        if (packType == DAT) {// deal with DAT packet
        
// file length
        int fLen;
        
// file buffer
        byte[] fBuf = new byte[512];
        
try {// try to load the packet data
            fLen = m_fileIn.read(fBuf, 0512);
        }
 catch (IOException ioExce) {// load packet data fail
            fLen = 0;// assume EOF
            ioExce.printStackTrace();
        }

        
if (fLen < 0)
            
/*
             * if load packet fail(like load empty file),the fLen will
             * be -1,then the array copy will fail,so turn it into zero
             
*/

            fLen 
= 0;
        
// copy the data into packet
        System.arraycopy(fBuf, 0, m_bytesOut, packLen, fLen);
        packLen 
+= fLen;
        
if (fLen != 512)
            m_EOF 
= true;
        }

        
break;
    
case ERR:// deal with ERR packet
        m_bytesOut[2= m_bytesOut[3= m_bytesOut[4= 0;
        packLen 
= 5;
        
break;
    
default:// unknown packet type, send fail
        return false;
    }


    
// setup the whole packet
    DatagramPacket pack = new DatagramPacket(m_bytesOut, packLen,
        m_hostAddr, m_serverPort);
    

    
if (DEBUG) {// DEBUG information
        System.out.println("sending_packet " + pack.getAddress() + ":"
            
+ pack.getPort() + " packet_code=" + packType);
    }


    
try {// try to send the packet
        send(pack);
    }
 catch (Exception exce) {// send fail
        exce.printStackTrace(m_errOut);
        
return false;
    }

    
// send success
    return true;
    }


    
/**
     * Process a received packet
     * 
     * 
@return type of packet
     
*/

    
protected int rcvPacket() throws SocketException {
    DatagramPacket pack 
= new DatagramPacket(m_bytesIn, 1024);
    
// receive packet status
    boolean recStatus = false;
    
try {// try to receive the packet
        receive(pack);
        recStatus 
= true;
    }
 catch (IOException ioExce) {
        ioExce.printStackTrace(m_errOut);
    }

    
if (!recStatus)
        
return TIMEOUT;

    
// deal with each type of packet
    m_serverPort = pack.getPort();
    
// data in the packet
    byte[] bytData = pack.getData();
    
// get the packet type
    int packType = (bytData[0<< 8+ bytData[1];
    
// the sequence number of block in the packet received

    
if (DEBUG) {// DEBUG information
        System.out.println("packet receive " + pack.getAddress() + ":"
            
+ pack.getPort() + " packet code=" + packType);
    }


    
int blockIn;
    
switch (packType) {
    
case RRQ:// deal with RRQ packet
    case WRQ:// deal with RRQ packet
        
// do nothing, things has been done in superior function
        return packType;
    
case ACK:// check block #
        blockIn = (bytData[2<< 8| (bytData[3& 0xFF);

        
if (DEBUG) {// DEBUG information
        System.out.println("block number=" + m_block
            
+ " blockIn number=" + blockIn);
        }


        
if (blockIn != m_block)
        
// block received is not the same as the socket (check fail)
        return NONE;
        
++m_block;
        
return ACK;
    
case DAT:// check block # and store
        blockIn = (bytData[2<< 8| (bytData[3& 0xFF);

        
if (DEBUG) {// DEBUG information
        System.out.println("block numer=" + m_block
            
+ " blockIn number=" + blockIn);
        }


        
if (blockIn > m_block)// some packet may be lost
        return UNKNOWN;
        
if (blockIn < m_block) {// the ACK packet may lost, so send it again
        sendAck(blockIn);
        
return NONE;
        }

        
// block number matches
        try {
        m_fileOut.write(bytData, 
4, pack.getLength() - 4);
        }
 catch (IOException ioExce) {// write fail
        ioExce.printStackTrace(m_errOut);
        }


        
// the file is end
        if (pack.getLength() < 516)
        m_EOF 
= true;
        
// ACK current
        sendPacket(ACK);
        
++m_block;
        
return DAT;
    
case ERR:// do something with the error message
        String errMsg = new String(bytData, 4, pack.getLength());
        m_errOut.println(errMsg);
        
return ERR;
    
default:// unknown packet type
        return UNKNOWN;
    }

    }


    
/**
     * Send a file
     * 
     * 
@return send file success or not
     
*/

    
public boolean fsend() throws SocketException {

    
try {// try to get the file
        m_fileIn = new FileInputStream(m_fNameString);
    }
 catch (Exception exce) {// get the file fail
        exce.printStackTrace(m_errOut);
        
return false;
    }


    
// send success or not
    boolean rv;
    
try {// try to send the packet
        
// retry time
        int retry = 0;
        
// set block number to 0
        m_block = 0;
        rv 
= sendPacket(WRQ);
        
if (!rv) // send fail
        return rv;
        
// receive packet state
        int recState;
        
while (!m_EOF) {
        
do {// send the packet until receive the ACK packet
            recState = rcvPacket();
            
if (recState == TIMEOUT) {
            
if (++retry > 5// timeout too many times, send fail
                return false;
            
if (m_block != 0)
                
if (!sendPacket(DAT))// send the DAT packet again
                return false;
            
continue;
            }

            
// reset retry count
            retry = 0;
            
if (recState == ERR)// receive error packet, send fail
            return false;
            
if (recState == UNKNOWN) {// receive unknown packet, send
            
// fail
            sendErrPacket(0"Unknown error");
            
return false;
            }

        }
 while (recState != ACK);
        
if (!sendPacket(DAT))// send packet fail
            return false;
        }

        
// send success
        return true;
    }
 finally {// close the stream no matter what
        streamClose(m_fileIn);
    }

    }


    
/**
     * Get a file
     * 
     * 
@return get file success or not
     
*/

    
public boolean freceive() throws SocketException {
    
// send success or not
    boolean rv;
    
// receive packet state
    int recState;

    
try {// try to save the file
        m_fileOut = new FileOutputStream(m_fNameString);
    }
 catch (Exception exce) {// save the file fail
        exce.printStackTrace(m_errOut);
        
return false;
    }


    
try {// try to receive the packet
        
// retry time
        int retry = 0;
        
// set the block number to 1
        m_block = 1;
        rv 
= sendPacket(RRQ);
        
if (!rv)// send packet fail
        return rv;
        
while (!m_EOF) {
        
do {// try get the packet until receive the DAT packet
            recState = rcvPacket();
            
if (recState == TIMEOUT) {
            
if (++retry > 5)// timeout too many times, get fail
                return false;
            
continue;
            }

            
// reset retry count
            retry = 0;
            
if (recState == ERR) // receive the ERR packet, get fail
            return false;
            
if (recState == UNKNOWN) {// receive unknown packet, send
            
// fail
            sendErrPacket(0"Unknown error");
            
return false;
            }

        }
 while (recState != DAT);
        }

        
// get success
        return true;
    }
 finally {// close stream in all cases
        if (!streamClose(m_fileOut))
        
return false;
    }

    }


    
/**
     * Handy function to close a file input stream.
     * 
     * 
@param inStrem
     *                the file input stream to close
     * 
@return close success or fail
     
*/

    
private boolean streamClose(FileInputStream fInStream) {
    
try {// try to close the stream
        fInStream.close();
    }
 catch (Exception exce) {// close stream fail
        exce.printStackTrace(m_errOut);
        
return false;
    }

    
// close success
    return true;
    }


    
/**
     * Handy function to close a file output stream.
     * 
     * 
@param fOutStream
     *                the file output stream to close
     * 
@return success or fail
     
*/

    
private boolean streamClose(FileOutputStream fOutStream) {
    
try {// try to close the stream
        fOutStream.close();
    }
 catch (Exception exce) {// close stream fail
        exce.printStackTrace(m_errOut);
    }

    
// send success
    return true;
    }


    
/**
     * Server thread
     
*/

    
public void run() {
    
if (m_serverCode == WRQ) {// deal with the WRQ request
        try {// try setup the output stream
        m_fileOut = new FileOutputStream(m_fNameString);
        }
 catch (IOException exce) {// can't get the file
        sendErrPacket(2"Access violation");
        
return;
        }


        
// send ACK 0
        sendAck(0);
        
// set block number to 1
        m_block = 1;
        
// wait for DATA
        int recState;// receive state
        do {// while the file is not end receive the packet
        try {// try receive the packet
            recState = rcvPacket();
        }
 catch (SocketException sockExce) {// receive fail
            sockExce.printStackTrace();
            recState 
= ERR;
        }

        
if (recState == ERR) {
            
// close the output stream
            streamClose(m_fileOut);
            
return;
        }

        
if (recState != DAT && recState != NONE) {
            
// close the output stream
            streamClose(m_fileOut);
            sendErrPacket(
0"?");
            
return;
        }

        }
 while (!m_EOF);
        
// close the output stream
        streamClose(m_fileOut);
    }

    
if (m_serverCode == RRQ) {// deal with RRQ packet
        try {// try to setup input stream
        m_fileIn = new FileInputStream(m_fNameString);
        }
 catch (IOException ioExce) {// can't get the file

        
if (DEBUG) {// DEBUG information
            ioExce.printStackTrace(m_errOut);
        }


        sendErrPacket(
1"File not found.");
        
return;
        }


        
// send DATA
        m_block = 1;
        
// retry time
        int retry = 0;
        
do {
        
int recState;
        
if (!sendPacket(DAT)) {// send packet fail
            sendErrPacket(0"?");
            
// close the input stream
            streamClose(m_fileIn);
            
return;
        }

        
do {
            
try {// try to get the ACK
            recState = rcvPacket();
            }
 catch (SocketException sockExce) {
            sockExce.printStackTrace(m_errOut);
            recState 
= ERR;
            }

            
if (recState == ERR) {
            
// close the input stream
            streamClose(m_fileIn);
            
return;
            }

            
if (recState == UNKNOWN || recState == RRQ
                
|| recState == WRQ || recState == DAT) {
            sendErrPacket(
4"Illegal");
            
// close the input stream
            streamClose(m_fileIn);
            
return;
            }

            
if (recState == TIMEOUT && ++retry > 5{
            
// time out and retry too many times
            sendErrPacket(0"TIMEOUT");
            
// close the input stream
            streamClose(m_fileIn);
            
return;
            }

            
if (recState == ACK) {
            
// ++m_block;
            
// reset retry count to 0
            retry = 0;
            }

        }
 while (recState != ACK && recState != TIMEOUT);
        }
 while (!m_EOF);
        
// close the output stream
        streamClose(m_fileIn);
    }

    }


    
/**
     * Interface for server
     
*/

    
public static void tftpd() {
    DatagramSocket serSock;
    
try {
        serSock 
= new DatagramSocket(69);
    }
 catch (SocketException sockExce) {
        System.out.print(
"Unable to start server:" + sockExce);
        
return;
    }


    
byte[] buf = new byte[1024];
    DatagramPacket pack 
= new DatagramPacket(buf, 1024);
    
while (true{
        
try {// wait for WRQ or RRQ -- all others are errors
        serSock.setSoTimeout(0);
        serSock.receive(pack);

        
byte[] data = pack.getData();
        
int packType = (data[0<< 8+ data[1];

        
if (DEBUG) {// DEBUG information
            System.out
                .println(
"receive packet " + pack.getAddress()
                    
+ ":" + pack.getPort() + " packet code="
                    
+ packType);
        }


        
if (packType != WRQ && packType != RRQ) {
            
// send back error packet
            byte[] errBytes = new byte[5];
            errBytes[
0= (byte) ((ERR >> 8& 0xFF);
            errBytes[
1= (byte) (ERR & 0xFF);
            errBytes[
2= (byte0;
            errBytes[
3= (byte0;
            errBytes[
4= 0;
            pack.setData(errBytes);
            serSock.send(pack);
            
continue;
        }


        
// start new thread (use private constructor)
        TftpSocket worker = new TftpSocket();
        
// file name length and file type length
        int zlen, zlen1;
        worker.m_serverPort 
= pack.getPort();
        worker.m_hostAddr 
= pack.getAddress();
        
// get the file name from packet data
        for (zlen = 0; data[zlen + 2!= 0++zlen)
            ;
        worker.m_fNameString 
= new String(data, 2, zlen);
        worker.m_fNameByte 
= worker.m_fNameString.getBytes();
        
// get the file type from packet data
        for (zlen1 = 0; data[zlen1 + 3 + zlen] != 0++zlen1)
            ;
        worker.m_fType 
= new String(data, zlen + 3, zlen1).getBytes();
        worker.m_serverCode 
= packType;
        
// start server thread
        new Thread(worker).start();
        }
 catch (IOException exce) {
        
// ignore
        exce.printStackTrace();
        }

    }

    }


    
/**
     * Test main. provide file name, file type, host on command line follow with
     * an S to send R(or nothing) to receive
     
*/

    
public static void main(String[] args) throws Exception {
    
if (args.length == 0{
        System.out.println(
"Starting server");
        
new Tftpd().start();
        
return;
    }


    TftpSocket sock 
= new TftpSocket(args[0], args[1], args[2]);
    
if (args.length <= 3 || !args[3].equals("S")) {
        System.out.println(
"Client request " + sock.freceive());
    }
 else {
        System.out.println(
"Client request " + sock.fsend());
    }

    }


    
/**
     * Helper thread for test main
     
*/

    
static class Tftpd extends Thread {
    
public void run() {
        TftpSocket.tftpd();
    }

    }

}



 
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:65438次
    • 积分:1086
    • 等级:
    • 排名:千里之外
    • 原创:32篇
    • 转载:23篇
    • 译文:0篇
    • 评论:4条
    文章分类
    最新评论