How to receive big files in a server over UDP? - java

I have to send any size file from a client to a server using UDP. I send the datagrams in byte arrays. I split the file and send the datagrams. I know the server receives the datagrams but my problem is on how to put the datagrams in a File again. This is my code for the Client (I'll just put the code that splits and sends the file, I used JavaFX for the client).
Thank you all in advance.
private void sendFile(ActionEvent event) {
ArrayList<byte[]> listDatagrams;
DatagramSocket socket = null;
try {
socket = new DatagramSocket();
String serverName = "localhost";
InetAddress IPserver;
int serverPort = 6789;
byte[] data;
DatagramPacket request;
IPservidor = InetAddress.getByName(serverName);
data = Files.readAllBytes(file.toPath());
listDatagrams = splitFile(data);
for(byte[] datagrama : listDatagrams){
request= new DatagramPacket(datagrama, datagrama.length, IPserver, serverPort );
socket.send(request);
}
try {
fileOuputStream.write(respuesta.getData());
} finally {
fileOuputStream.close();
}
} catch (SocketException e) {
System.out.println("SocketException: " + e.getMessage());
} catch (IOException e) {
System.out.println("IOException: " + e.getMessage());
} finally {
if (socket != null) {
socket.close();
}
}
}
This is the method I used to split the File:
public ArrayList<byte []> splitFile(byte[] file){
ArrayList<byte[]> data = new ArrayList<>();
double nPartitions;
byte[] partition= new byte[MAX];
if(file.length>MAX){
nPartitions =Math.ceil(file.length/MAX);
for(int i=0; i<=nPartitions; i++)
for(int j=0; j<MAX; j++){
if(i==0){
if(j==file.length-1)
break;
partition[j]=file[j];
}else if(i==1){
if((j+MAX)==file.length-1){
break;
}else{
partition[j]=file[(j+MAX)];
}
}else{
if((j+MAX)>=file.length-1)
break;
partition[j]=file[(i*(MAX+1))+j];
}
}
data.add(partition);
}
}
return data;
}
And the code for the server:
public static void main(String args[]) {
DatagramSocket socket = null;
ArrayList<DatagramPacket> data= new ArrayList<>();
try {
int serverPort= 6789;
socket = new DatagramSocket(serverPort);
byte[] buffer;
buffer = new byte[5000];
while (true) {
DatagramPacket request;
request= new DatagramPacket(buffer, buffer.length);
socket.receive(request);
//I add the datagramas to an array list but I am not sure how to re arrenge them into a File. The server should always be on and be able to receive a second file if I want to
data.add(request);
}
} catch (SocketException e) {
System.out.println("SocketException: " + e.getMessage());
} catch (IOException e) {
System.out.println("IOException: " + e.getMessage());
} finally {
if (socket != null) {
socket.close();
}
}
}
Note: I know TCP is better but this is for school so

Related

Get string size from Datagram Packet

I'm new to programming and I'm studying sockets, maybe this question is stupid, but thank you for your help.
I'm trying to extract the size of a message sent to the server(number of characters), but the size of the message sent in the server response always has a different size.
Client
import java.net.*;
import java.io.*;
public class UDPClient {
public static void main(String args[]) {
// args give message contents and server hostname
if (args.length != 2) {
System.err.println("Usage: java UDPClient 'Text Message' <server hostname>");
System.exit(1);
}
DatagramSocket aSocket = null;
try {
aSocket = new DatagramSocket();
byte[] m = args[0].getBytes();
InetAddress aHost = InetAddress.getByName(args[1]);
int serverPort = 6789;
DatagramPacket request = new DatagramPacket(m, m.length, aHost, serverPort);
aSocket.send(request);
byte[] buffer = new byte[1000];
DatagramPacket reply = new DatagramPacket(buffer, buffer.length);
aSocket.receive(reply);
System.out.println("Reply: " + new String(reply.getData()));
System.out.println("Reply: " + reply.getData().toString().length());
} catch (SocketException e) {
System.out.println("Socket: " + e.getMessage());
} catch (IOException e) {
System.out.println("IO: " + e.getMessage());
} finally {
if (aSocket != null)
aSocket.close();
}
}
}
Server
import java.net.*;
import java.io.*;
public class UDPServer {
public static void main(String args[]) {
DatagramSocket aSocket = null;
try {
aSocket = new DatagramSocket(6789);
byte[] buffer = new byte[1000];
while (true) {
DatagramPacket request = new DatagramPacket(buffer, buffer.length);
aSocket.receive(request);
String m = new String(request.getData()).replaceAll(" ", "");
int tam = m.length();
DatagramPacket reply = new DatagramPacket(request.getData(), tam, request.getAddress(),
request.getPort());
aSocket.send(reply);
}
} catch (SocketException e) {
System.out.println("Socket: " + e.getMessage());
} catch (IOException e) {
System.out.println("IO: " + e.getMessage());
} finally {
if (aSocket != null)
aSocket.close();
}
}
}

