/*
* Copyright 2011 Future Systems
*
* Licensed 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.
*/
package org.krakenapps.tftp;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ClientPutProcessor {
private static final int TIMEOUT = 60 * 1000; // 1min timeout
private static final int MAXIMUM_TFTP_PACKET_SIZE = 512;
private Logger logger = LoggerFactory.getLogger(ClientPutProcessor.class.getName());
private DatagramSocket socket;
private InetAddress target;
private int port;
private short blockNum = 0;
private int expectedAckNum = 1;
private boolean is512 = false;
public ClientPutProcessor() {
}
public void start(InetSocketAddress target, byte[] firstPacket, String fileName) throws IOException {
socket = new DatagramSocket();
socket.setSoTimeout(TIMEOUT);
sendFirstPacket(target, firstPacket);
processing(target.getAddress(), fileName);
}
private void sendFirstPacket(InetSocketAddress target, byte[] firstPacket) throws IOException {
DatagramPacket outgoing = new DatagramPacket(firstPacket, firstPacket.length, target.getAddress(), target
.getPort());
socket.send(outgoing);
}
private void processing(InetAddress target, String fileName) throws IOException {
FileInputStream is = null;
try {
/* first received */
byte[] inbuf = new byte[200];
DatagramPacket incoming = new DatagramPacket(inbuf, inbuf.length);
socket.receive(incoming);
this.target = target;
this.port = incoming.getPort();
is = new FileInputStream(new File(fileName));
handleSocket(target, port, is);
} finally {
if (is != null)
is.close();
}
}
private void handleSocket(InetAddress target, int port, FileInputStream is) throws IOException {
byte[] data;
do {
blockNum++;
data = new byte[MAXIMUM_TFTP_PACKET_SIZE];
int remain = is.available();
if (remain == 0) {
if (!is512)
break;
send(null);
receive();
break;
} else if (remain < 512) {
byte[] finalData = new byte[remain];
sendAndReceive(is, finalData);
} else if (remain == 512) {
is512 = true;
sendAndReceive(is, data);
} else {
sendAndReceive(is, data);
}
} while (true);
}
private void sendAndReceive(FileInputStream is, byte[] data) throws IOException {
while (true) {
is.read(data);
send(data);
int ackNum = receive();
if (ackNum == expectedAckNum) {
expectedAckNum++;
break;
}
}
}
private void send(byte[] data) throws IOException {
try {
byte[] b;
if (data == null)
b = setData(blockNum);
else
b = setData(blockNum, data);
DatagramPacket outgoing = new DatagramPacket(b, b.length, target, port);
socket.send(outgoing);
} catch (SocketTimeoutException e) {
if (logger.isDebugEnabled())
logger.debug("kraken tftp: socket timeout exception");
}
}
private int receive() throws IOException {
byte[] inbuf = new byte[200];
DatagramPacket incoming = new DatagramPacket(inbuf, inbuf.length);
socket.receive(incoming);
byte[] tftpData = incoming.getData();
return (int) (((int) 0 << 24) | ((int) 0 << 16) | ((tftpData[2] << 8) & 0xFFFF) | (tftpData[3] & 0xFF));
}
private byte[] setData(short blockNum, byte[] b) {
ByteBuffer bb = ByteBuffer.allocate(b.length + 4);
bb.putShort((short) 3);
bb.putShort(blockNum);
bb.put(b);
bb.flip();
int length = bb.limit();
byte[] b1 = new byte[length];
bb.get(b1);
return b1;
}
private byte[] setData(short blockNum) {
ByteBuffer bb = ByteBuffer.allocate(4);
bb.putShort((short) 3);
bb.putShort(blockNum);
bb.flip();
byte[] b1 = new byte[4];
bb.get(b1);
return b1;
}
}