Skip to content

Instantly share code, notes, and snippets.

@zoranzaric
Created April 23, 2010 20:49
Show Gist options
  • Save zoranzaric/377152 to your computer and use it in GitHub Desktop.
Save zoranzaric/377152 to your computer and use it in GitHub Desktop.
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Scanner;
/**
* Client for a network programming proof of concept.
*
* @author Zoran Zarić <[email protected]>
*
*/
public class Client {
/**
* The client's id.
*/
private String id;
/**
* The server's hostname or ip-address.
*/
private String host;
/**
* The server's port to connect to.
*/
private int port;
/**
* The path to the output file.
*/
private String outputFilePath;
/**
* A table that holds the status of all chunks of the file.
*/
private HashMap<String, Boolean> recieved;
/**
* Constructor for the client.
*
* @param id The client's id.
* @param host The server's hostname or ip-address.
* @param port The server's port to connect to.
*/
public Client(String id, String host, int port) {
this.id = id;
this.host = host;
this.port = port;
this.outputFilePath = "/tmp/testfile_" + this.id + ".out";
try {
//this.recieveFile();
// Retrieve the list of all chunks for the file.
this.recieved = this.recieveList();
//this.recieveFilePart("c4ca4238a0b92382dcc509a6f75849b"); // Offset: 0
//this.recieveFilePart("c9e174f5b3f9fc8ea15d152add07294"); // Offset: 1687552
// Retrieve all chunks
for (String hash : this.recieved.keySet()) {
this.recieveFilePart(hash);
}
} catch(IOException e) {
// Doh!
System.err.println("Something went wrong!");
}
}
/**
* Hashes a index. We have this abstraction-method so we can replace the hash-function.
*
* @param index
* @return The hashed index. If the chosen hash-function doesn't exist the index as a string is returned.
*/
private String hash(int index) {
byte[] defaultBytes = String.valueOf(index).getBytes();
try{
MessageDigest algorithm = MessageDigest.getInstance("MD5");
algorithm.reset();
algorithm.update(defaultBytes);
byte messageDigest[] = algorithm.digest();
StringBuffer hexString = new StringBuffer();
for (int i=0;i<messageDigest.length;i++) {
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
}
return hexString.toString();
}catch(NoSuchAlgorithmException nsae){
// If the hash-function we chose doesn't exist we just return the index as a string.
return String.valueOf(index);
}
}
/**
* Creates a zero-filled file with a given size.
*
* @param size
*/
private void createFile(long size) {
File file = new File(this.outputFilePath);
try {
FileOutputStream fileOut = new FileOutputStream(file);
for (long i = 0; i < size; i++) {
try {
fileOut.write(0);
} catch (IOException ioe) {
}
}
} catch (FileNotFoundException fnfe) {
}
}
/**
* Retrieves the list of chunks from the server.
*
* @return The table of chunks, all marked as not recieved
* @throws IOException
*/
private HashMap<String, Boolean> recieveList() throws IOException {
try {
// Connect to the server
Socket serverSocket = new Socket(this.host, this.port);
// We want to read strings from the server, so we need a Scanner.
Scanner in = new Scanner(serverSocket.getInputStream());
// We want to write strings to the server, so we need a PrintWriter.
PrintWriter out = new PrintWriter(serverSocket.getOutputStream());
// Send the LIST-request to the server
out.println("LIST");
out.flush();
HashMap<String, Boolean> recieved = new HashMap<String, Boolean>();
// Retrieve all hashes, puts them into the table and marks them as not received.
String hash = "";
while(in.hasNext()) {
hash = in.nextLine();
recieved.put(hash, false);
}
// Disconnect form the server
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// Creates the dummy file
//TODO retrieve the correct file-size
this.createFile(16384 * 192);
return recieved;
} catch (UnknownHostException e) {
return null;
} catch (IOException e) {
return null;
}
}
/**
* Receives a whole file.
*
* @throws IOException
*/
private void recieveFile() throws IOException {
// Connect to the server
Socket serverSocket = new Socket(this.host, this.port);
// We want to recieve bytes from the server, so we need a InputStream
InputStream in = serverSocket.getInputStream();
// We want to write strings to the server, so we need a PrintWriter
PrintWriter out = new PrintWriter(serverSocket.getOutputStream());
// Send the ALL-request to the server
out.println("ALL");
out.flush();
// Initialize the file
File file = new File(this.outputFilePath);
// Initialize the FileOutputStream
FileOutputStream fileOut = new FileOutputStream(file);
int count;
byte[] buffer = new byte[16384];
while ((count = in.read(buffer)) > 0) {
fileOut.write(buffer, 0, count);
fileOut.flush();
}
// Close the FileOutputStream
fileOut.close();
// Disconnect from server
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Receives a file-chunk by index.
*
* @param index
* @throws IOException
*/
private void recieveFilePart(int index) throws IOException {
String hash = this.hash(index);
this.recieveFilePart(hash);
}
/**
* Receives a file-chunk by hash.
*
* @param hash
* @throws IOException
*/
private void recieveFilePart(String hash) throws IOException {
// Connect to the server
Socket serverSocket = new Socket(this.host, this.port);
// We want to receive bytes from the server, so we need a InputStream
InputStream in = serverSocket.getInputStream();
// We want to read strings from the server, so we need a Scanner.
Scanner inScanner = new Scanner(in);
// We want to write strings to the server, so we need a PrintWriter
PrintWriter out = new PrintWriter(serverSocket.getOutputStream());
System.out.println("Recieving " + hash);
// Send the GET-request to the server
out.println("GET:" + hash);
out.flush();
// Retrieve the offset in the file
long offset = new Long(inScanner.nextLine());
System.out.println("Offset: " + offset);
// Initialize the file
File file = new File(this.outputFilePath);
// We want to write to a given place in the file so we need a RandomAccessFile
RandomAccessFile fileOut = new RandomAccessFile(file, "rw");
// Set the pointer to the offset
fileOut.seek(offset);
// Retrieve the chunk
byte[] buffer = new byte[16384];
int len = in.read(buffer, 0, 16384);
//TODO the received Chunk is to small and the data is corrupt
// Write the chunk to the file
fileOut.write(buffer, 0, len);
// Close the RandomAccessFile
fileOut.close();
// Mark the Chunk as received
this.recieved.put(hash, true);
// Disconnect from the server
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @param args
*/
public static void main(String[] args) {
Client c = new Client(((args.length>0) ? args[0] : "default-client"), "localhost", 8888);
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Scanner;
/**
* Server for a network programming proof of concept.
*
* @author Zoran Zarić <[email protected]>
*
*/
public class Server {
/**
* The default port the server binds to.
*/
private static int PORT = 8888;
/**
* The size of a chunk.
*/
private static int CHUNKSIZE = 16384;
/**
* The socket clients connect to.
*/
private ServerSocket serverSocket;
/**
* The port the server binds to.
*/
private int port;
/**
* The file-path of the served file.
*/
private String filePath;
/**
* A table of chunk-tables for more than one file.
* At the moment this isn't really used, in the future it is planned that more than one file can be served.
*/
private HashMap<String, HashMap<String, Integer>> fileMap;
/**
* The constructor for the server.
*
* @param port The port to bind to.
* @param filePath The filepath for the file to be served.
*/
public Server(int port, String filePath) {
this.port = port;
this.filePath = filePath;
this.fileMap = new HashMap<String, HashMap<String,Integer>>();
// Bind to the given port.
this.openPort(this.port);
}
/**
* Hashes a index. We have this abstraction-method so we can replace the hash-function.
*
* @param index
* @return The hashed index. If the chosen hash-function doesn't exist the index as a string is returned.
*/
private String hash(int index) {
byte[] defaultBytes = String.valueOf(index).getBytes();
try{
MessageDigest algorithm = MessageDigest.getInstance("MD5");
algorithm.reset();
algorithm.update(defaultBytes);
byte messageDigest[] = algorithm.digest();
StringBuffer hexString = new StringBuffer();
for (int i=0;i<messageDigest.length;i++) {
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
}
return hexString.toString();
}catch(NoSuchAlgorithmException nsae){
// If the hash-function we chose doesn't exist we just return the index as a string.
return String.valueOf(index);
}
}
/**
* Gets a chunk-table. If it doesn't exist yet it is generated.
*
* @param file
* @return
*/
private HashMap<String, Integer> getHashMap(File file) {
if (this.fileMap.containsKey(file.getAbsolutePath())) {
// If the chunk-table already exists return it
return this.fileMap.get(file.getAbsolutePath());
} else {
// Initialize a new chunk-table
HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
// Calculate how many chunks we need
long chunks = file.length() / CHUNKSIZE;
long lastChunkSize = file.length() % CHUNKSIZE;
if ( lastChunkSize != 0) {
chunks++;
}
// Fill the chunk-table
for (int i = 1; i <= chunks; i++) {
hashMap.put(hash(i), i);
}
this.fileMap.put(file.getAbsolutePath(), hashMap);
return hashMap;
}
}
/**
* Open the port.
*
* @param port
*/
private void openPort(int port) {
try {
this.serverSocket = new ServerSocket(port);
} catch(IOException e) {
System.out.println("Couldn't listen on Port " + port + "!");
System.exit(-1);
}
}
/**
* Handles a client-request.
* Possible requests are:
* ALL - send a whole file
* LIST - send a chunk-list
* GET:hash - send a chunk
*/
public void handleRequest() {
Socket client = null;
try {
// Accept a client-connection
client = this.serverSocket.accept();
// We want to read strings from the client, so we need a Scanner.
Scanner in = new Scanner(client.getInputStream());
//in.useDelimiter(".");
// Read the request
String request = in.nextLine();
// handle the request
if (request.equals("ALL")) {
this.sendFile(client);
} else if (request.equals("LIST")) {
this.sendHashMap(client);
} else if (request.startsWith("GET:")) {
String[] requestParts = request.split(":");
this.sendFilePart(client, requestParts[1]);
}else {
System.out.println("Unknown request: " + request);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// close the client-connection
if (client!=null) {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* Send a whole file to the client.
*
* @param client
* @throws IOException
*/
private void sendFile(Socket client) throws IOException {
Scanner in = new Scanner(client.getInputStream());
PrintWriter printOut = new PrintWriter(client.getOutputStream(), true);
// We want to write bytes to the client, so we need a OutputStream
OutputStream out = client.getOutputStream();
// Initialize the file an check for existance
File file = new File(this.filePath);
if (!file.exists()) {
System.out.println("The file doesn't exist!");
System.exit(-1);
}
// initialize the FileInputStream
InputStream fileInput = null;
try {
fileInput = new FileInputStream(file);
} catch (FileNotFoundException e) {
System.out.println("The file doesn't exist!");
System.exit(-1);
}
byte[] buffer = new byte[CHUNKSIZE];
int readData;
System.out.println("Sending file...");
// Read the file and send it to the client
int sendcounter = 0;
while ( (readData = fileInput.read(buffer)) != -1 ) {
out.write(buffer, 0, readData);
System.out.print(".");
sendcounter++;
}
System.out.println("\nfinished (" + sendcounter +")!");
}
/**
* Send a chunk to the client.
*
* @param client
* @param hash
* @throws IOException
*/
private void sendFilePart(Socket client, String hash) throws IOException {
Scanner in = new Scanner(client.getInputStream());
// We want to write strings to the client, so we need a PrintWriter
PrintWriter printOut = new PrintWriter(client.getOutputStream(), true);
// We want to write bytes to the client, so wee need a OutputStream
OutputStream out = client.getOutputStream();
// Initialize the file and check for existance
File file = new File(this.filePath);
if (!file.exists()) {
System.out.println("The file doesn't exist!");
System.exit(-1);
}
// Calculate how many chunks the file consists of
long chunks = file.length() / CHUNKSIZE;
long lastChunkSize = file.length() % CHUNKSIZE;
if ( lastChunkSize != 0) {
chunks++;
}
// Get the chunk-table
HashMap<String, Integer> hashMap = this.getHashMap(file);
// Get the index for the requested chunk
int index = hashMap.get(hash).intValue();
System.out.println("Hash: " + hash + " Index: " + index);
// Calculate the offset in the file for the requested chunk
long offset = (index-1) * CHUNKSIZE;
// Initialize the FileInputStream
FileInputStream fileInput = null;
try {
fileInput = new FileInputStream(file);
} catch (FileNotFoundException e) {
System.out.println("The file doesn't exist!");
System.exit(-1);
}
byte[] buffer = new byte[CHUNKSIZE];
System.out.println("Reading from file at offset: " + offset);
// Move the pointer to the offset
fileInput.skip(offset);
// Read the chunk
int len = fileInput.read(buffer, 0, CHUNKSIZE);
System.out.println("Sending to client");
// Send the offset to the client
printOut.println(String.valueOf(offset));
// Send the chunk to the client
out.write(buffer, 0, len);
}
/**
* Send a hash-list to the client.
*
* @param client
* @throws IOException
*/
private void sendHashMap(Socket client) throws IOException {
Scanner in = new Scanner(client.getInputStream());
OutputStream out = client.getOutputStream();
// We want to write strings to the client, so we need a PrintWriter
PrintWriter printOut = new PrintWriter(client.getOutputStream(), true);
// Initialize the file and check for existance
File file = new File(this.filePath);
if (!file.exists()) {
System.out.println("The file doesn't exist!");
System.exit(-1);
}
// Get the chunk-table
HashMap<String, Integer> hashMap = this.getHashMap(file);
// Send the hashes to the client
for (String hash : hashMap.keySet()) {
printOut.println(hash);
}
}
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
Server s = new Server(PORT, ((args.length>0) ? args[0] : "/tmp/testfile"));
while(true) {
s.handleRequest();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment