Read multiple lines using BufferedReader (Socket) - java

I already read some threads here on stackoverflow, also some tutorials, but I don't find a solution to my problem.
I have Java client which connects to a server, then sends exactly one line to the server, and I get 2 or 3 lines as a response.
Here is my code:
public static void main(String[] args) {
String message;
String response;
try {
BufferedReader inFromUser = new BufferedReader( new InputStreamReader(System.in));
Socket clientSocket = new Socket(hostname, port);
DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
message = inFromUser.readLine();
outToServer.writeBytes(message + '\n');
// here my program "freezes"
while ((response = inFromServer.readLine()) != null) {
System.out.println("response: " + response);
}
clientSocket.close();
} catch (UnknownHostException e) {
System.out.println("Unknown Host");
} catch (IOException e) {
System.out.println("IO Exception");
}
}
My problem is, I can read every line of the response, but my program won't exit. The line clientSocket.close(); gets never called. What am I doing wrong?

Presumably your server isn't closing the connection - therefore the underlying stream for the reader isn't closed... at any point the server could send more information. readLine() only returns null when the stream has been closed, i.e. there will definitely not be any more data.
Now we don't know anything about the protocol here, but if the expected behaviour is that the client won't send any more information, and the server will close the connection, then the bug is in the server. If the protocol states that the server will keep the connection open, then the bug is in your client code and you need to work out how to detect the end of data (or send some sort of ack that will cause the server to close the connection, or whatever).

Related

How to process and decode incoming bytes from a TCP server

I am currently working on a Java console application. It is run through the command prompt, connects to a server application coded in python and communicates with that server over TCP. My application sends "ISND" string to server which it accepts and in return server sends three images. The format in which Images are sent in is
Where "<"">" are not actually included. "ISND" is encoded into bytes using ascii. Size is the size of the image converted into bytes from int and it is always composed of 3 bytes regardless of the size of the image. For each individual image, a message in this format is sent.
I have been using BufferedReader to read server responses but at this point, I am at a loss on how to actually handle this message. I searched for ways to separate the incoming message into components since I know the length of the first two parts which are always fixed yet I couldn't find a way to actually accomplish that goal.
It has come to the point it feels like I am smashing my head into a wall. As such, I need advice from anyone that is more familiar with Java and Socket programming on how to handle this issue.
My current code
public class ImageLabeler {
/**
* #param args
*/
public static void main(String[] args) {
String IP = args[0];
System.out.println(IP + "\n");
String port = args[1];
System.out.println(port + "\n");
Socket clientSocket;
DataOutputStream outToServer = null;
BufferedReader inFromServer = null;
String serverResponse;
try {
clientSocket = new Socket(IP, Integer.parseInt(port));
outToServer = new DataOutputStream(clientSocket.getOutputStream());
inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
System.out.println("Connection success\n");
} catch (IOException ex) {
System.out.println("Connection failed\n");
System.exit(0);
}
PrintWriter writer = new PrintWriter(outToServer, true);
try {
//outToServer.writeBytes("USER bilkentstu\\n");
//outToServer.flush();
//System.out.println("check\n");
writer.println("USER bilkentstu");
serverResponse = inFromServer.readLine();
System.out.println(serverResponse + "\n");
writer.println("PASS cs421f2019");
//outToServer.writeBytes("PASS cs421f2019\\r\\n");
//outToServer.flush();
serverResponse = inFromServer.readLine();
System.out.println(serverResponse + "\n");
writer.println("IGET");
//This is where I need to handle the incoming Image messages.
writer.println("EXIT");
} catch (IOException ex) {
Logger.getLogger(ImageLabeler.class.getName()).log(Level.SEVERE, null, ex);
}
System.exit(0);
}
}
Don't use buffered reader. You need to write the code reading a string from the InputStream of the socket one byte at a time.

Make Java IO Socket listen just like a ServerSocket

