How can erase the contents of an array in Java with safety? - java

I'm trying to receive data from a client and then log it onto the console.
Here is how i do this:
private final int MAX_PACKET_SIZE = 1024;
private byte[] data = new byte[MAX_PACKET_SIZE];
private void receive() {
new Thread(() -> {
while (running) {
DatagramPacket packet = new DatagramPacket(data, data.length);
try {
socket.receive(packet);
sPort = packet.getPort();
ip = packet.getAddress();
address = ip.toString();
} catch (Exception e) {
e.printStackTrace();
}
String messageToPrint = new String(packet.getData());
System.out.println(messageToPrint.trim() + " " + address + " | " + sPort);
}
}).start();
}
When it comes to printing my messageToPrint it actually repeats the last one, and reprinting it with a newer one.
I've figured out what is the problem though.
If i put allocation of the array data inside the while loop, everything works fine and i don't get the previous message again, just current one.
I don't really want to do this, because allocation inside loops not a good idea so i need somehow to clear my array before new data comes in.
The output without allocation inside the loop is:
Console: past message
Console: (imagine i typed hello) hellomessage
and so on.

Create the packet outside of the loop, and also retrieve size data from the packet. (Otherwise you'll print the entire array which could contain the trailing text of the last message received)
final DatagramPacket packet = new DatagramPacket(data, data.length);
while (running) {
try {
socket.receive(packet);
...
final String messageToPrint = new String(
packet.getData(),
packet.getOffset(),
packet.getLength());
...

Related

UDP client does not receive bytes

This question has been asked a lot, but so far, none of the solutions that I applied from previous answers have helped me.
Main goal
I am trying to learn UDP conexions and this is my attempt. I want to have a client ask for a picture at a server via UDP and the server will send it. Then the client will create a file with that information given.
Explanation
My main idea is to ask the server for an image using a "GET" command (not the HTTP, just GET) followed by the name of the image(extension included). Then the client awaits an answer which is the image requested.
Problems
The client waits and answer which does no come
Research
From another similar question it was a problem that I was using the same PORT for both receive and connect, so I added two ports, receivingPORT and sendingPORT, no results from the Client.
From other similar questions, It was a Firewall problem. So, on a Win10 machine, I created a new rule for UDP in the Firewall for the ports that I am using for this application, and nothing was received by the Client...
I have checked that the image is loaded into byte[] and the image is sent. But on the Client, nothing is received and stays there waiting for a connection to come through
CODE from Server
public class UDPserver {
static DatagramSocket serverUDP;
static DatagramPacket packet;
static InetAddress address;
static byte[] buffer = new byte[65507];//65507
final static int receivingPORT = 6668;
final static int sendingPORT = 6669;
public static void main(String[] args) throws SocketException, IOException, InterruptedException{
boolean serverActive = true;
String order = "";
String file = "";
//Instantiate server
serverUDP = new DatagramSocket(receivingPORT);
while(serverActive){
//Kind of packet we want to receive
packet = new DatagramPacket(buffer, buffer.length);
System.out.println("Server awaiting connection...");
//Receive it
serverUDP.receive(packet);
System.out.println("Received packet from: " + packet.getAddress() + "/" + packet.getPort());
//What does the packet contain?
String msg = new String(packet.getData());
address = packet.getAddress();
System.out.println("Order from: " + address + "/" + receivingPORT + " says: " + msg);
try{
order = msg.split(" ")[0].trim();
file = msg.split(" ")[1].trim();
} catch (Exception e){
}
switch(order){
case("GET"):{
System.out.println("Sending back an image...");
buffer = loadImageFromServer(file);
packet = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
Thread.sleep(5000);
serverUDP.send(packet);
System.out.println("Client served");
break;
}
case("DISCONNECT"):{
buffer = "Server is disconnecting...".getBytes();
packet = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
serverUDP.send(packet);
serverActive = false;
serverUDP.close();
break;
}
}
}
}
static byte[] loadImageFromServer(String path) {
try {
System.out.println("Loading path: " + path);
//Instantiate a buffer from the image for it
BufferedImage img = ImageIO.read(UDPserver.class.getResource(path));
//Create a byte[] stream object to handle the data
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//Write the image data into those above with jpg format
ImageIO.write(img, "png", baos);
//Flush the information
baos.flush();
byte[] buffer = baos.toByteArray(); //Write it out on a byte string and return it
return buffer;
} catch (IOException ex) {
Logger.getLogger(UDPserver.class.getName()).log(Level.SEVERE, null, ex.fillInStackTrace());
System.exit(-1);
}
return null;
}
}
CODE client
public class Client {
static DatagramSocket clientUDP;
static InetAddress address;
static DatagramPacket packetSend;
static DatagramPacket packetReceive;
static int SIZE = 65507;
final static int receivingPORT = 6669;
final static int sendingPORT = 6668;
static byte[] buffer = new byte[SIZE];
static Scanner scan = new Scanner(System.in);
public static void main(String[] args) throws SocketException, UnknownHostException, IOException{
boolean clientLoop = true;
//Get address
address = InetAddress.getByName("localhost");
//Instantiate Client -> UDP
clientUDP = new DatagramSocket();
while(clientLoop){
System.out.print("Enter any key and press enter");
scan.next(); //Just to stop the loop
//Load the buffer
buffer = "GET imagenServidor.png".getBytes();
//buffer = "DISCONNECT".getBytes();
System.out.println("Buffer is ready");
//Arm the packet
packetSend = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
System.out.println("Packet is armed!");
//Send the packet to the server
clientUDP.send(packetSend);
System.out.println("Order sent to server");
System.out.println("Waiting an answer");
packetReceive = new DatagramPacket(buffer, buffer.length, address, receivingPORT);
clientUDP.receive(packetReceive);
System.out.println("Server answered!");
ByteArrayInputStream bais = new ByteArrayInputStream(packetReceive.getData());
BufferedImage image = ImageIO.read(bais);
System.out.println(image);
}
clientUDP.close();
}
}
NOTES
This is a UDP exercise
The Reason
MTU!
You are sending packets with long buffe through UDP directly, which may not work in most network circumstances.
A packet sent through UDP should not be longer than the network MTU, otherwise it would be dropped. The network MTU may not be more than 1500 on most net nods(routers/switchs/hosts...), and even smaller sometimes. Though some nods may do sigmentation for ip packets, but you should not count on it when you are using UDP.
Suggestions
Use TCP instead in this application, as for:
You are sending data which expected to be complete (otherwise it would be useless).
You do not care about congestion control algorithms.
So just go with TCP.
Edit Based on The Update of The Question
So, as this is an excercise, in which you have to use UDP only.
As a file might be useless unless it is complete, you have to make sure:
All packets are possible to pass the path. Which means network should be connected both physically and virtually, and packet size should always be smaller than the MTU.
If any packets are lost, both the receiver and the sender should be able to know.
If any apckets come out of order, the receiver should be able to know.
Sender should be able to cache and resend the packets which are not confirmed by the receiver yet.
Make sure your have a good network connection. Split the image buffer into buffer array with each buffer item length less than 1000bytes(should be safe).
Then let's design an amature but simple protocol for this:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| type | sequence number |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| payload ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
For types, we may need:
hello: 0x01
bye: 0x02
ack: 0x03
nack: 0x04
data: 0x05
feedback: 0x06
...
Sequence should be mono-increasing. e.g. 1, 2, 3, 4.... (Not necessory to start from 1 but OK)
It works like following:
Sender->Receiver: hello(seq=i)
Receiver->Sender: ack(seq=i)
# Sender->Receiver: hello(seq=i)
# if timeout and got no ack for seq=i
Sender->Receiver: data(seq=i+1)
Receiver->Sender: ack(seq=i+1)
# Sender->Receiver: hello(seq=i+1)
# if timeout and got no ack for seq=i+1
Sender->Receiver: data(seq=i+2)
Sender->Receiver: data(seq=i+3)
Receiver->Sender: ack(seq=i+2)
Receiver->Sender: ack(seq=i+3)
# Sender->Receiver: hello(seq=i+2)
# if timeout and got no ack for seq=i+2 or got nack for seq=i+2
Sender->Receiver: bye(seq=n)
Receiver->Sender: ack(seq=n)
# bye is not necessory
Firstly, I think you need to learn how to use wirshark or tcmpdump to analysis network streams when debugging, that will help you find out the problem and solve it.
As for your program, there are several problems the user207421 has mensioned. I think it's better to use TCP, but if you want to learn UDP by this way, the thing you need is to do a slim reliable UDP by yourself.
For example, you may need the following models
Build a send buffer and recive buffer, check every time if the buffer is empty, if not, send/receive and process it.(Cause UDP has MTU)
Add some extra format of information in the head of each datagram, which includes the size of the whole message, the sequence of the datagram, the left size, etc.(Cause you need to cut your message into many parts)
Build a controller, which need to have some function like retransmission, rebuild the message, etc.(Cause UDP is unreliable, you need to check the completeness of all parts)
Hope that can help you.

android studio byte[] for UDP is redudant

I just want to send data to another device (PC) using UDP by clicking on a button. So I made a method for it in which I put the UDP code and made a relation when button is clicked. But according to the IDE, the byte which contains the String data I want to send is not well created.
Here's the code:
String argv = "WhatISend";
int port = 9268;
byte[] buffer= new byte[argv.lenght()];
try {
InetAddress server = InetAddress.getByName("172.16.19.14");
buffer = argv.getBytes();
DatagramSocket socket = new DatagramSocket();
DatagramPacket udp_emission = new DatagramPacket(buffer, buffer.length, server, port);
socket.send(udp_emission);
socket.close();
} catch (Exception e) {
System.out.println("Fail socket");
}
the problem is on "byte[] buffer= new byte[size];"
Based on the wording of your question, it sounds like you want buffer to contain the characters of the original string. However, the byte array actually does not contain the string data. The code you have shown us just creates a byte array that has the same length as the input string. You have not assigned the individual characters over to the buffer.
What you probably want is this:
buffer = argv.getBytes();
For more information, see String.getBytes().

Send file part by part with UDP

I would send a XML file splitted into N parts in my server side.
Each files contains at start this informations : fileNumber and totalPart
For example if I have 32 parts :
- the first file contain at start : 0 (file number) and 32 (total parts)
- the second file contain at start : 1 (file number) and 32 (total parts)...
With a for loop, I can send all the parts in the same time to my client.
But my client can't receive all the parts, I lost some parts..
How I can process for requested the missing parts ?
This is my server side code :
for (int i = 0; i < nbPart + 1; i++) {
File f = null;
BufferedReader br = null;
String content = "";
byte[] sendBuffer = null;
try {
f = new File("xml/file.part" + i);
br = new BufferedReader(new FileReader(f));
StringBuilder sbuilder = new StringBuilder();
String line = br.readLine();
while (line != null) {
sbuilder.append(line);
line = br.readLine();
if (line != null) {
sbuilder.append("\n");
}
}
content = i + ";" + nbPart + "#tag#" + sbuilder.toString();
int total = new Long(f.length()).intValue();
sendBuffer = new byte[total];
sendBuffer = content.getBytes();
DatagramSocket sendSocket = new DatagramSocket();
DatagramPacket sendPacket = new DatagramPacket(sendBuffer, sendBuffer.length, source, PORT_SEND);
sendSocket.send(sendPacket);
sendSocket.close();
Thread.sleep(timeToSend);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (br != null)
br.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
And this is my client side code :
while (run) {
DatagramSocket receiveSocket = null;
DatagramPacket receivePacket = null;
data = "";
byte[] receiveBuffer = new byte[9999];
Arrays.fill(receiveBuffer, (byte) 0);
try {
receiveSocket = new DatagramSocket(PORT_RECEIVE);
receivePacket = new DatagramPacket(receiveBuffer,receiveBuffer.length);
receiveSocket.receive(receivePacket);
receiveSocket.close();
data = new String(receiveBuffer, receivePacket.getOffset(), receivePacket.getLength());
String datas[] = data.split("#tag#");
String dataParts[] = datas[0].split(";");
int numPart = Integer.parseInt(dataParts[0]);
totalPart = Integer.parseInt(dataParts[1]);
if(partReceive.isEmpty()){
for(int i=0;i<totalPart+1;i++){
partReceive.add(Boolean.FALSE);
}
}
File part = new File(filePath+"/file.part"+numPart);
if(part.exists()) part.delete();
writeToFile(part, datas[1]);
partReceive.set(numPart, Boolean.TRUE);
Log.wtf("Part"+numPart, partReceive.get(numPart).toString());
} catch (Exception e) {
e.printStackTrace();
}
}
As you can see, my first idea is : In client side, I create an ArrayList partReceive who contains boolean (False), when I receive a part, I set the index of the ArrayList to "True". But after How I can process ?
Two possibles ways that come to my mind:
Use TCP. It automatically ensures that all packets are received in order.
Maybe you deliberately want you use UDP. I don't quite understand the sense of your ArrayList partReceive. The loop will alawys set all elements false, as totalPart is constant throughout a transfer. In your example it will always be 32. I would rather assign an ID to each packet. An ID is a unique number. You could use numPart for example. When receiving a packet, store its ID in a list. After the entire transmission finished, check what ids are still missing. The easiest approach then would be to request each missing packet.
Something like that
ArrayList<Integer> receivedParts = new ArrayList<Integer>();
// save id of received packet
receivedParts.add(numPart);
// ...
// after transmission has finished
// Keep in mind that packets do not necessarily need to arrive in the same order like you've sent them when using UDP.
ArrayList<Integer> missingIDs = getMissingIDs(receivedParts);
for(Integer currentID : missingIDs) {
// request missing packets
requestMissingPacket(currentID);
}
There are different ways to check whether the tranmission has finished:
After having received all packets, the transmission. Just check the size of your receiveParts list.
Use a timeout. For example if you don't receive any packet during x seonds, consider the tranmission finished. Then check what packets are missing.

Reading a String array [NETWORKING]

I have this block of code to read an array sent from the server to the client of recent updates, the issue is that sometimes it works and sometimes it doesn't. It will print out the version properly, but everything else will either not print out, all print out on the same line, or have 2 on the same line.
The purpose of this is to receive a String[] of recent updates from the server, which is looped through and sent as an individual String. Those updates are then displayed on a GUI.
private Response update() {
try {
Socket socket = new Socket(RS2Client.IP, 55555);
byte[] bytes = new byte[1024];
socket.getInputStream().read(bytes);
String version = new String(bytes);
System.err.println("VERSION READ " + version);
for (int i = 0; i < 6; i++) {
byte[] b = new byte[1024];
socket.getInputStream().read(b);
String text = new String(b);
getRecentUpdates().add(text.trim());
System.out.println("New update: " + text);
}
for (String update : getRecentUpdates()) {
System.err.println(update);
}
System.out.println("Client connected! Version: " + version);
socket.close();
if (Double.parseDouble(version) != RS2Client.CLIENT_VERSION) {
return Response.BAD;
}
} catch (Exception e) {
e.printStackTrace();
return Response.ERROR;
}
return Response.GOOD;
}
A socket sends a stream of bytes. It does not keep track of the end of each byte array you send.
If you want to send a byte array, you should send the length first, so you know how many bytes to expect.

Java UDP socket - Data are left over at the server-side

I am implementing a really basic server-client model in Java, by using UDP sockets and I have come across a really strange issue.
All I want to do is let the user (client) send a message to the server and then the server will print it.
I have an example but I am missing something since I have the following issue:
If the client sends the message "a" to the server it gets received correctly.
If the client sends the message "bbb" to the server it gets received correctly.
If the client sends the message "c" to the server, then the server will print "cbb" as the received message.
It seems as if the server does clean some kind of buffer when it gets a new message.
This is the code I am using:
Server
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPServer {
public static void main(String args[]) throws Exception {
byte[] receive_data = new byte[256];
int recv_port;
DatagramSocket server_socket = new DatagramSocket(5000);
System.out.println("Server - Initialized server. Waiting for client on port 5000");
while (true) {
// System.out.println("Server - Listening for connections...");
DatagramPacket receive_packet = new DatagramPacket(receive_data, receive_data.length);
server_socket.receive(receive_packet);
String data = new String(receive_packet.getData());
InetAddress IPAddress = receive_packet.getAddress();
recv_port = receive_packet.getPort();
if (data.equals("q") || data.equals("Q")) {
System.out.println("Server - Exiting !");
break;
} else {
System.out.println("Server - Client from IP " + IPAddress + " # port " + recv_port + " said : " + data + " (length: " + receive_packet.getLength() + ")");
}
}
}
}
Client
public class UDPClient {
public static void main(String args[]) throws Exception {
byte[] send_data = new byte[256];
BufferedReader infromuser = new BufferedReader(new InputStreamReader(System.in));
DatagramSocket client_socket = new DatagramSocket();
InetAddress IPAddress = InetAddress.getByName("localhost");
System.out.println("Client - Initialized the client...");
while (true) {
System.out.print("Client - Type Something (q or Q to quit): ");
String data = infromuser.readLine();
if (data.equals("q") || data.equals("Q")) {
System.out.println("Client - Exited !");
DatagramPacket send_packet = new DatagramPacket(send_data, send_data.length, IPAddress, 5000);
System.out.println("Client - Sending data : <" + data + ">");
client_socket.send(send_packet);
break;
} else {
send_data = data.getBytes();
DatagramPacket send_packet = new DatagramPacket(send_data, send_data.length, IPAddress, 5000);
System.out.println("Client - Sending data : <" + data + ">");
client_socket.send(send_packet);
}
}
client_socket.close();
}
}
I suppose that the mistake is something trivial, but my skills in network programming are limited, therefore I don't know what exactly it is.
Just to make clear, I am running both the server and the client at the same machine (mac) on different terminals, just in case it affects the situation in anyway.
Any help would be greatly appreciated.
EDIT
...And I come back to answer my own question.
The problem was that I was not defining the amount of data that the server socket should expect to read.
Therefore when I change
String data = new String(receive_packet.getData());
with
String data = new String(receive_packet.getData(), 0, receive_packet.getLength());
everything worked smoothly.
Just for future reference and for people who might come across the same problem :)
When you're constructing the String based on the result, you're currently ignoring the length of the received packet.
After using DataSocket.receive(DatagramPacket), the length of the DatagramPacket should be set to the length that was actually received:
The length field of the datagram packet object contains the length of
the received message. If the message is longer than the packet's
length, the message is truncated.
This should fix the problem on the receiving side:
String data = new String(receive_packet.getData(), 0, receive_packet.getLength());
For this to work you also need to make sure the data sent is of the right size. In particular, don't use send_data.length to construct the outgoing DatagramPacket. This will always use the full length of the buffer). The length parameter isn't meant to be always send_data.length (otherwise the constructor would get it itself from the array), it's meant for the actual useful length of the message within that array.
On your first call this is what receive_data looks like:
--------------
|"a"| | |
--------------
On your second call:
--------------
|"b"|"b"| "b" | notice that the "a" in data_receive was overwritten
--------------
On your third call, you only send a single letter,
so the only part of the array that gets overwritten is the first element:
--------------
|"c"|"b"| "b" |
--------------
This is happening because there is still data left in the receive_data array in between messages to the server, a simple way around this would to just initialize a new array inside of you receive loop. That way every time you receive a message you will have a fresh array waiting for you.
while (true)
{
byte[] receive_data = new byte[256];
......
}
To solve the problem you should use length of receive_packet to create a String or array.
For higher performance in server side, it's better to initialize receive_packet before while section and reset its length at the end of while section to reuse it in loop : receive_packet.setLength(buffer.length);

Categories

Resources