How to make a asnychron REST Server with communication to external hardware

I am currently working on a REST server in Spring Boot that also communicates with external hardware via USB or TCP/IP. A command is sent to the external device, then I wait 2 seconds for the response and return it to the RestController.
For the USB communication I am currently using jSerialComm. For the TCP/IP communication I use sockets.
The whole thing also works so far when I use Thread.sleep. However, I would prefer it to be more dynamic, i.e. without the sleep, because TCP/IP can cause delays.
For me Java and REST are still new. Is there a way to let the server communicate asynchronously with the hardware?
#RestController
#RequestMapping("/api/v1")
public class CommController {
#GetMapping("/Version")
public String getVersion() {
Serial serial = new Serial();
String s_response = null;
s_response = serial.getVersion();
return s_response;
}
#GetMapping("/VersionTCPIP")
public String getVersionTCPIP() {
Serial serial = new Serial();
String s_response = null;
s_response = serial.getVersionTCPIP();
return s_response;
}
}
USB/serial-class:
public class Serial {
private static SerialPort port;
private static List<Byte> l_readBuffer;
private static final class MessageListener implements SerialPortDataListener
{
#Override
public int getListeningEvents()
{
return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
}
#Override
public void serialEvent(SerialPortEvent event)
{
if (event.getEventType() != SerialPort.LISTENING_EVENT_DATA_AVAILABLE)
return;
byte[] newData = new byte[port.bytesAvailable()];
int numRead = port.readBytes(newData, newData.length);
for (int i = 0; i < numRead; i++)
{
l_readBuffer.add(newData[i]);
}
System.out.println("Read " + numRead + " bytes.");
}
}
public String getVersion() {
SerialPort[] ports = SerialPort.getCommPorts();
port = null;
for (SerialPort currentPort :
ports)
{
if (currentPort.getDescriptivePortName().contains("XXX"))
{
port = currentPort;
break;
}
}
Objects.requireNonNull(port).setBaudRate(9600);
port.setParity(SerialPort.NO_PARITY);
port.setNumStopBits(SerialPort.ONE_STOP_BIT);
port.setNumDataBits(8);
port.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);
//port.clearRTS();
port.setComPortTimeouts(SerialPort.LISTENING_EVENT_DATA_AVAILABLE, 5000, 5000);
port.openPort();
MessageListener listener = new MessageListener();
port.addDataListener(listener);
l_readBuffer = new ArrayList<>();
byte[] sendBuffer = new byte[5];
// fill sendBuffer
port.writeBytes(sendBuffer, sendBuffer.length);
try
{
Thread.sleep(2000);
} catch (Exception e)
{
e.printStackTrace();
}
System.out.println("Data raw: " + l_readBuffer.toString());
byte[] ba_responseBuffer = new byte[l_readBuffer.size()];
for (int i = 0; i < l_readBuffer.size(); i++)
{
ba_responseBuffer[i] = l_readBuffer.get(i);
}
String s_version = new String(ba_responseBuffer);
System.out.println("Version: " + s_version);
port.removeDataListener();
port.closePort();
return s_version;
}
public String getVersionTCPIP()
{
Socket socket;
String s_versionString = null;
try
{
socket = new Socket();
socket.connect(new InetSocketAddress(s_hostnameTCPIP, i_port), 1000);
byte[] ba_sendBuffer = new byte[1024];
Arrays.fill(ba_sendBuffer, (byte) 0x00);
// fill sendBuffer
// send data
DataOutputStream dOut = new DataOutputStream(socket.getOutputStream());
dOut.write(ba_sendBuffer); // write the message
dOut.writeInt(i_sendLength); // write length of the message
dOut.flush();
try
{
Thread.sleep(2000);
} catch (Exception e)
{
e.printStackTrace();
}
byte[] ba_responseBuffer = new byte[0];
if (socket.isConnected())
{
try
{
InputStream inFromServer = socket.getInputStream();
DataInputStream dIn = new DataInputStream(inFromServer);
synchronized (dIn)
{
int length = dIn.available();
ba_responseBuffer = new byte[length];
// receive data
dIn.readFully(ba_responseBuffer);
}
} catch (IOException ex)
{
ex.printStackTrace();
}
String s_version = new String(ba_responseBuffer);
System.out.println("Version: " + s_version);
s_versionString = s_version;
socket.close();
}
} catch (IOException e)
{
e.printStackTrace();
}
return s_versionString;
}
}