I'm doing a financial messaging integration as follows:
A server has an interface listening for requests from a client socket on a specific IP and port
Server sends a response for every request, back to the client socket
Also, server sends requests to the same client socket
The following is working perfectly:
The client socket (Socket object of Java IO) successfully sends requests to the server interface
The client socket successfully receives response for every request
try {
Socket clientSocket = new Socket("example.com", 8888);
BufferedWriter output = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
output.write(data);
output.flush();
BufferedReader input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
// Read responses or new request from input
} catch (IOException e) {
e.printStackTrace();
}
The client socket is supposed to receive any request from the server - the same way it's receiving responses from the same sever. However, when a server initiates a request to the client socket, the request is never received. However, we can trace the traffic from the tcpdump on the client environment.
How can I make the client socket listen to requests from the server, and not just responses?
Update
It might help to clarify something on this integration:
a. The 'server' in this case is a third party system, with it's integration rules
b. My client socket posts message to the server (above)
c. My implementation listens to responses and requests from the third party system server, either by creating my own server socket (ruled out), or using the very client socket I'm sending with (the solution I was seeking)
This is a very common mistake, you are writing a message without writing "\n" (end line identifier) at the end so no messages will be read. To fix this use PrintWriter with println.
That will send a line to the other socket.
Here is an example of a thread-per-client model of a server
//create a new server socket with the port as a parameter, this will bind it to the specified port: 6000
ServerSocket server = new ServerSocket(6000);
System.out.println("Binded");
//create a while loop accepting sockets
while(true)
{
//accept a socket
Socket client = server.accept();
System.out.println("Client has connected");
//create a new thread for this socket
new Thread(() ->
{
try
{
/*
create a print writer so you can write a line, not only a message like BufferedWriter does,
if for some reason you still want to use BufferedWriter use
writer.write(message + "\n");
*/
PrintWriter writer = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
//Create a new reader
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
System.out.println("Waiting for requests...");
//create a while loop reading requests (lines)
String request;
while((request = reader.readLine()) != null)
{
System.out.println("Received message: " + request);
//here find the correct response and return it, I just sent a message, replace it with the correct response
writer.println("Hello there! How are you today?");
//flush, flushing will write the data to the client
writer.flush();
}
} catch(IOException e)
{
//print an exception if it occurred, if an exception occurrs its most likely just a disconnection exception
e.printStackTrace();
}
}).start();
}
and here is an example of a client
//connect to the server at "localhost" on port 6000
Socket client = new Socket("localhost", 6000);
System.out.println("Connected");
/*
create a print writer so you can write a line, not only a message like BufferedWriter does,
if for some reason you still want to use BufferedWriter use
writer.write(message + "\n");
*/
PrintWriter writer = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
//Create a new reader
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
//write a request
writer.println("Hi there!");
//flush, flushing will write the data to the server
writer.flush();
System.out.println("Written");
System.out.println("Waiting for responses...");
//create a while loop reading responses (lines)
//you may want to do this while loop in another thread
String response;
while((response = reader.readLine()) != null)
{
System.out.println("Received response: " + response);
}
Also if this is involved with financial information I recommend using TLS (SSL).
You don't have to worry Java already has it implemented and has made it easy to use, here is an example of a server
//create a new SSL server socket with the port as a parameter, this will bind it to the specified port: 6000
//you create it by getting the default SSLServerSocketFactory which will create a new SSLServerSocket
//you need to cast it since it actually returns ServerSocket but SSLServerSocket extends ServerSocket and this returns SSLServerSocket so it is safe
SSLServerSocket server = (SSLServerSocket) SSLServerSocketFactory.getDefault().createServerSocket(6000);
System.out.println("Binded");
//set the enabled ciphersuites, until you buy a certificate set only to ciphersuites with "anon" more info on ciphersuites on https://en.wikipedia.org/wiki/Cipher_suite
server.setEnabledCipherSuites(new String[]{"TLS_ECDH_anon_WITH_AES_256_CBC_SHA"});
//create a while loop accepting sockets
while(true)
{
//accept a socket a SSLSocket
SSLSocket client = (SSLSocket) server.accept();
System.out.println("Client has connected");
//create a new thread for this socket
new Thread(() ->
{
try
{
//begin a handshake more info about handshakes in https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.1.0/com.ibm.mq.doc/sy10660_.htm
client.startHandshake();
/*
create a print writer so you can write a line, not only a message like BufferedWriter does,
if for some reason you still want to use BufferedWriter use
writer.write(message + "\n");
*/
PrintWriter writer = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
//Create a new reader
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
System.out.println("Waiting for requests...");
//create a while loop reading requests (lines)
String request;
while((request = reader.readLine()) != null)
{
System.out.println("Received message: " + request);
//here find the correct response and return it, I just sent a message, replace it with the correct response
writer.println("Hello there! How are you today?");
//flush, flushing will write the data to the client
writer.flush();
}
} catch(IOException e)
{
//print an exception if it occurred, if an exception occurrs its most likely just a disconnection exception
e.printStackTrace();
}
}).start();
}
And here is an example of a client
//connect to the server at "localhost" on port 6000
//you create a SSLSocket by getting the default SSLSocketFactory which will create a new SSLSocket
//you need to cast it since it actually returns Socket but SSLSocket extends Socket and this returns SSLSocket so it is safe
SSLSocket client = (SSLSocket) SSLSocketFactory.getDefault().createSocket("localhost", 6000);
System.out.println("Connected");
//set the enabled ciphersuites to everything supported so the server can decide the ciphersuite, you can modify this to specified ciphersuites
client.setEnabledCipherSuites(client.getSupportedCipherSuites());
//begin a handshake more info about handshakes in https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.1.0/com.ibm.mq.doc/sy10660_.htm
client.startHandshake();
/*
create a print writer so you can write a line, not only a message like BufferedWriter does,
if for some reason you still want to use BufferedWriter use
writer.write(message + "\n");
*/
PrintWriter writer = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
//Create a new reader
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
//write a request
writer.println("Hi there!");
//flush, flushing will write the data to the server
writer.flush();
System.out.println("Written");
System.out.println("Waiting for responses...");
//create a while loop reading responses (lines)
//you may want to do this while loop in another thread
String response;
while((response = reader.readLine()) != null)
{
System.out.println("Received response: " + response);
}

