从书上抄来的TFTP协议,终于调试到可以运行,已发现但未解决的问题:
1文件名编码装换不明确,不知是否需要转换,如过用ISO-8859-1转换中文文件名会出错.
2UDP用wireshark截包UDP校验和出现错误:提示:Checksum: 0x0020 [incorrect, should be 0x131f (maybe caused by "UDP checksum offload"?)]
代码作者写上自己名字了,不好意思了...
注:源程序摘自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, 0, 512);
} 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] = (byte) 0;
errBytes[3] = (byte) 0;
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();
}
}
}
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, 0, 512);
} 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] = (byte) 0;
errBytes[3] = (byte) 0;
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();
}
}
}