Socket don't save values in list

I'm new in network developing in Java and I want to create a simple Socket server, that get values from client and collects all of them in ArrayList. I wrote an example code, but in server side it not collecting the strings. This is my server side:
Server
public class ServerSideSocket extends Thread{
private ServerSocket serverSocket;
private Socket socket;
private ArrayList<String> list = new ArrayList<String>();
DataInputStream inData;
DataOutputStream outData;
public ServerSideSocket(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void run() {
while(true) {
try{
System.out.println("Waiting for connection...");
socket = serverSocket.accept();
System.out.println("Connected!" );
inData = new DataInputStream(socket.getInputStream());
outData = new DataOutputStream(socket.getOutputStream());
System.out.println(inData.readUTF());
list.add(inData.readUTF());
System.out.println("------------ VALUES ---------");
for (String value: list) {
System.out.println(value);
}
System.out.println("------------ END VALUES ---------");
outData.writeUTF("Message saved!");
outData.flush();
} catch (SocketException ex) {
ex.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inData.close();
outData.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
int port = 9999;
try {
Thread t = new ServerSideSocket(port);
t.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
and Client:
public class ClientSideSocket {
public static void main(String[] args) {
String serverName = "localhost";
int port = 9999;
String line = "";
try {
System.out.println("Connecting to " + serverName + " on port " + port);
Socket client = new Socket(serverName, port);
System.out.println("Just connected to " + client.getRemoteSocketAddress());
OutputStream out = client.getOutputStream();
DataOutputStream outData = new DataOutputStream(out);
InputStream in = client.getInputStream();
DataInputStream inData = new DataInputStream(in);
outData.writeUTF("Simple text");
outData.flush();
System.out.println("Response from server: " + inData.readUTF());
System.out.println("You can write more messages!");
System.out.println();
client.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
what is wrong in my code?
This happens because you try to read twice from the data stream by calling inData.readUTF() method. First call successfully reads data from the stream, but instead of saving result you try to perform another read 2 lines below.
readUTF() is blocking method and thus it waits for another portion of data which never comes from the same client. That's why your server hungs forever
What you want to do is to read once and store result into local variable:
String res = inData.readUTF();
list.add(res);
You are writing data once as "Simple Text" which you can read only once.
Where in your code you are first reading it
System.out.println(inData.readUTF());
list.add(inData.readUTF());
Instead of this you should first store it in a String and then use it.
String message = inData.readUTF();
System.out.println(message);
list.add(message);

Sending a file with Java Sockets, losing data

I'm trying to send a file from a client to a server with Sockets in Java. It works fine when I am testing on the same machine, but when I test across different machines, I lose large chunks of data, resulting in a corrupted file. If I try to send a very small file (<20 bytes), it doesn't even reach the println inside the server's while loop.
Here is my code:
Server.java
package edu.mst.cs.sensorreceiver;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private static final int PORT = 51111;
private static final int CHUNK_SIZE = 1024;
private static final File _downloadDir = new File("downloads/");
public static void main(String[] args) {
if (!_downloadDir.exists()) {
if (!_downloadDir.mkdirs()) {
System.err.println("Error: Could not create download directory");
}
}
Socket socket = null;
try {
ServerSocket server = new ServerSocket(PORT);
while (true) {
System.out.println("Waiting for connection...");
socket = server.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String name = in.readLine();
File file = new File(_downloadDir, name);
String size = in.readLine();
int fileSize;
try {
fileSize = Integer.parseInt(size);
} catch (NumberFormatException e) {
System.err.println("Error: Malformed file size:" + size);
e.printStackTrace();
return;
}
System.out.println("Saving " + file + " from user... (" + fileSize + " bytes)");
saveFile(file, socket.getInputStream());
System.out.println("Finished downloading " + file + " from user.");
if (file.length() != fileSize) {
System.err.println("Error: file incomplete");
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void saveFile(File file, InputStream inStream) {
FileOutputStream fileOut = null;
try {
fileOut = new FileOutputStream(file);
byte[] buffer = new byte[CHUNK_SIZE];
int bytesRead;
int pos = 0;
while ((bytesRead = inStream.read(buffer, 0, CHUNK_SIZE)) >= 0) {
pos += bytesRead;
System.out.println(pos + " bytes (" + bytesRead + " bytes read)");
fileOut.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOut != null) {
try {
fileOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println("Finished, filesize = " + file.length());
}
}
Client.java
package edu.mst.cs.sensorreceiver;
import java.io.*;
import java.net.Socket;
public class Client {
private static final String HOSTNAME = "131.151.163.153";
private static final int PORT = 51111;
private static final int CHUNK_SIZE = 1024;
public static void main(String[] args) {
sendFile(args[0]);
}
private static void sendFile(String path) {
if (path == null) {
throw new NullPointerException("Path is null");
}
File file = new File(path);
Socket socket = null;
try {
System.out.println("Connecting to server...");
socket = new Socket(HOSTNAME, PORT);
System.out.println("Connected to server at " + socket.getInetAddress());
PrintStream out = new PrintStream(socket.getOutputStream(), true);
out.println(file.getName());
out.println(file.length());
System.out.println("Sending " + file.getName() + " (" + file.length() + " bytes) to server...");
writeFile(file, socket.getOutputStream());
System.out.println("Finished sending " + file.getName() + " to server");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void writeFile(File file, OutputStream outStream) {
FileInputStream reader = null;
try {
reader = new FileInputStream(file);
byte[] buffer = new byte[CHUNK_SIZE];
int pos = 0;
int bytesRead;
while ((bytesRead = reader.read(buffer, 0, CHUNK_SIZE)) >= 0) {
outStream.write(buffer, 0, bytesRead);
outStream.flush();
pos += bytesRead;
System.out.println(pos + " bytes (" + bytesRead + " bytes read)");
}
} catch (IndexOutOfBoundsException e) {
System.err.println("Error while reading file");
e.printStackTrace();
} catch (IOException e) {
System.err.println("Error while writing " + file.toString() + " to output stream");
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
I've been working on this for hours and I have made almost no progress. I thought I had a pretty good understanding of how reading/writing from streams works, but clearly I'm missing something here.
Again, everything works perfectly when I am sending and receiving from the same machine. But if I try to send a file between two computers, even if the two are on the same LAN, I lose a lot of the data that was sent.
Can anybody figure out my problem? I've already tried everything I could think of.
You appear to be mixing chunked data and line oriented operation. I suggest you use a DataInputStream on the server, and a DataOutputStream. Starting on the client, something like
private static void sendFile(String path) {
if (path == null) {
throw new NullPointerException("Path is null");
}
File file = new File(path);
Socket socket = null;
try {
System.out.println("Connecting to server...");
socket = new Socket(HOSTNAME, PORT);
System.out.println("Connected to server at "
+ socket.getInetAddress());
try (DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(socket.getOutputStream()));) {
dos.writeUTF(file.getName());
dos.writeLong(file.length());
System.out.println("Sending " + file.getName() + " ("
+ file.length() + " bytes) to server...");
writeFile(file, dos);
System.out.println("Finished sending " + file.getName()
+ " to server");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Then on the server
socket = server.accept();
DataInputStream dis = new DataInputStream(socket.getInputStream());
String name = dis.readUTF();
File file = new File(_downloadDir, name);
long fileSize = dis.readLong();
System.out.println("Saving " + file + " from user... ("
+ fileSize + " bytes)");
In your code you mixes all that should never be mixed: (a) input/output streams, (b) PrintStream, (c) stream reader and (d) buffered reader. this hell leads to described behavior.
you should use the only object (stream) to write all your staff. and the only object (stream) to read all your staff.
I can recommend you to use either
OutputStream out = new BufferedOutputStream(socket.getOutputStream()); // for writing
InputStream in = new BufferedInputStream(socket.getInputStream()); // for reading
or
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
The first is more flexible, the second is simpler to use.

DatagramSocket will randomly stop receiving packets (still receives sometimes)

I am trying to implement a TFTP client in Java. The client works perfectly on Localhost and will sometimes work sending to a TFTP server over the network. However, sometimes my DatagramSocket will randomly stop receiving packets. It will send a read/write request but it never receives the next message the server tries to send back. I've checked Wireshark, and the server is for sure receiving and trying to send. And firewalls are turned off where the need to be. Can't figure out what the problem is. Here is the code I am using:
public class TFTPClient {
String filename;
String mode;
boolean read;
PacketBuilder builder;
String IP;
JFrame frame;
public TFTPClient(String uifilename, String uimode, boolean uiread, String uiIP, JFrame uiFrame){
this.filename = uifilename;
this.read = uiread;
this.mode = uimode;
this.IP = uiIP;
builder = new PacketBuilder();
this.frame = uiFrame;
}
/*
* Method choses between reading a file and writing a file based on boolean selected in main UI.
*/
public void startTFTP() throws IOException{
if (read){
readFile();
}
else{
writeFile();
}
}
/*
* Method is used for writing a file
*/
private void writeFile() throws IOException{
byte[] WRQ = builder.getWRQ(filename,mode);
String filenameAndExtension = filename;
RandomAccessFile f = new RandomAccessFile(filenameAndExtension, "r");
byte[] fileBytes = new byte[(int)f.length()];
f.read(fileBytes);
f.close();
DatagramSocket TFTPSocket = new DatagramSocket();
TFTPSocket.setSoTimeout(5000);
//create the packet and send to port 69 of the given IP
DatagramPacket wrqPacket = new DatagramPacket(WRQ, WRQ.length,
InetAddress.getByName(IP), 69);
try {
TFTPSocket.send(wrqPacket);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
byte[] ackByte = new byte[4];
DatagramPacket ackPacket = new DatagramPacket(ackByte,
ackByte.length);
int blockNumber = 0;
DatagramPacket dataPacket;
boolean terminateOnNextAck = false;
boolean needExtraDataPacket = false;
int currentIndex = 0;
while(true)
{
TFTPSocket.receive(ackPacket);
System.out.println("Server acked " + ackByte[3]);
System.out.println("Expected ack " + blockNumber);
blockNumber++;
if(terminateOnNextAck){
break;
}
byte[]DATAdata;
if (needExtraDataPacket){
DATAdata = new byte[0];
terminateOnNextAck = true;
}
else if (currentIndex + 512 > fileBytes.length){
//This is our last byte. Length will be smaller than 508
DATAdata = new byte [fileBytes.length - currentIndex];
terminateOnNextAck = true;
}
else{
DATAdata = new byte[512];
}
if (currentIndex + 512 ==fileBytes.length){
needExtraDataPacket = true;
}
for (int i = 0; i<DATAdata.length; i++){
DATAdata[i] = fileBytes[currentIndex];
currentIndex++;
}
byte[] DATA = builder.getData(DATAdata, blockNumber);
dataPacket = new DatagramPacket(DATA, DATA.length,
InetAddress.getByName(IP),ackPacket.getPort());
try {
TFTPSocket.send(dataPacket);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
TFTPSocket.close();
System.out.println("Write sucessful");
}
/*
* Method is used for reading a file
*/
private void readFile() throws IOException{
//Get RRQ packet
byte[] RRQ = builder.getRRQ(filename,mode);
StringBuffer fileText = new StringBuffer();
DatagramSocket TFTPSocket = new DatagramSocket();
TFTPSocket.setSoTimeout(5000);
//create the packet and send to port 69 of the given IP
DatagramPacket rrqPacket = new DatagramPacket(RRQ, RRQ.length,
InetAddress.getByName(IP), 69);
try {
TFTPSocket.send(rrqPacket);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
byte[] dataByte = new byte[516];
for (int i = 516;i<516;i++){
dataByte[i] = 0;
}
DatagramPacket dataPacket = new DatagramPacket(dataByte,
dataByte.length);
System.out.println("Client: Waiting for packet.");
DatagramPacket ackPacket;
boolean error = false;
while(true)
{
TFTPSocket.receive(dataPacket);
System.out.println(TFTPSocket.getLocalPort());
if (dataByte[1] == 5){
error = true;
break;
}
fileText.append(new String(dataPacket.getData(),0,dataPacket.getLength()));
byte blockNumbers[] = new byte[2];
blockNumbers[0] = dataByte[2];
blockNumbers[1] = dataByte[3];
byte[] ACK = builder.getACK(blockNumbers);
ackPacket = new DatagramPacket(ACK, ACK.length,
InetAddress.getByName(IP),dataPacket.getPort());
try {
TFTPSocket.send(ackPacket);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
if (dataByte[515] == 0){
break;
}
dataByte[515] = 0;
}
if (!error){
JOptionPane.showMessageDialog(frame, "Read Successful!");
System.out.println(fileText);
}
else{
JOptionPane.showMessageDialog(frame,"Error from server: " + new String(dataPacket.getData(),0,dataPacket.getLength()));
}
}
}
The issue turned out to be something with Mac OS X. The program runs fine on Windows. Not entirely sure why, though.

Categories