Socket Multithreading - Reading input stream pauses thread

I'm testing out sockets on my local machine. I'm trying to run both a socket and server in one program using threads. My server is an echo server so that it sends back whatever message it receives. My problem is that when I start both threads, on both the client and server, they 'freeze' when they reach the part where I read from the input stream. It works fine up to the part where the client sends the message. Afterwards, it simply stops as it appears that the client is waiting for a message and so is the server even if I already sent a message to the server via writing to the outputstream. What's wrong with the code?
Client.java
#Override
public void run() {
try {
Socket socket = new Socket("localhost", 22600);
BufferedReader br = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream()));
BufferedReader input = new BufferedReader(new InputStreamReader(
System.in));
System.out.println("Client 1");
while (true) {
System.out.print("\nEnter text : ");
String inputText = input.readLine();
writer.write(inputText);
System.out.println("Client 2");
System.out.println("Client 3");
String s = br.readLine();
System.out.println("CLIENT RECEIVED : " + s);
}
} catch (Exception e) {
e.printStackTrace();
}
}
Server.java
#Override
public void run() {
try {
ServerSocket server = new ServerSocket(22600);
Socket socket = server.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream()));
System.out.println("Server 1");
while (true) {
System.out.println("Server 2");
String s = br.readLine();
System.out.println("Server 3");
if (s == null) {
System.out.println("NULL SERVER SIDE ERROR");
break;
}
writer.write("ECHO : " + s);
System.out.println("SYSOUT ECHO " + s);
}
server.close();
} catch (Exception e) {
e.printStackTrace();
}
}
You are writing a string that does not have an end-of-line at its end.
String inputText = input.readLine();
writer.write(inputText);
System.out.println("Client 2");
The inputText string does not include the end-of-line you typed. And you write it as-is to the server. However, the server tries to read a line:
String s = br.readLine();
System.out.println("Server 3");
So it will keep waiting until the client sends in a newline. But by now the client is waiting for an answer from the server, and now they are deadlocked.
So, you should add a writer.newLine() to the client, as well as the server's echo, which suffers from the same issue. It's also recommended, after each write, to use writer.flush(), on both server and client. Otherwise, it may wait until the buffer is full before actually writing, and the same deadlock will result.
The readLine method of BufferedReader requires a new line terminator to return a value (unless the end of Stream is reached), and then returns the line without this character. So the Client
Reads a line from the user into the variable inputText
Client writes inputText to the OutputStream
Server receives data, but waits until it receives a new line (which it does not).
If you wish to use new line as a delimiter for communication, append this to the end of the data sent
writer.write(inputText + "\n");

Java - Server and Client not interacting (Socket)

