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.
Related
Excuse me my understanding of streams is slowly building.
I initally tried to stream a music file over to my client but that didn't work out so I moved transferring the entire file via bytes then saving it at the client side. The problem is the input stream is still receiving bytes and so won't break out of the while loop (Where I want to play the song)
Below is part of my client code:
//This is part of a task that carries this out (Part of a UI application)
#Override
protected Void call()
{
try(Socket socket = new Socket(host,portNumber);
ObjectOutputStream toServer = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream fromServer = new ObjectInputStream(socket.getInputStream()))
{
//Client requests a song from the server (song is a String)
toServer.writeUTF(".Music."+song);
toServer.flush();
//Create a new file (tempSong is a string)
File test = new File(tempSong);
test.createNewFile();
//New file writer
BufferedOutputStream bOS = new BufferedOutputStream(new FileOutputStream(test));
byte[] buffer = new byte[4096];
int current;
/**
* Read the bytes from the server and write the file
* The file is written and I can play it (externally)
* but the while loop doesn't break after writting the file
*/
while ((current = fromServer.read(buffer)) > 0)
{
bOS.write(buffer, 0 , current);
}
System.out.println("Finished writing");
bOS.close();
/**
* down here a method is ran to play the file
* but it never happen because the task is still in the while loop
*/
}
catch (IOException e)
{
e.printStackTrace();
}
return null;
}
below is in the server where the server reads the message and sends the file
/** This is part of a task and the main call area */
#Override
public Void call ()
{
try
{
//Setup I/O
toClient = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream fromClient = new ObjectInputStream(socket.getInputStream());
while(!socket.isClosed())
{
//If server has received a message
if(fromClient.available() > 0)
{
//Reads message and objects from client
String input = fromClient.readUTF();
if (input.contains(".Music"))
{
findMusic(input, toClient);
}
/**
* more else IFs
*/
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
/**
* this method is part of the task discussed above
*/
//The method that is called
private void findMusic(String input, ObjectOutputStream toClient)
{
logoff();
String[] names = input.split("[.]");
clientManagerTemp.logger("Someone request song: " + names[2] + ".mp3");
File musicFile = AudioUtil.getSoundFile("src/Resources/Songs/" + names[2]+ ".mp3");
byte[] buffer = new byte[(int) musicFile.length()];
try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(musicFile)))
{
bis.read(buffer, 0, buffer.length);
clientManagerTemp.logger("Sending " + "src/Resources/Songs/" + names[2]+ ".mp3" + "(" + buffer.length + " bytes)");
//write the file to the client
toClient.write(buffer,0, buffer.length);
toClient.flush();
clientManagerTemp.logger("Finished sending");
}
catch (IOException e)
{
e.printStackTrace();
}
}
So as you can see the server sends the file fine and my client receives it. It just won't stop the while loop. Can someone explain why? So I can get a better understanding of how streaming bytes over sockets work
**EDIT
When the client receives the file it can be played even after closing the client and server
You can send the file-length to the client, with this information the client knows when to exit the loop.
Server
private void findMusic(String input, ObjectOutputStream toClient)
{
logoff();
String[] names = input.split("[.]");
clientManagerTemp.logger("Someone request song: " + names[2] + ".mp3");
File musicFile = AudioUtil.getSoundFile("src/Resources/Songs/" + names[2]+ ".mp3");
int fileLength = (int) musicFile.length();
byte[] buffer = new byte[fileLength];
try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(musicFile)))
{
bis.read(buffer, 0, buffer.length);
clientManagerTemp.logger("Sending " + "src/Resources/Songs/" + names[2]+ ".mp3" + "(" + buffer.length + " bytes)");
//write the file to the client
toClient.writeInt(fileLength);
toClient.write(buffer,0, buffer.length);
toClient.flush();
clientManagerTemp.logger("Finished sending");
}
catch (IOException e)
{
e.printStackTrace();
}
}
Client
byte[] buffer = new byte[4096];
int current;
int fileLength = fromServer.readInt();
while ( fileLength > 0 && (current = fromServer.read(buffer, 0, Math.min(4096,fileLength))) > 0)
{
bOS.write(buffer, 0 , current);
fileLength -= current;
}
This is due to the nature of the read method, seen here Javadocs. This method will block until there is data, so your loop will never end. Now the reason for this is because you never close the stream on the server end, you just flush it, which forces a send of all the data currently in the buffer, but will not close the stream. If you call .close() on the stream from the server side then that should exit the while loop on the client side and continue to your playing code.
I haven't tested this, but from the documentation and a brief look at your code, that seems to be the issue.
If you don't close the connection (server side), there will not be an end-of-file/end-of-stream and your client side will run the loop forever, unless you configured a read timeout.
If you won't to close the loop automatically after sending the file is finished, send the count of bytes (size of file) first to client and then the file itself. This makes it possible to read only a exact number of bytes on client side and close the connection when finished.
Alternative you can send a special sequence and check for them at client side to mark the end of stream.
The reason you program is stuck is due to the behaviour of ObjectInputStream.read(). This method is blocking until it has read some data or the stream is closed. Since you never close the stream the 2nd condition will never be met. So there is only option 1. To return when data arrived. Unfortuenatly there is no way to recognize the end of a file unless you define it yourself e.g. by sending a unique sequenz or something that you can recognize to break the while loop.
Example:
while ((current = fromServer.read(buffer)) > 0)
{
// example value 42 could be anything else as well
if(current == -42)
{
break;
}
bOS.write(buffer, 0 , current);
}
The downside of this is, that your data could also contain this special value so you gotta think about different possibilities e.g memorize the sequence up to 3 values and compare them to an 'interrupt sequence'.
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());
...
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.
i have just started playing around with java nio packages an am at a bit of a loss
i have a client
Socket s=new Socket(ip,port);
OutputStream out=new OutputStream();
PrintStream ps=new PrintStream(s.getOutputStream());
String t=""hiya";
ps.print(t);
ps.flush();
InputSTreamReader in=new InputSTreamReader(s.getInputSTream);
BufferReader b=nwe BufferedReader(in);
System.out.println(b.readLine());//prints echo response from server
and on the server side
this.selkey = selkey;
this.chan = (SocketChannel) chan.configureBlocking(false); // asynchronous/non-blocking
buf = ByteBuffer.allocateDirect(64); // 64 byte capacity
void read() {
try {
int amount_read = -1;
try {
amount_read = chan.read((ByteBuffer) buf.clear());
} catch (Throwable t) {
}
if (amount_read == -1)
disconnect();//deelts client on exit
if (amount_read < 1)
return; // if zero
System.out.println("sending back " + buf.position() + " bytes");
// turn this bus right around and send it back!
buf.flip();
chan.write(buf);//sending data 2 client
} catch (Throwable t) {
disconnect();
t.printStackTrace();
}
}
what this does i send string t to the server into bytebuffer and echos it back which all works fine but hoiw would i print the string on the server side for example in read method
buf.flip()
System.out.write(buff);//which just prints what looks to be chinese
k this is typical have been working on this problem for like an hour but when i post the question the answer acours to me
CAST IT TO A CHAR!
this works
buf.flip();
while (buf.hasRemaining()) {
System.out.print((char) buf.get();
}
System.out.println();
Since you are sending data as bytes, it wont work as Strings.
Even if you did, it would be referencing the byte array.
Try this:
String str = new String (buff);
I'm trying to send images from my computer to my phone by using a Java program connect to an Android app.
The code I use to send the image:
byte[] data = imageToByteArray(img);
sendMessage(Main.imageCheck + data.length);
out.write(data);
out.flush();
This code is run from the Java program on my computer. The sendMessage() method is:
public void sendMessage(String s) {
if (out != null && server.isConnected()) {
try {
out.writeUTF(s);
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
The code I use to receive the image and other data:
while ((input = in.readUTF()) != null) {
if (!input.equalsIgnoreCase(pulseCheckMessage)) {
if (input.contains(imageCheck)) {
//byte[] data = Base64.decode(input.replace(imageCheck, ""), Base64.DEFAULT);
byte[] data = new byte[Integer.parseInt(input.replace(imageCheck, ""))];
int length = in.read(data);
updateScreenImage = BitmapFactory.decodeByteArray(data, 0, length);
MainActivity.this.runOnUiThread(updateUiRunnable);
} else {
inLine = "Received: " + input;
MainActivity.this.runOnUiThread(updateUiRunnable);
}
}
pulseTakenTime = System.currentTimeMillis();
pulseCheckReceived = true;
isAlive = true;
}
The commented out code (including the base64 class) was when I was sending the image data as a string by using Base64.encode to convert the byte[] array to a string to send over, which worked without a problem except that it was too slow. So I decided to send the bytes over which I thought would be faster.
imageCheck and Main.imageCheck are strings that are the same. The app receives that string and recognizes it and expects a byte[] array so it tries to do in.read(data) however I get an error:
ERROR: java.nio.charset.ModifiedUtf8.decode(ModifiedUtf8.java:57)
ERROR: java.io.DataInputStream.decodeUTF(DataInputStream.java:444)
ERROR: java.io.DataInputStream.decodeUTF(DataInputStream.java:438)
ERROR: java.io.DataInputStream.readUTF(DataInputStream.java:433)
ERROR: com.mangopearapples.pccontroller.MainActivity.run(MainActivity.java:238)
Thanks.
My guess is that int length = in.read(data); doesn't read all the bytes that have been sent, as documented:
Reads some number of bytes from the contained input stream and stores them into the buffer array b. The number of bytes actually read is returned as an integer
(emphasis mine)
So, the next iteration tries to read the unread bytes of the image as UTF, and it fails. Always use a loop to read bytes from a stream, which stops once all the expected number of bytes have been read.
To confirm that this is the real problem, debug and see if the length returned matches with the actual length.