Created
April 23, 2010 20:49
-
-
Save zoranzaric/377152 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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