I'm currently working on a small chat-program. The 2 classes, I have a problem with are the classes containing the clientside and the serverside of a socket. I want them to interact, sending a string from the server to the client, but the string isn't received apparently. I also don't get an error. Can someone explain, why this happens or how to fix it?
Codesnippet from the client:
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true) {
System.out.println("WAITING");
System.out.println(br.readLine());
}
} catch(Exception ex) { ex.printStackTrace(); }
Codesnippet from the server:
// Method of the code throws Exception
System.out.println("Waiting for someone to connect.");
Socket socket = serverSocket.accept();
System.out.println("Someone connected.");
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os, true);
while(true) {
System.out.println("WRITING");
pw.write("hi");
}
The output is currently:
server:
WRITING
WRITING
WRITING
WRITING
WRITING
...
client:
WAITING //only once
The client expects a complete line:
br.readLine()
and blocks until the line is complete.
But the server only sends "hi" repeatedly, without ever sending any EOL character:
pw.write("hi");
So the line never ends, and the client keeps blocking.
Use pw.println("hi");
The server buffer is not getting flushed. If you change pw.write(...) to pw.println(...) it should fix the issue - println(...) auto-flushes the buffer

Communication between Client and Server using Sockets

Okay this is a revised question from earlier today, I have included code to help explain the problem. I am sending two messages from the client to the server. The server then picks the messages up and processes them. The server finally attempts to send a message back to the client(please note in the server code "testmessage"), it is here I am having problems. Either I am not recieving the message at the client side or sending it incorrectly from the server side.
public class ClientConnection {
String address, language, message;
int portNumber;
Socket clientSocket = null;
public ClientConnection(String lan, String mes, String add, int pn) throws IOException{
address = add;
portNumber = pn;
language = lan;
message = mes;
}
public String createAndSend() throws IOException{
// Create and connect the socket
Socket clientSocket = null;
clientSocket = new Socket(address, portNumber);
PrintWriter pw = new PrintWriter(clientSocket.getOutputStream(),true);
BufferedReader br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
// Send first message - Message is being correctly received
pw.write(language+"\n");
pw.flush();
// Send off the data
// Send the second message - Message is being correctly received
pw.write(message);
pw.flush();
pw.close();
// Send off the data
// NOTE: Either I am not receiving the message correctly or I am not sending it from the server properly.
String translatedMessage = br.readLine();
br.close();
//Log.d("application_name",translatedMessage); Trying to check the contents begin returned from the server.
return translatedMessage;
}
Server Code:
public class ServerConnection {
public static void main(String[] args) throws Exception {
// Delete - Using while loop to keep connection open permanently.
boolean status = false;
while( !status){
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(4444);
} catch (IOException e) {
System.err.println("Could not listen on port: 4444.");
System.exit(1);
}
Socket clientSocket = null;
try {
clientSocket = serverSocket.accept();
} catch (IOException e) {
System.err.println("Accept failed.");
System.exit(1);
}
// Delete - Working as of here, connection is established and program runs awaiting connection on 4444
BufferedReader br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String language = br.readLine();
String message = br.readLine();
// Test - Works
System.out.println(language);
// Test - Works
System.out.println(message);
// Delete - Working as of here, both messages are passed and applied. Messages are received as sent from client.
TranslateMessage tm = new TranslateMessage();
String translatedMessage = tm.translateMessage(language, message);
// NOTE: This seems to be where I am going wrong, either I am not sending the message correctly or I am not receiving it correctly..
// PrintWriter writer = new PrintWriter(new BufferedOutputStream(clientSocket.getOutputStream()));
PrintWriter pw = new PrintWriter(clientSocket.getOutputStream(),true);
// Send translation back
System.out.println(translatedMessage);
// pw.write(translatedMessage+"\n");
pw.write("Return test"); // Test message!
pw.flush();
// Send off the data
pw.close();
br.close();
clientSocket.close();
serverSocket.close();
}
}
}
The code is a bit of a mess and I can see a few duplicates, I have commented where I feel the problems occour.
Thanks for any help!
You are using BufferedReader.readLine() to read the response from the server, but in the test case you are sending a string that is not terminated with a \n or \r\n, so it will not get the line as far as I can tell from the docs...
public String readLine()
throws IOException
Read a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed.
Returns:
A String containing the contents of the line, not including any line-termination characters, or null if the end of the stream has been reached
An additional suggestion...
When writing request response protocols like this I would not rely on line endings to terminate the requests or responses. Typically I would use either a fully formatted JSON string, or my preference is for a binary protocol where all requests and response are prepended with a binary count (usually 4 bytes bigendian/network byte order). Then the client and server reads the 4 bytes then reads the number of bytes that follow. This handles the packet fragmentation that typically happens over network connections, also it helps avoid DOS attacks by malicious users sending long strings that never terminate.
In Java you can use ByteBuffer.order() to handle bigendian numbers.

Categories

Resources