Skip to content

Instantly share code, notes, and snippets.

@aurorapar
Last active April 15, 2018 21:59
Show Gist options
  • Save aurorapar/77afed3aae189793e8c539bbe2ba3f20 to your computer and use it in GitHub Desktop.
Save aurorapar/77afed3aae189793e8c539bbe2ba3f20 to your computer and use it in GitHub Desktop.
// 4-15-18
// Client
import java.net.*;
import java.io.*;
import java.util.*;
import java.nio.charset.*;
import java.time.LocalTime;
import java.nio.file.*;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.APPEND;
public class Client
{
public static int serverPort = 69;
public static int blockNumber = 0;
public static String serverAddress;
public static int timeouts = 0;
public static int missedPackets = 0;
public static int clientPort = 0;
public static FileInputStream fileStream;
public static String fileString;
public static final int TIMEOUTS_MAX = 6;
public static final int MISSED_PACKET_MAX = 6;
public static final int BUFFER_SIZE = 516;
public static final int TIMEOUT_INTERVAL = 2000;
public static final boolean DEBUG = false;
public static void main(String args[])
{
if(args.length != 3 && args.length != 4)
{
System.out.println("Improper argument length. Correct options are:\n\t <host> <PUT|GET> <file> [binary]");
return;
}
serverAddress = args[0];
String operation = args[1].toUpperCase();
String fileName = args[2];
String mode = "netascii";
if(!"PUT".equals(operation) && !"GET".equals(operation))
{
System.out.println(String.format("Could not handle \'operation\' %s\nAccepted operations are:\n\tPUT\tGET\n",operation));
return;
}
File file = new File(fileName);
long fileSize = 0;
if("PUT".equals(operation))
{
if(!file.exists() || file.isDirectory())
{
System.out.println(String.format("Could not handle request\n\t%s\ndoes not exist", fileName));
return;
}
fileSize = file.length();
if(fileSize > 65536 * 512)
{
System.out.println("\nFile over maximum per protocol. Consider packaging file into 32MB archives.");
//return;
}
}
if(args.length == 4)
if("binary".equals(args[3]))
{
mode = "octet";
try{fileStream = new FileInputStream(file);}
catch(Exception e)
{
debug(e.toString());
}
}
else
{}
else
{
try
{
String str = new String(Files.readAllBytes(Paths.get(fileName)));
char[] fileChar = str.toCharArray();
for(int index = 0; index<fileChar.length; index++)
{
if((int) fileChar[index] == 13 && (int) fileChar[index+1] != 10)
fileString += (char) 13 + (char) 10;
else
fileString += fileChar[index];
}
fileString = fileString.substring(3,fileString.length());
}
catch(Exception e)
{
debug(e.toString());
}
}
byte OPCode;
if("GET".equals(operation))
OPCode = 0x1;
else
{
OPCode = 0x2;
}
// Communications
byte[] receiveData = new byte[Client.BUFFER_SIZE];
try
{
ByteArrayOutputStream sendStream = new ByteArrayOutputStream(Client.BUFFER_SIZE);
ByteArrayOutputStream receiveStream = new ByteArrayOutputStream(Client.BUFFER_SIZE);
int replyPort = 0;
int randomPort = new Random().nextInt(65536-1023) + 1024;
Client.clientPort = randomPort;
sendStream.write(0x0);
sendStream.write(OPCode);
sendStream.write(fileName.getBytes("US-ASCII"));
sendStream.write(0x0);
sendStream.write(mode.getBytes("US-ASCII"));
sendStream.write(0x0);
DatagramSocket client = new DatagramSocket(randomPort);
client.setSoTimeout(Client.TIMEOUT_INTERVAL);
DatagramPacket receivedPacket = new DatagramPacket(receiveData, receiveData.length);
while(true)
{
try
{
send(sendStream.toByteArray(), client, Client.serverPort);
client.receive(receivedPacket);
replyPort = receivedPacket.getPort();
receiveData = receivedPacket.getData();
break;
}
catch(SocketTimeoutException e)
{
Client.timeouts++;
if(Client.timeouts>Client.TIMEOUTS_MAX)
{
System.out.println("Maximum number of attempts reached. No ACK received");
return;
}
System.out.println("\nTimed out, retrying...\nNumber of attemps: " + Client.timeouts);
send(sendStream.toByteArray(), client, replyPort);
}
}
//debug("'" + new String(receiveData, "US-ASCII") + "'");
int replyCode = receiveData[1];
switch(replyCode)
{
case(5):
System.out.println("Error:\n\t" + new String(Arrays.copyOfRange(receiveData, 4, receiveData.length), StandardCharsets.US_ASCII));
return;
case(4):
if(0x2 == OPCode)
{
// State information
debug("Acknowledged write request");
int blockOne = 1;
int blockTwo = 0;
int sentData = 0;
byte[] lastSentPacket = sendStream.toByteArray();
byte[] lastReceivedPacket = receiveData;
//Date date = new Date();
long start = new Date().getTime();
int dataInterval = 0;
double rate = 0;
while(sentData < fileSize)
{
Client.timeouts = 0;
missedPackets = 0;
debug("Creating packet");
sendStream.reset();
sendStream.write(0x0);
sendStream.write(0x3);
sendStream.write((byte) blockTwo);
sendStream.write((byte) blockOne);
read(sendStream, mode);
debug(""+sendStream.size());
sentData += sendStream.size() - 4; // Don't do buffer size because of last f
dataInterval += sendStream.size() - 4;
if(blockOne % 50 == 0)
{
start = new Date().getTime();
dataInterval = 0;
}
if(blockOne % 50 == 49)
rate = (double) dataInterval / (new Date().getTime() - start);
if(blockOne % 100 == 0)
{
String status = String.format("\rStatus:\t%.2f%%", (double) sentData/fileSize*100);
status += String.format("\t\tRate: \t%.0fkbps", rate);
status += String.format("\t\tBlock: \t%h", (blockTwo * 128 + blockOne));
System.out.print(status);
}
debug("Sending data now");
send(sendStream.toByteArray(), client, replyPort);
lastSentPacket = sendStream.toByteArray();
while(true)
{
try
{
client.receive(receivedPacket);
receiveData = receivedPacket.getData();
lastReceivedPacket = receiveData;
// TFTP resets at 128 to block 63 ???
//if(block==128 && receiveData[3] == 63)
// block = 62;
replyPort = receivedPacket.getPort();
break;
}
catch(SocketTimeoutException e)
{
Client.timeouts++;
if(Client.timeouts>Client.TIMEOUTS_MAX)
{
System.out.println("Maximum number of attempts reached. Ending program.");
return;
}
System.out.println("\nTimed out, retrying...\nNumber of attemps: " + Client.timeouts);
send(lastSentPacket, client, replyPort);
}
}
if(receiveData[3] != blockOne || receiveData[2] != blockTwo)
{
if(receiveData[1] == 5)
{
System.out.println("Error:\n\t" + new String(Arrays.copyOfRange(receiveData, 4, receiveData.length), StandardCharsets.US_ASCII));
return;
}
if(receiveData[3] == blockOne - 1 && receiveData[2] == blockTwo)
{
while(true)
{
try
{
Client.missedPackets++;
if(Client.missedPackets > Client.MISSED_PACKET_MAX)
{
System.out.println("Maximum number of missed packets reached. Ending program.");
return;
}
System.out.println("\nMissed packet...\n"+ Client.missedPackets + " missed packets");
send(lastSentPacket, client, replyPort);
client.receive(receivedPacket);
receiveData = receivedPacket.getData();
lastReceivedPacket = receiveData;
/*
if(block==128 && receiveData[3] == 63)
block = 62;
*/
replyPort = receivedPacket.getPort();
break;
}
catch(SocketTimeoutException e)
{
Client.timeouts++;
if(Client.timeouts>Client.TIMEOUTS_MAX)
{
System.out.println("Maximum number of attempts reached. Ending program.");
return;
}
System.out.println("Timed out, retrying...\nNumber of attemps: " + Client.timeouts);
}
}
}
}
//System.out.println("\nBlock "+block+"\n");
if(blockOne == Integer.MAX_VALUE)
{
blockOne = 0;
if(blockTwo == Integer.MAX_VALUE)
{
blockTwo = 0;
}
else
{
blockTwo++;
}
}
else
{
blockOne++;
if((byte) blockOne == 0 && (blockOne > 0 || blockTwo>0))
blockTwo++;
if(blockTwo + blockOne == 0xffff && fileStream.available() > 0)
{
System.out.println("\n\nMaximum transfer size reached. \nConsider packaging file into 32MB archives if errors occur.\n");
}
}
}
System.out.println("\nFile transfer complete.");
}
break;
case(3):
if(OPCode == 1)
{
debug("Acknowledged write request");
int blockOne = 1;
int blockTwo = 0;
int sentData = 0;
byte[] lastSentPacket = sendStream.toByteArray();
byte[] lastReceivedPacket = receiveData;
//Date date = new Date();
long start = new Date().getTime();
int dataInterval = 0;
double rate = 0;
Client.timeouts = 0;
missedPackets = 0;
Files.deleteIfExists(Paths.get(fileName));
while(true)
{
//Acknowledgment Packet
sendStream.reset();
sendStream.write(0x0);
sendStream.write(0x4);
sendStream.write((byte) blockTwo);
sendStream.write((byte) blockOne);
write(receivedPacket, fileName, mode);
while(true)
{
try
{
send(sendStream.toByteArray(), client, replyPort);
lastSentPacket = sendStream.toByteArray();
client.receive(receivedPacket);
receiveData = receivedPacket.getData();
lastReceivedPacket = receiveData;
// TFTP resets at 128 to block 63 ???
//if(block==128 && receiveData[3] == 63)
// block = 62;
if(Client.timeouts > 0)
System.out.println("\nPacket received, continuing normal program execution.\n");
break;
}
catch(SocketTimeoutException e)
{
Client.timeouts++;
if(Client.timeouts>Client.TIMEOUTS_MAX)
{
System.out.println("Maximum number of attempts reached. Ending program.");
return;
}
if(receivedPacket.getLength() < Client.BUFFER_SIZE && Client.timeouts == 1)
System.out.println("\nLikely last data packet was sent. Size: " + (receivedPacket.getLength() - 4) + "\nDallying...");
if(receivedPacket.getLength() == Client.BUFFER_SIZE)
System.out.println("\nTimed out, retrying...\nNumber of attemps: " + Client.timeouts);
}
catch(Exception e)
{
System.out.println("Unhandled exception: " + e.toString());
}
}
replyPort = receivedPacket.getPort();
debug("\nReply port: "+replyPort);
if(blockOne == Integer.MAX_VALUE)
{
System.out.println("\nMax block value reached.\n");
blockOne = 0;
if(blockTwo == Integer.MAX_VALUE)
{
blockTwo = 0;
}
else
{
blockTwo++;
}
}
else
{
blockOne++;
if((byte) blockOne == 0 && (blockOne > 0 || blockTwo>0))
blockTwo++;
if(blockTwo + blockOne == 0xffff && fileStream.available() > 0)
{
System.out.println("\n\nMaximum transfer size reached. \nConsider packaging file into 32MB archives if errors occur.\n");
}
}
sentData += receivedPacket.getLength() - 4; // Don't do buffer size because of last f
dataInterval += receivedPacket.getLength();
if(blockOne % 50 == 0)
{
start = new Date().getTime();
dataInterval = 0;
}
if(blockOne % 50 == 49)
rate = (double) dataInterval / (new Date().getTime() - start);
if(blockOne % 100 == 0)
{
String status = String.format("\rStatus:\t%.2fkb", (double) sentData/1024);
status += String.format("\t\tRate: \t%.0fkbps", rate);
status += String.format("\t\tBlock: \t%h", (blockTwo * 128 + blockOne));
System.out.print(status);
}
debug("Sending data now");
debug("Creating packet");
if(receiveData.length > Client.BUFFER_SIZE)
{
System.out.println("\nData over maximum packet size. Terminating program.");
//Error Packet
sendStream.reset();
sendStream.write(0x0);
sendStream.write(0x5);
sendStream.write(0x0);
sendStream.write(0x4);
sendStream.write("Data sent over maximum size".getBytes());
send(sendStream.toByteArray(), client, replyPort);
return;
}
if(receiveData[3] != blockOne || receiveData[2] != blockTwo)
{
if(receiveData[1] == 5)
{
System.out.println("Error:\n\t" + new String(Arrays.copyOfRange(receiveData, 4, receiveData.length), StandardCharsets.US_ASCII));
return;
}
if(receiveData[3] == blockOne - 1 && receiveData[2] == blockTwo)
{
while(true)
{
try
{
Client.missedPackets++;
if(Client.missedPackets > Client.MISSED_PACKET_MAX)
{
System.out.println("Maximum number of missed packets reached. Ending program.");
return;
}
System.out.println("\nMissed packet...\n"+ Client.missedPackets + " missed packets");
send(lastSentPacket, client, replyPort);
client.receive(receivedPacket);
receiveData = receivedPacket.getData();
lastReceivedPacket = receiveData;
/*
if(block==128 && receiveData[3] == 63)
block = 62;
*/
replyPort = receivedPacket.getPort();
break;
}
catch(SocketTimeoutException e)
{
Client.timeouts++;
if(Client.timeouts>Client.TIMEOUTS_MAX)
{
System.out.println("Maximum number of attempts reached. Ending program.");
return;
}
System.out.println("Timed out, retrying...\nNumber of attemps: " + Client.timeouts);
}
}
}
}
}
}
else
{
sendStream.reset();
sendStream.write(0x0);
sendStream.write(0x3);
sendStream.write(0x0);
sendStream.write(0x5);
sendStream.write(0x0);
sendStream.write(0x4);
sendStream.write("Client does not support operation".getBytes("US-ASCII"));
System.out.print("Data request denied");
send(sendStream.toByteArray(), client, replyPort);
}
break;
default:
System.out.println("\nInvalid OPCode.");
//Error Packet
sendStream.reset();
sendStream.write(0x0);
sendStream.write(0x5);
sendStream.write(0x0);
sendStream.write(0x4);
sendStream.write("Invalid OPCode".getBytes());
send(sendStream.toByteArray(), client, replyPort);
return;
}
}
catch(Exception e)
{
debug(e.toString());
}
return;
}
public static void read(ByteArrayOutputStream sendStream, String mode)
{
switch(mode)
{
case "octet":
try
{
int b = 0;
while(sendStream.size() < Client.BUFFER_SIZE)
{
if((b = fileStream.read()) > -1)
{
sendStream.write(b);
}
else
{
break;
}
}
}
catch(Exception e)
{
debug(e.toString());
}
debug("Last bit read");
break;
case "netascii":
try
{
int b = 0;
while(sendStream.size() < Client.BUFFER_SIZE)
{
if(fileString.length() < 2)
break;
fileString = fileString.substring(1,fileString.length());
sendStream.write(fileString.substring(0,1).getBytes("US-ASCII"));
}
}
catch(Exception e)
{
debug(e.toString());
}
break;
default:
System.out.println("Invalid mode specified");
return;
}
}
public static void write(DatagramPacket packet, String fileName, String mode)
{
byte[] byteData = packet.getData();
switch(mode)
{
case("octet"):
try(FileOutputStream fos = new FileOutputStream(fileName, true);)
{
fos.write(Arrays.copyOfRange(byteData, 4, packet.getLength()));
}
catch(Exception e)
{
debug(e.toString());
}
break;
case("netascii"):
try(OutputStream out = new BufferedOutputStream(Files.newOutputStream(Paths.get(fileName), CREATE, APPEND));)
{
String data = new String(Arrays.copyOfRange(byteData, 4, packet.getLength()), "US-ASCII");
data.replace(""+ (char) 13, ""+(char) 13 + (char) 10);
debug(data);
out.write(data.getBytes());
}
catch(Exception e)
{
debug(e.toString());
}
break;
// Should never be reached, mode is handled by client
default:
System.out.println("\nUnspecified mode. Terminating Program.");
/*
sendStream.reset();
sendStream.write(0x0);
sendStream.write(0x5);
sendStream.write(0x0);
sendStream.write(0x4);
sendStream.write("Unhandled transfer mode".getBytes());
send(sendStream.toByteArray(), client, replyPort);
*/
return;
}
}
public static void send(byte[] response, DatagramSocket client, int port)
{
try
{
InetAddress IP = InetAddress.getByName(Client.serverAddress);
DatagramPacket packetToSend = new DatagramPacket(
response,
response.length,
IP,
port
);
client.send(packetToSend);
}
catch(Exception e)
{
System.out.println(e.toString());
}
}
public static void debug(String output)
{
if(Client.DEBUG)
System.out.println(output);
}
public static void debug(byte[] array)
{
if(Client.DEBUG)
{
String debug = "";
for(int x = 0; x < array.length; x++)
{
debug += " " + array[x];
}
System.out.println(debug);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment