|  | 
 
| 1、POM文件定义: <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <parent>
 <artifactId>multi-project-parent</artifactId>
 <groupId>multi-project-parent</groupId>
 <version>1.0-SNAPSHOT</version>
 </parent>
 <modelVersion>4.0.0</modelVersion>
 <artifactId>tftp</artifactId>
 <dependencies>
 <dependency>
 <groupId>commons-net</groupId>
 <artifactId>commons-net</artifactId>
 <version>3.3</version>
 </dependency>
 </dependencies>
 </project>
 ==========================================================================
 2、Server实现:
 package com.wolf.tftp.server;
 /*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintStream;
 import java.net.SocketException;
 import java.net.SocketTimeoutException;
 import java.util.HashSet;
 import java.util.Iterator;
 import com.wolf.tftp.common.Constants;
 import com.wolf.tftp.common.Constants;
 import org.apache.commons.net.io.FromNetASCIIOutputStream;
 import org.apache.commons.net.io.ToNetASCIIInputStream;
 import org.apache.commons.net.tftp.*;
 /**
 * A fully multi-threaded tftp server. Can handle multiple clients at the same time. Implements RFC
 * 1350 and wrapping block numbers for large file support.
 * <p/>
 * To launch, just create an instance of the class. An IOException will be thrown if the server
 * fails to start for reasons such as port in use, port denied, etc.
 * <p/>
 * To stop, use the shutdown method.
 * <p/>
 * To check to see if the server is still running (or if it stopped because of an error), call the
 * isRunning() method.
 * <p/>
 * By default, events are not logged to stdout/stderr. This can be changed with the
 * setLog and setLogError methods.
 * <p/>
 * <p/>
 * Example usage is below:
 * <p/>
 * <code>
 * public static void main(String[] args) throws Exception
 * {
 * if (args.length != 1)
 * {
 * System.out
 * .println("You must provide 1 argument - the base path for the server to serve from.");
 * System.exit(1);
 * }
 * <p/>
 * TFTPServer ts = new TFTPServer(new File(args[0]), new File(args[0]), GET_AND_PUT);
 * ts.setSocketTimeout(2000);
 * <p/>
 * System.out.println("TFTP Server running.  Press enter to stop.");
 * new InputStreamReader(System.in).read();
 * <p/>
 * ts.shutdown();
 * System.out.println("Server shut down.");
 * System.exit(0);
 * }
 * <p/>
 * </code>
 *
 * @author <A HREF="mailto:daniel.armbrust.list@gmail.com">Dan Armbrust</A>
 * @since 2.0
 */
 public class TFTPServer implements Runnable {
 
 public static enum ServerMode {GET_ONLY, PUT_ONLY, GET_AND_PUT;}
 private HashSet<TFTPTransfer> transfers_ = new HashSet<TFTPTransfer>();
 private volatile boolean shutdownServer = true;
 private volatile boolean runningServer = false;
 private TFTP serverTftp_;
 private File serverReadDirectory_;
 private File serverWriteDirectory_;
 private int port_;
 private Exception serverException = null;
 private ServerMode mode_;
 /* /dev/null output stream (default) */
 private static final PrintStream nullStream = new PrintStream(
 new OutputStream() {
 @Override
 public void write(int b) {
 }
 @Override
 public void write(byte[] b) throws IOException {
 }
 }
 );
 // don't have access to a logger api, so we will log to these streams, which
 // by default are set to a no-op logger
 private PrintStream log_;
 private PrintStream logError_;
 private int maxTimeoutRetries_ = 3;
 private int socketTimeout_;
 private Thread serverThread;
 
 public TFTPServer(File serverReadDirectory, File serverWriteDirectory, ServerMode mode, PrintStream log, PrintStream error)
 throws IOException {
 this(serverReadDirectory, serverWriteDirectory, Constants.DEFAULT_TFTP_PORT, mode, log, error);
 }
 /**
 * Start a TFTP Server on the default port (69). Gets and Puts occur in the specified
 * directories.
 * <p/>
 * The server will start in another thread, allowing this constructor to return immediately.
 * <p/>
 * If a get or a put comes in with a relative path that tries to get outside of the
 * serverDirectory, then the get or put will be denied.
 * <p/>
 * GET_ONLY mode only allows gets, PUT_ONLY mode only allows puts, and GET_AND_PUT allows both.
 * Modes are defined as int constants in this class.
 *
 * @param serverReadDirectory  directory for GET requests
 * @param serverWriteDirectory directory for PUT requests
 * @param mode                 A value as specified above.
 * @throws IOException if the server directory is invalid or does not exist.
 */
 public TFTPServer(File serverReadDirectory, File serverWriteDirectory, ServerMode mode)
 throws IOException {
 this(serverReadDirectory, serverWriteDirectory, Constants.DEFAULT_TFTP_PORT, mode, null, null);
 }
 /**
 * Start a TFTP Server on the specified port. Gets and Puts occur in the specified directory.
 * <p/>
 * The server will start in another thread, allowing this constructor to return immediately.
 * <p/>
 * If a get or a put comes in with a relative path that tries to get outside of the
 * serverDirectory, then the get or put will be denied.
 * <p/>
 * GET_ONLY mode only allows gets, PUT_ONLY mode only allows puts, and GET_AND_PUT allows both.
 * Modes are defined as int constants in this class.
 *
 * @param serverReadDirectory  directory for GET requests
 * @param serverWriteDirectory directory for PUT requests
 * @param mode                 A value as specified above.
 * @param log                  Stream to write log message to. If not provided, uses System.out
 * @param errorLog             Stream to write error messages to. If not provided, uses System.err.
 * @throws IOException if the server directory is invalid or does not exist.
 */
 public TFTPServer(File serverReadDirectory, File serverWriteDirectory, int port, ServerMode mode,
 PrintStream log, PrintStream errorLog) throws IOException {
 port_ = port;
 mode_ = mode;
 log_ = (log == null ? nullStream : log);
 logError_ = (errorLog == null ? nullStream : errorLog);
 log_.println("Starting TFTP Server on port " + port_ + ".  Read directory: "
 + serverReadDirectory + " Write directory: " + serverWriteDirectory
 + " Server Mode is " + mode_);
 serverReadDirectory_ = serverReadDirectory.getCanonicalFile();
 if (!serverReadDirectory_.exists() || !serverReadDirectory.isDirectory()) {
 throw new IOException("The server read directory " + serverReadDirectory_
 + " does not exist");
 }
 serverWriteDirectory_ = serverWriteDirectory.getCanonicalFile();
 if (!serverWriteDirectory_.exists() || !serverWriteDirectory.isDirectory()) {
 throw new IOException("The server write directory " + serverWriteDirectory_
 + " does not exist");
 }
 //launch(serverReadDirectory, serverWriteDirectory);
 }
 /**
 * Set the max number of retries in response to a timeout. Default 3. Min 0.
 *
 * @param retries
 */
 public void setMaxTimeoutRetries(int retries) {
 if (retries < 0) {
 throw new RuntimeException("Invalid Value");
 }
 maxTimeoutRetries_ = retries;
 }
 /**
 * Get the current value for maxTimeoutRetries
 */
 public int getMaxTimeoutRetries() {
 return maxTimeoutRetries_;
 }
 /**
 * Set the socket timeout in milliseconds used in transfers. Defaults to the value here:
 * http://commons.apache.org/net/apidocs/org/apache/commons/net/tftp/TFTP.html#DEFAULT_TIMEOUT
 * (5000 at the time I write this) Min value of 10.
 */
 public void setSocketTimeout(int timeout) {
 if (timeout < 10) {
 throw new RuntimeException("Invalid Value");
 }
 socketTimeout_ = timeout;
 }
 /**
 * The current socket timeout used during transfers in milliseconds.
 */
 public int getSocketTimeout() {
 return socketTimeout_;
 }
 /*
 * start the server, throw an error if it can't start.
 */
 public void launch() {
 shutdownServer = false;
 runningServer = true;
 serverTftp_ = new TFTP();
 // This is the value used in response to each client.
 socketTimeout_ = serverTftp_.getDefaultTimeout();
 // we want the server thread to listen forever.
 serverTftp_.setDefaultTimeout(0);
 try {
 serverTftp_.open(port_);
 System.out.println("TFTP Server is listen on "+port_);
 } catch (SocketException e) {
 shutdownServer = true;
 runningServer = false;
 e.printStackTrace();
 }
 serverThread = new Thread(this);
 serverThread.setDaemon(true);
 serverThread.start();
 }
 @Override
 protected void finalize() throws Throwable {
 shutdown();
 }
 /**
 * check if the server thread is still running.
 *
 * @return true if running, false if stopped.
 * @throws Exception throws the exception that stopped the server if the server is stopped from
 *                   an exception.
 */
 public boolean isRunning() throws Exception {
 if (serverException != null) {
 throw serverException;
 }
 return runningServer;
 }
 public void run() {
 try {
 while (!shutdownServer) {
 TFTPPacket tftpPacket;
 tftpPacket = serverTftp_.receive();
 TFTPTransfer tt = new TFTPTransfer(tftpPacket);
 synchronized (transfers_) {
 transfers_.add(tt);
 }
 Thread thread = new Thread(tt);
 thread.setDaemon(true);
 thread.start();
 }
 } catch (Exception e) {
 if (!shutdownServer) {
 serverException = e;
 logError_.println("Unexpected Error in TFTP Server - Server shut down! + " + e);
 }
 } finally {
 shutdownServer = true; // set this to true, so the launching thread can check to see if it started.
 runningServer = false;
 if (serverTftp_ != null && serverTftp_.isOpen()) {
 serverTftp_.close();
 }
 }
 }
 /**
 * Stop the tftp server (and any currently running transfers) and release all opened network
 * resources.
 */
 public void shutdown() {
 shutdownServer = true;
 runningServer = false;
 synchronized (transfers_) {
 Iterator<TFTPTransfer> it = transfers_.iterator();
 while (it.hasNext()) {
 it.next().shutdown();
 }
 }
 try {
 serverTftp_.close();
 } catch (RuntimeException e) {
 // noop
 }
 try {
 serverThread.join();
 } catch (InterruptedException e) {
 // we've done the best we could, return
 }
 log_.flush();
 logError_.flush();
 log_.close();
 logError_.close();
 }
 /*
 * An instance of an ongoing transfer.
 */
 private class TFTPTransfer implements Runnable {
 private TFTPPacket tftpPacket_;
 private boolean shutdownTransfer = false;
 TFTP transferTftp_ = null;
 public TFTPTransfer(TFTPPacket tftpPacket) {
 tftpPacket_ = tftpPacket;
 }
 public void shutdown() {
 shutdownTransfer = true;
 try {
 transferTftp_.close();
 } catch (RuntimeException e) {
 // noop
 }
 }
 public void run() {
 try {
 transferTftp_ = new TFTP();
 transferTftp_.beginBufferedOps();
 transferTftp_.setDefaultTimeout(socketTimeout_);
 transferTftp_.open();
 if (tftpPacket_ instanceof TFTPReadRequestPacket) {
 handleRead(((TFTPReadRequestPacket) tftpPacket_));
 } else if (tftpPacket_ instanceof TFTPWriteRequestPacket) {
 handleWrite((TFTPWriteRequestPacket) tftpPacket_);
 } else {
 log_.println("Unsupported TFTP request (" + tftpPacket_ + ") - ignored.");
 }
 } catch (Exception e) {
 e.printStackTrace();
 if (!shutdownTransfer) {
 logError_
 .println("Unexpected Error in during TFTP file transfer.  Transfer aborted. "
 + e);
 }
 } finally {
 try {
 if (transferTftp_ != null && transferTftp_.isOpen()) {
 transferTftp_.endBufferedOps();
 transferTftp_.close();
 }
 } catch (Exception e) {
 // noop
 }
 synchronized (transfers_) {
 transfers_.remove(this);
 }
 }
 }
 /*
 * Handle a tftp read request.
 */
 private void handleRead(TFTPReadRequestPacket trrp) throws IOException, TFTPPacketException {
 InputStream is = null;
 try {
 if (mode_ == ServerMode.PUT_ONLY) {
 transferTftp_.bufferedSend(new TFTPErrorPacket(trrp.getAddress(), trrp
 .getPort(), TFTPErrorPacket.ILLEGAL_OPERATION,
 "Read not allowed by server."));
 return;
 }
 try {
 is = new BufferedInputStream(new FileInputStream(buildSafeFile(
 serverReadDirectory_, trrp.getFilename(), false)));
 } catch (FileNotFoundException e) {
 transferTftp_.bufferedSend(new TFTPErrorPacket(trrp.getAddress(), trrp
 .getPort(), TFTPErrorPacket.FILE_NOT_FOUND, e.getMessage()));
 return;
 } catch (Exception e) {
 transferTftp_.bufferedSend(new TFTPErrorPacket(trrp.getAddress(), trrp
 .getPort(), TFTPErrorPacket.UNDEFINED, e.getMessage()));
 return;
 }
 if (trrp.getMode() == TFTP.NETASCII_MODE) {
 is = new ToNetASCIIInputStream(is);
 }
 byte[] temp = new byte[TFTPDataPacket.MAX_DATA_LENGTH];
 TFTPPacket answer;
 int block = 1;
 boolean sendNext = true;
 int readLength = TFTPDataPacket.MAX_DATA_LENGTH;
 TFTPDataPacket lastSentData = null;
 // We are reading a file, so when we read less than the
 // requested bytes, we know that we are at the end of the file.
 while (readLength == TFTPDataPacket.MAX_DATA_LENGTH && !shutdownTransfer) {
 if (sendNext) {
 readLength = is.read(temp);
 if (readLength == -1) {
 readLength = 0;
 }
 lastSentData = new TFTPDataPacket(trrp.getAddress(), trrp.getPort(), block,
 temp, 0, readLength);
 transferTftp_.bufferedSend(lastSentData);
 }
 answer = null;
 int timeoutCount = 0;
 while (!shutdownTransfer
 && (answer == null || !answer.getAddress().equals(trrp.getAddress()) || answer
 .getPort() != trrp.getPort())) {
 // listen for an answer.
 if (answer != null) {
 // The answer that we got didn't come from the
 // expected source, fire back an error, and continue
 // listening.
 log_.println("TFTP Server ignoring message from unexpected source.");
 transferTftp_.bufferedSend(new TFTPErrorPacket(answer.getAddress(),
 answer.getPort(), TFTPErrorPacket.UNKNOWN_TID,
 "Unexpected Host or Port"));
 }
 try {
 answer = transferTftp_.bufferedReceive();
 } catch (SocketTimeoutException e) {
 if (timeoutCount >= maxTimeoutRetries_) {
 throw e;
 }
 // didn't get an ack for this data. need to resend
 // it.
 timeoutCount++;
 transferTftp_.bufferedSend(lastSentData);
 continue;
 }
 }
 if (answer == null || !(answer instanceof TFTPAckPacket)) {
 if (!shutdownTransfer) {
 logError_
 .println("Unexpected response from tftp client during transfer ("
 + answer + ").  Transfer aborted.");
 }
 break;
 } else {
 // once we get here, we know we have an answer packet
 // from the correct host.
 TFTPAckPacket ack = (TFTPAckPacket) answer;
 if (ack.getBlockNumber() != block) {
 /*
 * The origional tftp spec would have called on us to resend the
 * previous data here, however, that causes the SAS Syndrome.
 * http://www.faqs.org/rfcs/rfc1123.html section 4.2.3.1 The modified
 * spec says that we ignore a duplicate ack. If the packet was really
 * lost, we will time out on receive, and resend the previous data at
 * that point.
 */
 sendNext = false;
 } else {
 // send the next block
 block++;
 if (block > 65535) {
 // wrap the block number
 block = 0;
 }
 sendNext = true;
 }
 }
 }
 } finally {
 try {
 if (is != null) {
 is.close();
 }
 } catch (IOException e) {
 // noop
 }
 }
 }
 /*
 * handle a tftp write request.
 */
 private void handleWrite(TFTPWriteRequestPacket twrp) throws IOException,
 TFTPPacketException {
 OutputStream bos = null;
 try {
 if (mode_ == ServerMode.GET_ONLY) {
 transferTftp_.bufferedSend(new TFTPErrorPacket(twrp.getAddress(), twrp
 .getPort(), TFTPErrorPacket.ILLEGAL_OPERATION,
 "Write not allowed by server."));
 return;
 }
 int lastBlock = 0;
 String fileName = twrp.getFilename();
 try {
 File temp = buildSafeFile(serverWriteDirectory_, fileName, true);
 //if file exists, overwrite
 /*if (temp.exists()) {
 transferTftp_.bufferedSend(new TFTPErrorPacket(twrp.getAddress(), twrp
 .getPort(), TFTPErrorPacket.FILE_EXISTS, "File already exists"));
 return;
 }*/
 bos = new BufferedOutputStream(new FileOutputStream(temp,false));
 if (twrp.getMode() == TFTP.NETASCII_MODE) {
 bos = new FromNetASCIIOutputStream(bos);
 }
 } catch (Exception e) {
 transferTftp_.bufferedSend(new TFTPErrorPacket(twrp.getAddress(), twrp
 .getPort(), TFTPErrorPacket.UNDEFINED, e.getMessage()));
 return;
 }
 TFTPAckPacket lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), 0);
 transferTftp_.bufferedSend(lastSentAck);
 while (true) {
 // get the response - ensure it is from the right place.
 TFTPPacket dataPacket = null;
 int timeoutCount = 0;
 while (!shutdownTransfer
 && (dataPacket == null
 || !dataPacket.getAddress().equals(twrp.getAddress()) || dataPacket
 .getPort() != twrp.getPort())) {
 // listen for an answer.
 if (dataPacket != null) {
 // The data that we got didn't come from the
 // expected source, fire back an error, and continue
 // listening.
 log_.println("TFTP Server ignoring message from unexpected source.");
 transferTftp_.bufferedSend(new TFTPErrorPacket(dataPacket.getAddress(),
 dataPacket.getPort(), TFTPErrorPacket.UNKNOWN_TID,
 "Unexpected Host or Port"));
 }
 try {
 dataPacket = transferTftp_.bufferedReceive();
 } catch (SocketTimeoutException e) {
 if (timeoutCount >= maxTimeoutRetries_) {
 throw e;
 }
 // It didn't get our ack. Resend it.
 transferTftp_.bufferedSend(lastSentAck);
 timeoutCount++;
 continue;
 }
 }
 if (dataPacket != null && dataPacket instanceof TFTPWriteRequestPacket) {
 // it must have missed our initial ack. Send another.
 lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), 0);
 transferTftp_.bufferedSend(lastSentAck);
 } else if (dataPacket == null || !(dataPacket instanceof TFTPDataPacket)) {
 if (!shutdownTransfer) {
 logError_
 .println("Unexpected response from tftp client during transfer ("
 + dataPacket + ").  Transfer aborted.");
 }
 break;
 } else {
 int block = ((TFTPDataPacket) dataPacket).getBlockNumber();
 byte[] data = ((TFTPDataPacket) dataPacket).getData();
 int dataLength = ((TFTPDataPacket) dataPacket).getDataLength();
 int dataOffset = ((TFTPDataPacket) dataPacket).getDataOffset();
 if (block > lastBlock || (lastBlock == 65535 && block == 0)) {
 // it might resend a data block if it missed our ack
 // - don't rewrite the block.
 bos.write(data, dataOffset, dataLength);
 lastBlock = block;
 }
 lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), block);
 transferTftp_.bufferedSend(lastSentAck);
 if (dataLength < TFTPDataPacket.MAX_DATA_LENGTH) {
 // end of stream signal - The tranfer is complete.
 bos.close();
 // But my ack may be lost - so listen to see if I
 // need to resend the ack.
 for (int i = 0; i < maxTimeoutRetries_; i++) {
 try {
 dataPacket = transferTftp_.bufferedReceive();
 } catch (SocketTimeoutException e) {
 // this is the expected route - the client
 // shouldn't be sending any more packets.
 break;
 }
 if (dataPacket != null
 && (!dataPacket.getAddress().equals(twrp.getAddress()) || dataPacket
 .getPort() != twrp.getPort())) {
 // make sure it was from the right client...
 transferTftp_
 .bufferedSend(new TFTPErrorPacket(dataPacket
 .getAddress(), dataPacket.getPort(),
 TFTPErrorPacket.UNKNOWN_TID,
 "Unexpected Host or Port"));
 } else {
 // This means they sent us the last
 // datapacket again, must have missed our
 // ack. resend it.
 transferTftp_.bufferedSend(lastSentAck);
 }
 }
 // all done.
 break;
 }
 }
 }
 } finally {
 if (bos != null) {
 bos.close();
 }
 }
 }
 /*
 * Utility method to make sure that paths provided by tftp clients do not get outside of the
 * serverRoot directory.
 */
 private File buildSafeFile(File serverDirectory, String fileName, boolean createSubDirs)
 throws IOException {
 File temp = new File(serverDirectory, fileName);
 temp = temp.getCanonicalFile();
 if (!isSubdirectoryOf(serverDirectory, temp)) {
 throw new IOException("Cannot access files outside of tftp server root.");
 }
 // ensure directory exists (if requested)
 if (createSubDirs) {
 createDirectory(temp.getParentFile());
 }
 return temp;
 }
 /*
 * recursively create subdirectories
 */
 private void createDirectory(File file) throws IOException {
 File parent = file.getParentFile();
 if (parent == null) {
 throw new IOException("Unexpected error creating requested directory");
 }
 if (!parent.exists()) {
 // recurse...
 createDirectory(parent);
 }
 if (parent.isDirectory()) {
 if (file.isDirectory()) {
 return;
 }
 boolean result = file.mkdir();
 if (!result) {
 throw new IOException("Couldn't create requested directory");
 }
 } else {
 throw new IOException(
 "Invalid directory path - file in the way of requested folder");
 }
 }
 /*
 * recursively check to see if one directory is a parent of another.
 */
 private boolean isSubdirectoryOf(File parent, File child) {
 File childsParent = child.getParentFile();
 if (childsParent == null) {
 return false;
 }
 if (childsParent.equals(parent)) {
 return true;
 } else {
 return isSubdirectoryOf(parent, childsParent);
 }
 }
 }
 /**
 * Set the stream object to log debug / informational messages. By default, this is a no-op
 *
 * @param log
 */
 public void setLog(PrintStream log) {
 this.log_ = log;
 }
 /**
 * Set the stream object to log error messsages. By default, this is a no-op
 *
 * @param logError
 */
 public void setLogError(PrintStream logError) {
 this.logError_ = logError;
 }
 }
 ==========================================================================
 3、包装server实现
 package com.wolf.tftp.server;
 import java.io.*;
 /**
 * Created with IntelliJ IDEA.
 * User: jiyanbin
 * Date: 13-8-6
 * Time: 下午12:43
 * To change this template use File | Settings | File Templates.
 */
 public class TFTPServerConsole {
 private TFTPServer server;
 private String serverReadDir;
 private String serverWriteDir;
 private int port;
 private int timeout = 30000;
 private TFTPServer.ServerMode serverMode;
 //single instance
 private volatile static TFTPServerConsole console;
 private TFTPServerConsole() throws Exception {
 //set default value;
 serverReadDir = SERVER_READ_DIR;
 serverWriteDir = SERVER_WRITE_DIR;
 port = DEFAULT_TFTP_PORT;
 serverMode = TFTPServer.ServerMode.GET_AND_PUT;
 server = new TFTPServer(new File(serverReadDir), new File(serverWriteDir), port, serverMode,
 null, null);
 server.setSocketTimeout(timeout);
 }
 public static TFTPServerConsole getInstance() throws Exception {
 if (console == null) {
 synchronized (Object.class) {
 if (console == null) {
 console = new TFTPServerConsole();
 }
 }
 }
 return console;
 }
 public void startServer() {
 try {
 if (server != null && !server.isRunning()) {
 server.launch();
 } else {
 System.out.println("TFTP server has been running already!");
 }
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
 public void stopServer() {
 try {
 if (server != null && server.isRunning()) {
 server.shutdown();
 System.out.println("TFTP Server shutdown.");
 } else {
 System.out.println("TFTP server is not running");
 }
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
 public void setServerReadDir(String serverReadDir) {
 this.serverReadDir = serverReadDir;
 }
 public void setServerWriteDir(String serverWriteDir) {
 this.serverWriteDir = serverWriteDir;
 }
 public void setPort(int port) {
 this.port = port;
 }
 public void setServerMode(TFTPServer.ServerMode serverMode) {
 this.serverMode = serverMode;
 }
 public void setTimeout(int timeout) {
 this.timeout = timeout;
 }
 }
 ==========================================================================
 4、客户端工作器
 package com.wolf.tftp.client;
 import org.apache.commons.net.tftp.TFTP;
 import org.apache.commons.net.tftp.TFTPClient;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.net.SocketException;
 import java.net.UnknownHostException;
 /**
 * Created with IntelliJ IDEA.
 * User: jiyanbin
 * Date: 13-8-7
 * Time: 上午11:02
 * To change this template use File | Settings | File Templates.
 */
 public class TFTPClientWorker {
 private final String host;
 private final int port;
 private final boolean isReceive;
 private final int transferMode;
 private final String localFilename;
 private final String remoteFilename;
 /**
 * TFTPClientTask Builder
 */
 public static class TFTPClientWorkerBuilder {
 private String host = "localhost";
 private int port = DEFAULT_TFTP_PORT;
 private boolean isReceive;
 private int mode = TFTP.BINARY_MODE;// or TFTP.ASCII_MODE
 private String localFilename;
 private String remoteFilename;
 public TFTPClientWorkerBuilder host(String host) {
 this.host = host;
 return this;
 }
 public TFTPClientWorkerBuilder port(int port) {
 this.port = port;
 return this;
 }
 public TFTPClientWorkerBuilder isReceive(boolean isReceiver) {
 this.isReceive = isReceiver;
 return this;
 }
 public TFTPClientWorkerBuilder mode(int mode) {
 this.mode = mode;
 return this;
 }
 public TFTPClientWorkerBuilder localFilename(String localFilename) {
 this.localFilename = localFilename;
 return this;
 }
 public TFTPClientWorkerBuilder remoteFilename(String remoteFilename) {
 this.remoteFilename = remoteFilename;
 return this;
 }
 public TFTPClientWorker builder() {
 return new TFTPClientWorker(this);
 }
 }
 private TFTPClientWorker(TFTPClientWorkerBuilder builder) {
 host = builder.host;
 port = builder.port;
 isReceive = builder.isReceive;
 transferMode = builder.mode;
 localFilename = builder.localFilename;
 remoteFilename = builder.remoteFilename;
 }
 public String getHost() {
 return host;
 }
 public boolean isReceive() {
 return isReceive;
 }
 public int getTransferMode() {
 return transferMode;
 }
 public String getLocalFilename() {
 return localFilename;
 }
 public String getRemoteFilename() {
 return remoteFilename;
 }
 /**
 *
 * @return the File path in TFTP server
 * @throws SocketException
 */
 public String doJob() throws SocketException{
 TFTPClient tftp = new TFTPClient();
 // We want to timeout if a response takes longer than 60 seconds
 tftp.setDefaultTimeout(60000);
 // Open local socket
 try {
 tftp.open();
 } catch (SocketException e) {
 System.err.println("Error: could not open local UDP socket.");
 System.err.println(e.getMessage());
 throw e;
 }
 if (isReceive) {
 received(tftp);
 return SERVER_WRITE_DIR.endsWith(File.separator)?
 SERVER_WRITE_DIR + remoteFilename:
 SERVER_WRITE_DIR + File.separator + remoteFilename;
 } else {
 send(tftp);
 return SERVER_READ_DIR.endsWith(File.separator)?
 SERVER_READ_DIR + remoteFilename:
 SERVER_READ_DIR + File.separator + remoteFilename;
 }
 }
 private void received(TFTPClient tftp) {
 FileOutputStream output = null;
 File file = new File(localFilename);
 // Try to open local file for writing
 try {
 // If file exists,  overwrite it.
 output = new FileOutputStream(file,false);
 } catch (IOException e) {
 tftp.close();
 System.err.println("Error: could not open local file for writing.");
 System.err.println(e.getMessage());
 return;
 }
 // Try to receive remote file via TFTP
 try {
 tftp.receiveFile(remoteFilename, transferMode, output, host,port);
 } catch (UnknownHostException e) {
 System.err.println("Error: could not resolve hostname.");
 System.err.println(e.getMessage());
 return;
 } catch (IOException e) {
 System.err.println(
 "Error: I/O exception occurred while receiving file.");
 System.err.println(e.getMessage());
 return;
 } finally {
 // Close local socket and output file
 tftp.close();
 try {
 if (output != null) {
 output.close();
 }
 //closed = true;
 } catch (IOException e) {
 //closed = false;
 System.err.println("Error: error closing file.");
 System.err.println(e.getMessage());
 }
 }
 }
 private void send(TFTPClient tftp) {
 // We're sending a file
 FileInputStream input = null;
 // Try to open local file for reading
 try {
 input = new FileInputStream(localFilename);
 } catch (IOException e) {
 tftp.close();
 System.err.println("Error: could not open local file for reading.");
 System.err.println(e.getMessage());
 return;
 }
 // Try to send local file via TFTP
 try {
 tftp.sendFile(remoteFilename, transferMode, input, host, port);
 } catch (UnknownHostException e) {
 System.err.println("Error: could not resolve hostname.");
 System.err.println(e.getMessage());
 return;
 } catch (IOException e) {
 System.err.println(
 "Error: I/O exception occurred while sending file.");
 System.err.println(e.getMessage());
 return;
 } finally {
 // Close local socket and input file
 tftp.close();
 try {
 if (input != null) {
 input.close();
 }
 //closed = true;
 } catch (IOException e) {
 //closed = false;
 System.err.println("Error: error closing file.");
 System.err.println(e.getMessage());
 }
 }
 }
 }
 ==========================================================================
 5、公共常量类
 package com.wolf.tftp.common;
 /**
 * Created with IntelliJ IDEA.
 * User: jiyanbin
 * Date: 13-8-7
 * Time: 上午11:00
 * To change this template use File | Settings | File Templates.
 */
 public abstract class Constants {
 public static final int DEFAULT_TFTP_PORT = 69;
 public static final String SERVER_READ_DIR;
 public static final String SERVER_WRITE_DIR;
 static {
 SERVER_READ_DIR = System.getProperty("tftp.server.read_dir",System.getProperty("java.io.tmpdir"));
 SERVER_WRITE_DIR = System.getProperty("tftp.server.write_dir",System.getProperty("java.io.tmpdir"));
 }
 }
 ==========================================================================
 6、测试,可以测试并发时的性能
 启动tftp server
 package com.wolf.tftp.test;
 import com.wolf.tftp.server.TFTPServer;
 import com.wolf.tftp.server.TFTPServerConsole;
 import java.io.*;
 /**
 * Created with IntelliJ IDEA.
 * User: jiyanbin
 * Date: 13-8-6
 * Time: 下午12:43
 * To change this template use File | Settings | File Templates.
 */
 public class TFTPServerConsoleTest {
 private final static String WRITE_PATH = "E:\\tftp";
 private final static String READ_PATH = "E:\\tftp";
 private final static String LOG_FILE="e:\\log\\tftp.log";
 private final static String LOG_ERROR_FILE="e:\\log\\tftp.err";
 public static void main(String[] args) throws Exception {
 TFTPServerConsole console = TFTPServerConsole.getInstance();
 console.startServer();
 System.out.println("TFTP Server running.  Press enter to stop.");
 new InputStreamReader(System.in).read();
 console.stopServer();
 System.exit(0);
 }
 
 public void startServer() throws Exception {
 {
 PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream(LOG_FILE)));
 PrintStream err = new PrintStream(new BufferedOutputStream(new FileOutputStream(LOG_ERROR_FILE)));
 TFTPServer ts = new TFTPServer(new File(READ_PATH), new File(WRITE_PATH),6900, TFTPServer.ServerMode.GET_AND_PUT,
 out,err );
 ts.setSocketTimeout(2000);
 System.out.println("TFTP Server running.  Press enter to stop.");
 new InputStreamReader(System.in).read();
 ts.shutdown();
 System.out.println("Server shut down.");
 System.exit(0);
 }
 }
 }
 客户端调用
 package com.wolf.tftp.test;
 import org.apache.commons.net.tftp.TFTP;
 import java.util.Random;
 /**
 * Created with IntelliJ IDEA.
 * User: jiyanbin
 * Date: 13-8-6
 * Time: 下午2:18
 * To change this template use File | Settings | File Templates.
 */
 public class TFTPClientConsoleTest {
 static final String USAGE =
 "Usage: tftp [options] hostname localfile remotefile\n\n" +
 "hostname   - The name of the remote host\n" +
 "localfile  - The name of the local file to send or the name to use for\n" +
 "\tthe received file\n" +
 "remotefile - The name of the remote file to receive or the name for\n" +
 "\tthe remote server to use to name the local file being sent.\n\n" +
 "options: (The default is to assume -r -b)\n" +
 "\t-s Send a local file\n" +
 "\t-r Receive a remote file\n" +
 "\t-a Use ASCII transfer mode\n" +
 "\t-b Use binary transfer mode\n";
 
 private final static int CLIENT_COUNT = 200;
 private final static int DURATION = 300;
 private final static String TASK_PREFIX = "TFTP-client-";
 public static void main(String[] args) {
 int argc = 0;
 String arg = "";
 boolean receiveFile = false;
 int transferMode = TFTP.ASCII_MODE;
 String hostname, localFilename = "", remoteFilename = "";
 // Parse options
 for (argc = 0; argc < args.length; argc++) {
 arg = args[argc];
 if (arg.startsWith("-")) {
 if (arg.equals("-r")) {
 receiveFile = true;
 } else if (arg.equals("-s")) {
 receiveFile = false;
 } else if (arg.equals("-a")) {
 transferMode = TFTP.ASCII_MODE;
 } else if (arg.equals("-b")) {
 transferMode = TFTP.BINARY_MODE;
 } else {
 System.err.println("Error: unrecognized option.");
 System.err.print(USAGE);
 System.exit(1);
 }
 } else {
 break;
 }
 }
 // Make sure there are enough arguments
 if (args.length - argc < 2 || args.length - argc > 3) {
 System.err.println("Error: invalid number of arguments.");
 System.err.print(USAGE);
 System.exit(1);
 }
 // Get host and file arguments
 hostname = args[argc];
 if (args.length - argc == 2) {
 localFilename = remoteFilename = args[argc + 1];
 }
 if (args.length - argc == 3) {
 localFilename = args[argc + 1];
 remoteFilename = args[argc + 2];
 }
 
 TaskSchedule schedule = new TaskSchedule(DURATION, CLIENT_COUNT);
 for (int i = 0; i < CLIENT_COUNT; i++) {
 String taskId = TASK_PREFIX + i;
 Random random = new Random();
 //取1-500之间的正整数
 int temp = random.nextInt(SOURCE_FILE_COUNT);
 localFilename = SOURCE_FILE_DIR + PREFIX + temp;
 //remoteFilename = taskId + PREFIX + System.currentTimeMillis();
 remoteFilename = PREFIX + temp;
 TFTPClientTask task = new TFTPClientTask.TFTPClientTaskBuilder(taskId)
 .host(hostname)
 .isReceive(receiveFile)
 .localFilename(localFilename)
 .remoteFilename(remoteFilename)
 .mode(transferMode)
 .barrier(schedule.cyclicBarrier)
 .builder();
 schedule.schedule(task);
 }
 }
 private final static String SOURCE_FILE_DIR = "D:\\work\\documents\\tftp-test-file\\";
 private final static String PREFIX = "CiscoConfig-";
 //upload file count + 1
 private final static int SOURCE_FILE_COUNT = 501;
 }
 package com.wolf.tftp.test;
 import java.util.concurrent.CyclicBarrier;
 /**
 * Created with IntelliJ IDEA.
 * User: jiyanbin
 * Date: 13-8-7
 * Time: 上午10:39
 * To change this template use File | Settings | File Templates.
 */
 public abstract class AbstractTask implements Runnable {
 String taskId;
 long successTimes;
 long failTimes;
 static long totalTimes;
 static CyclicBarrier cyclicBarrier;
 public String getTaskId() {
 return taskId;
 }
 public void setTaskId(String taskId) {
 this.taskId = taskId;
 }
 public long getSuccessTimes() {
 return successTimes;
 }
 public void setSuccessTimes(long successTimes) {
 this.successTimes = successTimes;
 }
 public long getFailTimes() {
 return failTimes;
 }
 public void setFailTimes(long failTimes) {
 this.failTimes = failTimes;
 }
 public long getTotalTimes() {
 return totalTimes;
 }
 public void setTotalTimes(long totalTimes) {
 this.totalTimes = totalTimes;
 }
 public CyclicBarrier getCyclicBarrier() {
 return cyclicBarrier;
 }
 public void setCyclicBarrier(CyclicBarrier cyclicBarrier) {
 this.cyclicBarrier = cyclicBarrier;
 }
 }
 package com.wolf.tftp.test;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CyclicBarrier;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 /**
 * Created with IntelliJ IDEA.
 * User: jiyanbin
 * Date: 13-8-7
 * Time: 上午9:18
 * To change this template use File | Settings | File Templates.
 */
 public class TaskSchedule<T extends AbstractTask> {
 CyclicBarrier cyclicBarrier;
 long begin = System.currentTimeMillis();
 List<T> tasks = new ArrayList<T>();
 ExecutorService executor = Executors.newCachedThreadPool();
 public TaskSchedule(final long duration, int taskCount) {
 System.out.println("TFTP client task is running...");
 System.out.println("Total task : " + taskCount);
 System.out.println("Time of duration :" + duration + "s");
 cyclicBarrier = new CyclicBarrier(taskCount, new Runnable() {
 @Override
 public void run() {
 long times = 0L;
 long successTimes = 0L;
 while (System.currentTimeMillis() - begin > duration * 1000) {
 for (AbstractTask task : tasks) {
 times = task.getTotalTimes();
 successTimes += task.getSuccessTimes();
 System.out.println("==============================================================");
 //System.out.println(task.getTaskId() + " execute total times is: " + task.getTotalTimes());
 System.out.println(task.getTaskId() + " execute success times is: " + task.getSuccessTimes());
 System.out.println(task.getTaskId() + " execute fail times is: " + task.getFailTimes());
 //System.out.println(task.getTaskId() + " execute rate is: " + task.getTotalTimes() / duration + "times/s");
 System.out.println("==============================================================");
 }
 System.out.println("All of " + tasks.size() + " tasks execute TFTP transfer file task total count is : " + times);
 System.out.println("Execute success rate is: " + successTimes / duration + " times/s");
 executor.shutdown();
 return;
 }
 System.out.println("Task is execute...");
 }
 });
 }
 public void schedule(T task) {
 tasks.add(task);
 executor.execute(task);
 }
 }
 package com.wolf.tftp.test;
 import com.wolf.tftp.client.TFTPClientWorker;
 import com.wolf.tftp.common.Constants;
 import org.apache.commons.net.tftp.TFTP;
 import java.net.SocketException;
 import java.util.concurrent.BrokenBarrierException;
 import java.util.concurrent.CyclicBarrier;
 /**
 * Created with IntelliJ IDEA.
 * User: jiyanbin
 * Date: 13-8-6
 * Time: 下午1:27
 * To change this template use File | Settings | File Templates.
 */
 public class TFTPClientTask extends AbstractTask {
 final TFTPClientWorker worker;
 /**
 * TFTPClientTask Builder
 */
 public static class TFTPClientTaskBuilder {
 private final String taskId;
 private String host = "localhost";
 private int port = Constants.DEFAULT_TFTP_PORT;
 private boolean isReceive;
 private int mode = TFTP.BINARY_MODE;// or TFTP.ASCII_MODE
 private String localFilename;
 private String remoteFilename;
 private CyclicBarrier barrier;
 public TFTPClientTaskBuilder(String taskId) {
 this.taskId = taskId;
 }
 public TFTPClientTaskBuilder host(String host) {
 this.host = host;
 return this;
 }
 public TFTPClientTaskBuilder port(int port) {
 this.port = port;
 return this;
 }
 public TFTPClientTaskBuilder isReceive(boolean isReceiver) {
 this.isReceive = isReceiver;
 return this;
 }
 public TFTPClientTaskBuilder mode(int mode) {
 this.mode = mode;
 return this;
 }
 public TFTPClientTaskBuilder localFilename(String localFilename) {
 this.localFilename = localFilename;
 return this;
 }
 public TFTPClientTaskBuilder remoteFilename(String remoteFilename) {
 this.remoteFilename = remoteFilename;
 return this;
 }
 public TFTPClientTaskBuilder barrier(CyclicBarrier barrier) {
 this.barrier = barrier;
 return this;
 }
 public TFTPClientTask builder() {
 return new TFTPClientTask(this);
 }
 }
 private TFTPClientTask(TFTPClientTaskBuilder builder) {
 TFTPClientWorker.TFTPClientWorkerBuilder jobBuilder = new TFTPClientWorker.TFTPClientWorkerBuilder();
 jobBuilder.host(builder.host)
 .port(builder.port)
 .isReceive(builder.isReceive)
 .mode(builder.mode)
 .localFilename(builder.localFilename)
 .remoteFilename(builder.remoteFilename);
 worker = jobBuilder.builder();
 taskId = builder.taskId;
 cyclicBarrier = builder.barrier;
 }
 public TFTPClientWorker getWorker() {
 return worker;
 }
 public void run() {
 try {
 while (!Thread.interrupted()) {
 //System.out.println("Task :" + taskId + "is executing...");
 totalTimes++;
 String filePath = worker.doJob();
 //System.out.println("Remote file path is : " + filePath);
 successTimes++;
 cyclicBarrier.await();
 }
 } catch (InterruptedException e) {
 e.printStackTrace();
 failTimes++;
 } catch (BrokenBarrierException e) {
 e.printStackTrace();
 failTimes++;
 } catch (SocketException e) {
 System.err.println("Error: could not open local UDP socket.");
 System.err.println(e.getMessage());
 failTimes++;
 }
 }
 
 }
 | 
 |