Safe/Trustworthy File Transfer Using UDP (Datagrams) Sockets in Java
This file contains 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
package FileTransfer; | |
import java.io.File; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.net.DatagramPacket; | |
import java.net.DatagramSocket; | |
import java.net.InetAddress; | |
/** | |
* | |
* @author Absalom Herrera | |
*/ | |
public class FileReceive { | |
public static void main(String[] args) { | |
System.out.println("Ready to receive!"); | |
int port = 1234; // Change this to the desired port | |
String serverRoute = "C:\\Users\\elpat\\Documents\\FileTransferTest"; // Change this to the desired directory route (Where the file will be stored) | |
createFile(port, serverRoute); // Creating the file | |
} | |
public static void createFile (int port, String serverRoute){ | |
try{ | |
DatagramSocket socket = new DatagramSocket(port); | |
byte[] receiveFileName = new byte[1024]; // Where we store the data of datagram of the name | |
DatagramPacket receiveFileNamePacket = new DatagramPacket(receiveFileName, receiveFileName.length); | |
socket.receive(receiveFileNamePacket); // Receive the datagram with the name of the file | |
System.out.println("Receiving file name"); | |
byte [] data = receiveFileNamePacket.getData(); // Reading the name in bytes | |
String fileName = new String(data, 0, receiveFileNamePacket.getLength()); // Converting the name to string | |
System.out.println("Creating file"); | |
File f = new File (serverRoute + "\\" + fileName); // Creating the file | |
FileOutputStream outToFile = new FileOutputStream(f); // Creating the stream through which we write the file content | |
receiveFile(outToFile, socket); // Receiving the file | |
}catch(Exception ex){ | |
ex.printStackTrace(); | |
System.exit(1); | |
} | |
} | |
private static void receiveFile(FileOutputStream outToFile, DatagramSocket socket) throws IOException { | |
System.out.println("Receiving file"); | |
boolean flag; // Have we reached end of file | |
int sequenceNumber = 0; // Order of sequences | |
int foundLast = 0; // The las sequence found | |
while (true) { | |
byte[] message = new byte[1024]; // Where the data from the received datagram is stored | |
byte[] fileByteArray = new byte[1021]; // Where we store the data to be writen to the file | |
// Receive packet and retrieve the data | |
DatagramPacket receivedPacket = new DatagramPacket(message, message.length); | |
socket.receive(receivedPacket); | |
message = receivedPacket.getData(); // Data to be written to the file | |
// Get port and address for sending acknowledgment | |
InetAddress address = receivedPacket.getAddress(); | |
int port = receivedPacket.getPort(); | |
// Retrieve sequence number | |
sequenceNumber = ((message[0] & 0xff) << 8) + (message[1] & 0xff); | |
// Check if we reached last datagram (end of file) | |
flag = (message[2] & 0xff) == 1; | |
// If sequence number is the last seen + 1, then it is correct | |
// We get the data from the message and write the ack that it has been received correctly | |
if (sequenceNumber == (foundLast + 1)) { | |
// set the last sequence number to be the one we just received | |
foundLast = sequenceNumber; | |
// Retrieve data from message | |
System.arraycopy(message, 3, fileByteArray, 0, 1021); | |
// Write the retrieved data to the file and print received data sequence number | |
outToFile.write(fileByteArray); | |
System.out.println("Received: Sequence number:" + foundLast); | |
// Send acknowledgement | |
sendAck(foundLast, socket, address, port); | |
} else { | |
System.out.println("Expected sequence number: " + (foundLast + 1) + " but received " + sequenceNumber + ". DISCARDING"); | |
// Re send the acknowledgement | |
sendAck(foundLast, socket, address, port); | |
} | |
// Check for last datagram | |
if (flag) { | |
outToFile.close(); | |
break; | |
} | |
} | |
} | |
private static void sendAck(int foundLast, DatagramSocket socket, InetAddress address, int port) throws IOException { | |
// send acknowledgement | |
byte[] ackPacket = new byte[2]; | |
ackPacket[0] = (byte) (foundLast >> 8); | |
ackPacket[1] = (byte) (foundLast); | |
// the datagram packet to be sent | |
DatagramPacket acknowledgement = new DatagramPacket(ackPacket, ackPacket.length, address, port); | |
socket.send(acknowledgement); | |
System.out.println("Sent ack: Sequence Number = " + foundLast); | |
} | |
} |
This file contains 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
package FileTransfer; | |
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.SocketTimeoutException; | |
import javax.swing.JFileChooser; | |
/** | |
* | |
* @author Absalom Herrera | |
*/ | |
public class FileSend { | |
/* | |
Method for getting the file to be send and send its name | |
*/ | |
private void ready(int port, String host) { | |
System.out.println("Choosing file to send"); | |
try { | |
DatagramSocket socket = new DatagramSocket(); | |
InetAddress address = InetAddress.getByName(host); | |
String fileName; | |
JFileChooser jfc = new JFileChooser(); // Choosing the file to send | |
jfc.setFileSelectionMode(JFileChooser.FILES_ONLY); // Only files can be choosed (not directories) | |
if (jfc.isMultiSelectionEnabled()) { // Only one file at a time (no multiple selection) | |
jfc.setMultiSelectionEnabled(false); | |
} | |
int r = jfc.showOpenDialog(null); | |
if (r == JFileChooser.APPROVE_OPTION) { // If a file is choosed | |
File f = jfc.getSelectedFile(); | |
fileName = f.getName(); | |
byte[] fileNameBytes = fileName.getBytes(); // File name as bytes to send it | |
DatagramPacket fileStatPacket = new DatagramPacket(fileNameBytes, fileNameBytes.length, address, port); // File name packet | |
socket.send(fileStatPacket); // Sending the packet with the file name | |
byte[] fileByteArray = readFileToByteArray(f); // Array of bytes the file is made of | |
sendFile(socket, fileByteArray, address, port); // Entering the method to send the actual file | |
} | |
socket.close(); | |
} catch (Exception ex) { | |
ex.printStackTrace(); | |
System.exit(1); | |
} | |
} | |
private void sendFile(DatagramSocket socket, byte[] fileByteArray, InetAddress address, int port) throws IOException { | |
System.out.println("Sending file"); | |
int sequenceNumber = 0; // For order | |
boolean flag; // To see if we got to the end of the file | |
int ackSequence = 0; // To see if the datagram was received correctly | |
for (int i = 0; i < fileByteArray.length; i = i + 1021) { | |
sequenceNumber += 1; | |
// Create message | |
byte[] message = new byte[1024]; // First two bytes of the data are for control (datagram integrity and order) | |
message[0] = (byte) (sequenceNumber >> 8); | |
message[1] = (byte) (sequenceNumber); | |
if ((i + 1021) >= fileByteArray.length) { // Have we reached the end of file? | |
flag = true; | |
message[2] = (byte) (1); // We reached the end of the file (last datagram to be send) | |
} else { | |
flag = false; | |
message[2] = (byte) (0); // We haven't reached the end of the file, still sending datagrams | |
} | |
if (!flag) { | |
System.arraycopy(fileByteArray, i, message, 3, 1021); | |
} else { // If it is the last datagram | |
System.arraycopy(fileByteArray, i, message, 3, fileByteArray.length - i); | |
} | |
DatagramPacket sendPacket = new DatagramPacket(message, message.length, address, port); // The data to be sent | |
socket.send(sendPacket); // Sending the data | |
System.out.println("Sent: Sequence number = " + sequenceNumber); | |
boolean ackRec; // Was the datagram received? | |
while (true) { | |
byte[] ack = new byte[2]; // Create another packet for datagram ackknowledgement | |
DatagramPacket ackpack = new DatagramPacket(ack, ack.length); | |
try { | |
socket.setSoTimeout(50); // Waiting for the server to send the ack | |
socket.receive(ackpack); | |
ackSequence = ((ack[0] & 0xff) << 8) + (ack[1] & 0xff); // Figuring the sequence number | |
ackRec = true; // We received the ack | |
} catch (SocketTimeoutException e) { | |
System.out.println("Socket timed out waiting for ack"); | |
ackRec = false; // We did not receive an ack | |
} | |
// If the package was received correctly next packet can be sent | |
if ((ackSequence == sequenceNumber) && (ackRec)) { | |
System.out.println("Ack received: Sequence Number = " + ackSequence); | |
break; | |
} // Package was not received, so we resend it | |
else { | |
socket.send(sendPacket); | |
System.out.println("Resending: Sequence Number = " + sequenceNumber); | |
} | |
} | |
} | |
} | |
private static byte[] readFileToByteArray(File file) { | |
FileInputStream fis = null; | |
// Creating a byte array using the length of the file | |
// file.length returns long which is cast to int | |
byte[] bArray = new byte[(int) file.length()]; | |
try { | |
fis = new FileInputStream(file); | |
fis.read(bArray); | |
fis.close(); | |
} catch (IOException ioExp) { | |
ioExp.printStackTrace(); | |
} | |
return bArray; | |
} | |
public static void main(String[] args) { | |
int port = 1234; | |
String host = "127.0.0.1"; // local host | |
FileSend fs = new FileSend(); | |
fs.ready(port, host); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment