I'm sending a string over the socket I previously sent a file to, but the recipient reads it as part of the file itself, is there a way to send a sort of EOF before sending the string?
To send the file I'm using
byte[] buffer = new byte[1024];
int count;
while ((count = fis.read(buffer)) >= 0) os.write(buffer, 0, count);
os.flush();
(and almost the same to receive it)
To send the string I'm using OutputStreamWriter
(Here you are my code: hatebin)
I've also read here that I should send a SOH character, but which one should I send and how?
Thanks in advance.
No there's no way to send an "eof" and then send something afterwards.
If you don't want to open a new connection, there are basically two ways to solve this.
You can modify the client so it recognizes some special byte sequence as a "delimiter", and stops writing to the file when it reads the delimiter from the socket. In this case you need to have some strategy to deal with the possibility that the file actually contains the delimiter.
You can send the size of the file in bytes before sending the file, and modify the client so it counts the number of bytes it reads from the socket. When the client has read enough, it should stop writing to the file.
Related
InputStream in = socket.getInputStream();
byte[] content = new byte[2048];
int received = in.read(content, 0, content.length);
System.out.println(received);
Using this code, I would like to know how I retrieve only the number of bytes that the server sends me.
I was told to use a loop using a Buffer, but as I am new to this area, I didn't quite understand what it means, could someone give me a hand?
You are storing the number of bytes that the server sends you in the received variable. If you want to convert the data that the server sent into a string for debugging purposes, this is how you can do it:
int received = in.read(content, 0, content.length);
String messageFromServer = new String(content, 0, received);
Note that in general you need to call read multiple times in order to receive all the data from the server, just like with any InputStream. You can find tutorials on using Input and OutputStreams here:
https://docs.oracle.com/javase/tutorial/essential/io/bytestreams.html - specific example for reading and writing files, but sockets are no different.
http://tutorials.jenkov.com/java-io/inputstream.html - more general and thorough tutorial
I have created a basic client-server program that transfers file from server to the client. Since the file is about 500MB, the server transfers the file as chunks of bytes to the client over the DataOutputStream object. While this logic works okay when both client and server are running on the same computer, it doesn't work when the two programs are running on separate computers(Both computers are on the same network and I have disabled the firewall for both)
When running on separate computers the problem is that a few bytes get transferred
Server Logic:
byte byteArr[] = new byte[1024];
while((c=fileInputStream.read(byteArr, 0, 1024) != -1))
{
dataOutputStream.writeBoolean(true);
dataOutputStream.flush();
dataOutputStream.write(byteArr, 0, 1024);
dataOutputStream.flush();
}
/*When running on different computers, after a few hundred iterations
it just stops looping the following lines are never executed*/
dataOutputStream.writeBoolean(false);
System.out.println("Transfer complete");
Client Logic
byte byteArr[] = new byte[1024];
while(dataInputStream.readBoolean())
{
dataInputStream.read(byteArr, 0, 1024);
fileOutputStream.write(byteArr, 0, 1024);
}
A read(buf, 0, 1024) call is not guaranteed to read exactly 1024 bytes. This causes bugs in both pieces of code:
the server incorrectly assumes that each chunk read from the file is always exactly 1024 bytes long and sends the whole buffer to the client.
the client might not read the whole chunk in a single iteration. It will then treat the first byte of the remainder as a boolean and get out of sync with the server.
To resolve this, you could:
send the file size (if known) before sending the file,
then just keep reading until you've read that many bytes.
or send c (the chunk size) instead of a single boolean,
then use dataInputStream.readFully() to make sure that
many bytes will be read.
I have a Socket connection to an application that I hosted elsewhere. Once I connected I made a OutputStream and DataInputStream.
Once the connection has been made, I use the OutputStream to send out a handshake packet to the application. Once this handshake has been approved, it returns a packet through the DataInputStream (1).
This packet is processed and is returned to the application with the OutputStream.
If this returned data is valid, I get another packet from the DataInputStream (2). However, I have not been able to read this packet through the DataInputStream.
I have tried to use DataInputStream.markSupported() and DataInputStream.mark() but this gave me nothing (except for an empty Exception message).
Is it possible to read the input stream for a second time? And if so, can someone please point me out what I'm doing wrong here?
EDIT: Here is my solution:
// First define the Output and Input streams.
OutputStream output = socket.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
// Send the first packet to the application.
output.write("test"); // (not actual data that I sent)
// Make an empty byte array and fill it with the first response from the application.
byte[] incoming = new byte[200];
bis.read(incoming); //First packet receive
//Send a second packet to the application.
output.write("test2"); // (not actual data that I sent)
// Mark the Input stream to the length of the first response and reset the stream.
bis.mark(incoming.length);
bis.reset();
// Create a second empty byte array and fill it with the second response from the application.
byte[] incoming2 = new byte[200];
bis.read(incoming2);
I'm not sure if this is the most correct way to do this, but this way it worked for me.
I would use ByteArrayInput stream or something that you can reset. That would involve reading the data into another type of input stream and then creating one.
InputStream has a markSupported() method that you could check on the original and the byte array one to find one that the mark will work with:
https://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#markSupported()
https://docs.oracle.com/javase/7/docs/api/java/io/ByteArrayInputStream.html
The problem here is not re-reading the input. I don't see anything in the question that requires you to read the input twice. The problem is the BufferedInputStream, which will read everything that is available to be read, including the second message, if it has already arrived.
The solution is not to use a buffered stream until you have completed the handshake. Just issue a read on the socket input stream for exactly the length of the first message, do the handshake, and then proceed to construct and read the buffered stream.
My Java server sends an Integer and a String (to a C client):
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeInt(ClientNumber); //send the Integer
String randomString= getRandomValue(10,20);
dos.writeUTF(randomString); //send the String
String clientString=din.readLine();
The C code for the client that's reading them is:
if( recv( to_server_socket, &reply, sizeof( reply ), MSG_WAITALL ) != sizeof( reply ) )
{
printf( "socket read failed");
exit( -1 );
}
char buf[50];
int byte_count;
byte_count = recv(to_server_socket, buf, sizeof buf, 0);
printf("recv()'d %d bytes of data in buf\n", byte_count)
Until here, it works fine.
Now, I want to send another String to the Client. So, I tried just adding the line:
dos.writeUTF("blabla");
It's still working and when I tried to get the client to read it, I added:
byte_count2 = recv(to_server_socket, buf2, sizeof buf2, 0);
printf("recv()'d %d bytes of data in buf\n", byte_count2);
And it doesn't work. The client receives the number and the first String but it doesn't send anything and doesn't receive the "blabla" string. I'm not sure if the problem is in the client or the server.
Can anyone explain to me what I'm doing wrong?
Try closing your dos(DataOutpuStream) after every write. You may try to check first if flush helps.
You are mixing your protocols. I suggest you use either binary or text wire format. It's not clear which one you are trying to use.
I suggest text wire format as it is easier to work with in this case. i.e. don't DataInputStream or DataOutputStream as these are for binary formats.
Instead you can use BufferedReader for reading lines of text and PrintWriter for writing lines of text. You can test your server works by connecting to it with telnet i.e. if it doesn't work with telnet, it won't work with C.
Once this is working, get your C client to work as well. BTW You shouldn't assume that one write translates to one read. You are writing a Stream of data, not messages.
I'm writing a protocol on top of sockets, so I've decided to implement headers then send the information. So there is one thread per connection on the server which sits there reading in headers, then delegates off to methods to read in the rest of the information when it arrives.
So essentially it looks like this:
while ((length = inStream.read(buffer)) != -1)
{
dispatch(buffer, length);
}
So the dispatch method then decrypts the headers and delegates the method depending what is found in the header. It looks similar to:
byte[] clearText = decrypt(message,length);
if (cleartext == foo) sendFooToSocket();
So then sendFooToSocket() would then sit there and read from the instream or send to the outstream.
This is where I seem to run into some problems, in the client I'm sending the header then flushing, then sending the rest of the data, but it appears it's all coming as one and not being split up into header then data. Also is there a best way to force out of the sendFooToSocket method?
public void sendFooToSocket()
{
byte[] buffer = new byte[1024];
int length = 0;
while ((length = inStream.read(buffer) >0)
{
message = decrypt(buffer, length);
}
}
I would assume flush would allow me to break out of this method as it closes then opens the stream?
So I have 2 problems, flush doesn't seem to be breaking up my messages and flush doesn't seem to be allowing to drop out of methods such as sendFooToSocket(), any suggestions?
For clarity sake, the client just does this:
byte[] header = "MESG".getBytes();
cipher = encrypt(header);
outStream.write(cipher,0,cipher.length);
outStream.flush();
byte[] message = "Hi server".getBytes();
cipher = encrypt(message);
outStream.write(cipher,0,cipher.length);
outStream.flush();
But this is received by the server as 1 message even though it's been flushed after every write. Sending just the header works, and we get stuck in the sendFooToSocket() method, but if I send the data after the flush it comes all at once.
The client uses OutputStream and InputStreams just from the socket.get. The client also uses OutputStream and InputStream. Not sure if this matters?
What you seem to want is "record boundaries". With streams in general there are no implicit record boundaries. If you want that kind of functionality you will need to implement it yourself, by buffering the input and looking for, say, newlines to indicate the end of a record.
Look at BufferedInputStream.
inStream.read() may not be returning on a message boundary. You can't assume that it'll return at any particular boundary (such as a blank line separating headers and content if that's how you're doing it.) You'll have to manually parse the content and ignore the fact that it could come from multiple read()s or maybe one read() contains both the headers and content.
Unless you actually want control at the level you have implemented, you could consider Object streams (see ObjectInputStream and ObjectOutputStream). Such streams will allow you to send Java Objects over sockets and read them at the other end with out having to deal with headers and boundaries etc. See ObjectOutputStream for more details, but it's pretty much:
Sender:
writeObject(objectX)
Receiver:
myCopyOfObjectx = readObject()
and you can send any objects you like (as long as they are Serializable).