I am writing a socket client in which I am sending data to server (using getOutputStream()),below is my code
this.wr = this.socket.getOutputStream();
wr.write(hexStringToByteArray(messageBody));
wr.flush();
The above is successfull able to send the data.
1) but when I try to read the response using
this.in = new ObjectInputStream(this.socket.getInputStream());
As I dont know what format the server is returning. Getting error at this line
"java.io.StreamCorruptedException: invalid stream header" .
I am not sure why ? I know the values that I will recieve will be in the hex format i.e
say 600185 would be as in 60 01 86 ....
Could any one please help me, to over come this error.
2) Also in case if I dont receive any response after certain duration, how to close the socket connection.
Thanking you all in advance.
ObjectInputStream expects a header in the stream that is written by ObjectOutputStream. So If you use one, you need to use both.
As your sample doesn't really need ObjectOutputStream, you may just want to not use ObjectInputStream.
something like:
public void doWrite(Socket socket, String messageBody) {
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
byte[] data = hexStringToByteArray(messageBody);
dos.writeInt(data.length);
dos.write(data);
dos.flush();
}
public String doRead(Socket socket) throws IOException {
DataInputStream dis = new DataInputStream(socket.getInputStream());
int len = dis.readInt();
byte[] data = new byte[len];
dis.read(data);
return byteArrayToHexString(data);
}
Related
I have a TCP Server and Client in Java. The Server can send commands to the Client, the Client will then execute the command, for example: send an image to the Server.
Im sending the data with a bytearray and thats working.
But lets imagine, I want to send an image and a file separately. How would the Server supposed to know which is the right bytearray? Or if I want to make a VoiceChat (which needs to be sending bytearrays continously) and separately sending an image?
Thats my code send bytes:
Client.java
public void writeBytes(byte[] bytes, Socket socket) throws IOException {
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.write(bytes);
out.flush();
}
Thats my code to receive and convert them to an Image:
Server.java
public BufferedImage writeScreenshot(Socket socket, int length) throws IOException {
DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
byte[] buffer = new byte[length];
in.readFully(buffer);
return ImageIO.read(new ByteArrayInputStream(buffer));
}
You need to design a "protocol" for the communication. A protocol defines what are the messages that can be exchanged and how they are represented in the lower level data stream.
A quick and easy protocol is where you first send the length of the data you are going to send, and then the data:
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.writeInt(bytes.length);
out.write(bytes);
out.flush();
The receiver now has to read the length field:
DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
int length = in.readInt()
byte[] buffer = new byte[length];
in.readFully(buffer);
When you get to applications like voice chat, the protocol has to get more complex. Each message has to have metadata, like what type of data it contains: image or voice or something else. Also you would likely not want to design this protocol from scratch, but use something that already exists - for example the real-time streaming protocol (RTSP).
i have to send a short string as text from client to server and then after that send a binary file.
how would I send both binary file and the string using the same socket connection?
the server is a java desktop application and the client is an Android tablet. i have already set it up to send text messages between the client and server in both directions. i have not yet done the binary file sending part.
one idea is to set up two separate servers running at the same time. I think this is possible if i use two different port numbers and set up the servers on two different threads in the application. and i would have to set up two concurrent clients running on two services in the Android app.
the other idea is to somehow use an if else statement to determine which of the two types of files is being sent, either text of binary, and use the appropriate method to receive the file for the file type being sent.
example code for sending text
PrintWriter out;
BufferedReader in;
out = new PrintWriter(new BufferedWriter
(new OutputStreamWriter(Socket.getOutputStream())) true,);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out.println("test out");
String message = in.readLine();
example code for sending binary file
BufferedOutputStream out;
BufferedInputStream in;
byte[] buffer = new byte[];
int length = 0;
out = new BufferedOutputStream(new FileOutputStream("test.pdf));
in = new BufferedInputStream(new FileOutputStream("replacement.pdf"));
while((length = in.read(buffer)) > 0 ){
out.write(buffer, 0, length);
}
I don't think using two threads would be necessary in your case. Simply use the socket's InputStream and OutputStream in order to send binary data after you have sent your text messages.
Server Code
OutputStream stream = socket.getOutputStream();
PrintWriter out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(stream)
)
);
out.println("test output");
out.flush(); // ensure that the string is not buffered by the BufferedWriter
byte[] data = getBinaryDataSomehow();
stream.write(data);
Client Code
InputStream stream = socket.getInputStream();
String message = readLineFrom(stream);
int dataSize = getSizeOfBinaryDataSomehow();
int totalBytesRead = 0;
byte[] data = new byte[dataSize];
while (totalBytesRead < dataSize) {
int bytesRemaining = dataSize - totalBytesRead;
int bytesRead = stream.read(data, totalBytesRead, bytesRemaining);
if (bytesRead == -1) {
return; // socket has been closed
}
totalBytesRead += bytesRead;
}
In order to determine the correct dataSize on the client side you have to transmit the size of the binary block somehow. You could send it as a String right before out.flush() in the Server Code or make it part of your binary data. In the latter case the first four or eight bytes could hold the actual length of the binary data in bytes.
Hope this helps.
Edit
As #EJP correctly pointed out, using a BufferedReader on the client side will probably result in corrupted or missing binary data because the BufferedReader "steals" some bytes from the binary data to fill its buffer. Instead you should read the string data yourself and either look for a delimiter or have the length of the string data transmitted by some other means.
/* Reads all bytes from the specified stream until it finds a line feed character (\n).
* For simplicity's sake I'm reading one character at a time.
* It might be better to use a PushbackInputStream, read more bytes at
* once, and push the surplus bytes back into the stream...
*/
private static String readLineFrom(InputStream stream) throws IOException {
InputStreamReader reader = new InputStreamReader(stream);
StringBuffer buffer = new StringBuffer();
for (int character = reader.read(); character != -1; character = reader.read()) {
if (character == '\n')
break;
buffer.append((char)character);
}
return buffer.toString();
}
You can read about how HTTP protocol works which essentially sends 'ascii and human readable' headers (so to speak) and after that any content can be added with appropriate encoding like base64 for example. You may create sth similar yourself.
You need to first send the String, then the size of the byte array then the byte array, use String.startsWith() method to check what is being send.
I did a Java Socket server, and a C++ Client.
However, the client connects to the server, without problems.
But when I write something client-server, the server doesn't catch the message.
What I'm doing wrong?
A little bit of the code of the Java Server:
DataInputStream dis=new DataInputStream(usrSocket.getInputStream());
ByteArrayOutputStream out = new ByteArrayOutputStream();
int data;
while((data = dis.read())>=0) {
out.write(data);
}
byte[] bytes = out.toByteArray();
String decrypt = new String(bytes);
if(decrypt.equals("status")){
System.out.println("Status emitted.");
}
System.out.println("Received a message.");
C++ Client writing:
QByteArray qba;
qba.append(text);
sock->write(qba.data());
qDebug() << "Send status";
I need help with that, thank you very much.
(that variable "text" it's a QString)
EDIT
Java server: That's only one part of all the code, the main thread waits for connections (Socket sock = server.accept()) and create a new thread for each user.
The code that I published of the java server, its one part of that threads for the users.
If you need ALL the code, plese tell me.
I will be waiting the answers!
Thank u very much!
Sorry if I answer ya late.
Try this code for Java Server.
ServerSocket ss = new ServerSocket(Port_No);
Socket incomingClient = ss.accept();
InputStream i = incomingClient.getInputStream();
OutputStream o = incomingClient.getOutputStream(); // Use it write to the Client Socket
InputStreamReader isr = new InputStreamReader(i);
BufferedReader br = new BufferedReader(isr);
String str = new String();
while ((str = br.readLine())!=null){
// do what you want with the data received in str.
}
As youre using QTcpSocket, it highly likely that you are running the client in the default asynchronous mode. This means after when you write after calling connectToHost, nothing will be sent as the socket is not connected.
Try using:
socket->connectToHost(hostAddress, hostPort, QIODevice::ReadWrite);
if (socket->waitForConnected()) {
QString text = "test string";
QByteArray array;
array.append(string);
qDebug() << socket->write(array);
} else {
// connect error!
}
Your Java code reads the socket until EOS and then prints something, which by the way is not a decryption operation. Your C++ client writes something and never closes the socket. So the server can never get out of the read loop.
If I read it correctly it is caused by the fact that your client is still running. Read() returns number >= 0 until the client socket is closed.
Hello all my friends,
I am trying to send a long string through socket connection but I have them in two parts so I get an error while doing my processs.
In client I am sending the file,
BufferedWriter bufferedOut = null;
BufferedReader in = null;
socket = new Socket("192.168.0.15",4444);
bufferedOut = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
bufferedOut.write(xmlInString, 0, xmlInString.length());
/**
* wait for response
*/
byte[] buf = new byte[10000];
int actualNumberOfBytesRead = socket.getInputStream().read(buf);
String responseLine = new String(buf, 0, actualNumberOfBytesRead);
In the server,
BufferedReader in = null;
PrintWriter out = null;
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
out = new PrintWriter(client.getOutputStream(), true);
//get the input
byte[] buf = new byte[10000];
int actualNumberOfBytesRead = client.getInputStream().read(buf);
line = new String(buf, 0, actualNumberOfBytesRead);
//send back
out.println(result);
How I can get my string as one part ? Can you please show me where is my mistake on the code ?
Thank you all
You will need a loop to repeatedly read from the input stream, concatenating the read data together each time, until you reach the end of the string.
Edit - a little more detail. If you are looking at transmitting multiple such strings/files, then see #arnaudĀ“s answer. If all your looking to to is send 1 big string then:
On the sender side, create the output stream, send the data (as you have done), and then don't forget to close the stream again (this will also perform a flush which ensure the data gets sent over the wire, and informs the other end that there is no more data to come).
On the recipient site, read the data in a loop until the input stream ends (read(buf) returns -1), concatenating the data together each time in one big buffer, then close the input stream.
Also, please read my comment about sending a file as bytes rather than a string. This is particularly important for XML files, which have rather special rules for encoding detection.
When using a TCP socket, you are handling "streams". That is, there is no delimitation between messages by default. By proceeding as you do, you may read part of a message, or worse, read more than a message.
The most common way to proceed is to delimit your messages. You can use DataInputStream/DataOutputStream which encodes strings into bytes and use the first bytes to indicate it's length. That way, it knows how many bytes it should read on the receiver end.
DataOutputStream out = null;
DataInputStream in = null;
Socket socket = new Socket("192.168.0.15",4444);
out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
out.writeUTF(xmlInString);
out.flush(); // to ensure everything is sent and nothing is kept in the buffer.
// wait for response
String responseLine = in.readUTF();
Then, adjust the server code accordingly.
When using Buffered outputs with sockets, which is advised for performance reasons, it is advised to flush() after you wrote the message to ensure that everything is actually sent over the network and nothing is kept in the buffer.
Your initial problem probably occurred because your message requires several TCP/IP packets and in your server, you read only the first one(s) which just arrived.
My client receive raw HTTP headers (including GET, POST, Multipart POST, etc.) and I want to send them to a server and get output.
But I don't want to parse whole request manually, then set all that parsed stuff to HttpClient...
Does an elegant way to do this (even something like code below)?
AGoodHttpClient response = new AGoodHttpClient(host, port, myHeaders);
InputStream in = response.getInputStream();
// ...
Edited
Let's say I have this code. How do I recognize EOS (-1 isn't working for HTTP/1.1). Is there a guaranteed way how to cut the connection, when transfer is done? I want something what will care about cutting a connection (something like HttpClient), but with direct access to sending headers (like outToServer.write(myHeaders)).
Socket connectionToServer = new Socket(host, port);
OutputStream outToServer = connectionToServer.getOutputStream();
outToServer.write(myHeaders.getBytes());
InputStream inputFromServer = connectionToServer.getInputStream();
byte[] buff = new byte[1024];
int count;
while ((count = inputFromServer.read(buff)) != -1) {
System.out.write(buff, 0, count);
}
Thanks for help!