I'm writting a simple server for experimenting with server socket and telnet.exe
public static void main(String[] args) throws IOException {
String line;
ServerSocket ss = new ServerSocket(5555);
Socket socket = ss.accept();
System.out.println("Waiting for a client...");
InputStream sin = socket.getInputStream();
OutputStream sout = socket.getOutputStream();
DataInputStream in = new DataInputStream(sin);
DataOutputStream out = new DataOutputStream(sout);
out.writeUTF("\u001B[2J");
out.writeUTF("Hello client\r\n");
line = in.readUTF();
System.out.println("The dumb client just sent me this line : " + line);
System.out.println("I'm sending it back...");
out.writeUTF(line);
out.flush();
System.out.println("Waiting for the next line...");
System.out.println();
}
Now I'm running this server and connecting to him via telnet.exe. It's ok. But when i'm sending message to server I dont receive this back:
Why it doesnt work?
A telnet client terminates each input-line with a newline. But a DataInputStream does not recognize this as a terminator for input-strings, because a DataInputStream is for binary data.
Wrap your input stream with an InputStreamReader to handle it as a character-based input stream.
Then wrap this one in a BufferedReader. This has the advantage that the input-stream can be filled by the socket in the background while your program executes. It also provides some handy utility methods like the following.
Use the readLine method, which reads data until a newline is found.
Like this:
BufferedReader inputReader = new BufferedReader(new InputStreamReader(in));
[...]
line = inputReader.readLine();
For text-based output of newline-terminated messages back to the client, you should use the output analogue of the BufferedReader, which is the PrintWriter.
DataInput is for reading binary.
From the Javadoc for readUTF()
Reads in a string that has been encoded using a modified UTF-8 format. The general contract of readUTF is that it reads a representation of a Unicode character string encoded in modified UTF-8 format; this string of characters is then returned as a String.
First, two bytes are read and used to construct an unsigned 16-bit integer in exactly the manner of the readUnsignedShort method . This integer value is called the UTF length and specifies the number of additional bytes to be read. These bytes are then converted to characters by considering them in groups. The length of each group is computed from the value of the first byte of the group. The byte following a group, if any, is the first byte of the next group.
This means the first two byte have to have the length in binary of the following UTF string.
You are typing something like k and j for the first two bytes so the length is something like 25000 bytes, i.e. you haven't typed that much which is why it doesn't return.
What you want instead is to be able to read/write text using classes like BufferedReader and PrintWriter.
Related
I'm getting an unknown character when reading from a socket using DataInputStream. When I send "Hello" on the server side the output is "Hello" the unknown character is the issue. I'm new to socket programming so I don't know what the issue could be. I tried using a BufferReader and PrintWriter too bu the server when using readLine() does not print the text sent but rather java.io.BufferedReader.
Server Side:
//BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
DataInputStream is= new DataInputStream(socket.getInputStream());
String userinput= is.readLine();
System.out.println("Client message: "+userinput);
Client Side:
//BufferedReader std= new BufferedReader(new InputStreamReader(System.in));
String userInput;
DataOutputStream out;
while((userInput=std.readLine()) !=null){
Socket socketClient= new Socket("localhost",5000);
OutputStream os= socketClient.getOutputStream();
out=new DataOutputStream(os);
out.writeUTF(userInput);
out.flush();
socketClient.close();
}
You're using writeUTF on the client side, are you sure readLine on the server side is compatible with it? I had never seen that before, but the javadoc mentions it's a "modified" UTF-8 encoding. Try using readUTF instead, or simply use the regular write method.
If you read a UTF line then you can write UTF or ASCII characters but as opposed to if you read ASCII characters then you must write those characters as ASCII.
try to use out.write(userInput); instead of out.writeUTF(userInput);
I need to receive a unicode (UTF-8) string sent by client on a server side. The length of the string is of course unknown.
ServerSocket serverSocket = new ServerSocket(567);
Socket clientSocket = serverSocket.accept();
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
I can read bytes using in.read() (until it returns -1) but the problem is that the string is unicode, in other words, every character is represented by two bytes. So converting the result of read() which would work with normal ascii characters makes no sense.
update
As per suggestions bello, I created the reader as follows:
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(),"UTF-8"));
I've changed the client side to send a newline (#10#13) after each string.
But the new problem is I get bullshit instead of real string if i call:
in.readLine();
And print the result I get some nonsense string (I cannot even copy it here) although I am not dealing with non-latin chars or anything else.
To see what's going on I introduced following code:
int j = 0
while (j < 255){
j++;
System.out.print(in.read()+", ");
}
So here I just print all bytes received. If I send "ab" I get:
97, 0, 98, 0, 10, 13,
This is what one would expect, but than why the readLine method doesn't produce "good" results?
Anyway, if we couldn't find the actual answer, I should probably collect the bytes (like above) and create my string from them? How to do that?
P.S. Just a quick note - I am on windows.
Use new InputStreamReader(clientSocket.getInputStream(), "UTF-8") in order to set properly the name of the charset to use while reading the InputStream coming from your client
When creating InputStreamReader you can set encoding like this:
BufferedReader in =
new BufferedReader(
new InputStreamReader(clientSocket.getInputStream(), "UTF-8")
);
Try this way:
Reader in = new BufferedReader(
new InputStreamReader(
clientSocket.getInputStream(), StandardCharsets.UTF_8));
Note the StandardCharsets class. It is supported since Java 1.7 and provides more elegant way to specify a standard encoding like UTF-8.
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.
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 assignment includes sending an image file using UDP service (using java I implemented that successfully). My professor asked to include:
"The exchanged data messages must also have a header part for the sender to include 16-bit message sequence number for duplicate filtering at the receiver end"
How to do this?
I assume to create your UDP packet, you are using a ByteArrayOutputStream to generate the data. If that is the case, just Wrap a DataOutputStream on top of that ByteArrayOutputStream, and call writeInt(somesequenceNumber) before writing the image data to the stream.
on the receive side, do the opposite, wrap a DataInputStream around a ByteArrayInputStream, and call readInt() to get the sequence number. From there you can check whether you have already received this packet.
Something like
Write Side
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(sequenceNumber++);
dos.writeInt(imageDataLength);
dos.write(imageData);
dos.flush();
byte[] udpPacketBytes = baos.toByteArray();
Read Side
ByteArrayInputStream bais = new ByteArrayInputStream(udpPacketBytes);
DataInputStream dis = new DataInputStream(bais);
int sequenceNumber = dis.readInt();
if (seenSequenceNumbers.add(Integer.valueOf(sequenceNumber)))
{
int imageLength = dis.readInt();
byte[] imageData = new byte[imageLength];
dis.read(imageData);
}
where seenSequenceNumbers is some Set
For a 16-bit value I would use DataOutputStream.writeShort() and DataInputSTream readShort()/readUnsignedShort(). writeInt() and readInt() are for 32-bit values. If you want to avoid duplicates, a 32-bit value may be a better choice in any case. ;)