Non-blocking IO solution in Java - java

I'm writing a program that uses multiple SSL connections. Basically, my code has to 3 separate programs that can connect to one another and communicate through SSL sockets.
The problem I'm having is with blocking I/O. For example, my class CLAFrame.java has to be able to detect and handle input from a stream, but also be able to carry out other functions. I don't want this to block the entire program and freeze it up waiting for input (like it is now).
Here's how I had it set up.
static void initializeCLAConnection(){
try {
SSLServerSocketFactory sslserversocketfactory =
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
SSLServerSocket sslserversocket =
(SSLServerSocket) sslserversocketfactory.createServerSocket(3577);
SSLSocket sslsocket = (SSLSocket) sslserversocket.accept();
InputStream inputstream = sslsocket.getInputStream();
InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
OutputStream outputstream = sslsocket.getOutputStream();
OutputStreamWriter outputstreamwriter = new OutputStreamWriter(outputstream);
BufferedWriter bufferedwriter = new BufferedWriter(outputstreamwriter);
input=bufferedreader;
output=bufferedwriter;
System.out.println("Connection accepted from: "+sslserversocket.getInetAddress());
String string=null;
//RECEIVING INPUT
while(input.ready()){
System.out.println("CLA receiving msg");
// while ((string = input.readLine()) != null) {
string=input.readLine();
System.out.println(string);
/*
* HEADERS FOR INPUT RECEIVED
* Format: VAL Ryan Smith
* VAL - requesting validation id
*/
String header=null;
String rest=null;
header=string.substring(0, 3);
if(string.length()>3){
rest=string.substring(4, string.length()-1);
}
//perform actions based on input
switch(header){
case "VAL ":
//do something
requestValidationID(rest);
break;
default:
break;
}
}
}
// System.out.println("Successfully established CLA server on SSL port 3577");
catch (Exception exception) {
exception.printStackTrace();
}
}
Note - Previously, I had my while loop setup as
while ((string = input.readLine()) != null) {
However, clearly this will block I/O until input is received. I then switched to
while(input.ready()){
This has stopped the blocking but now my buffer (input) is not receiving anything at all when I write to the stream.
Is there a better way to do this? This program is set up as a GUI so I need the buffer to be able to receive input, but I also need to be able to simultaneously use other buttons and functions on the program without it blocking everything.
Should I make it multithreaded? A thread for reading the buffer and another for all other functions? I'm a bit confused what to try next.
Any input/advice would be greatly appreciated, thanks!

You're trying to re-invent the wheel here. There are many non-blocking frameworks out there, all built on top of Java NIO (which provides non-blocking IO functionality in Java).
Netty is one such example, very fast and not the highest abstraction.
Edit:
Considering this is a school project, and that you just need to be able to do things simultaneously, I'd say what you want isn't non-blocking code, you just want your blocking code to run in a separate thread.
See Java's Executors for that.

Related

How to display content that has been written to a Socket.OutputStream while maintaining a persistent connection?

Suppose that I have a multi-threaded web server that only allow clients to perform GET requests for a couple of HTML files. I want to maintain a persistent connection (i.e HTTP Connection: keep-alive) while "dynamically" displaying the content for each request the client makes. Like if they first request index.html then foo.html etc. The problem right now is when I don't close the streams and socket, the program will hang until it happens.
Simply put, the multi-threaded web server consist of a thread pool (Java's ExecutorService) with a ServerSocket that listens to a specific port (e.g 9000) and selects a thread from the threadpool to handle the opening of a client socket to the server. It is basically the same setup as showed in http://tutorials.jenkov.com/java-multithreaded-servers/thread-pooled-server.html.
My modified setup looks like this:
WorkerRunnable.java:
public void run() {
try {
InputStream input = this.clientSocket.getInputStream();
OutputStream output = this.clientSocket.getOutputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
List<String> headers = readInputStream(input)
Request request = new Request(headers);
Response response = new Response(request);
// response.raw() returns correctly formatted HTTP
output.write(response.raw().getBytes(StandardCharsets.UTF_8));
// close the socket if the client specifies Connection: close
if (!request.keepAlive()) {
output.close();
input.close();
} else {
this.clientSocket.setKeepAlive(true);
}
} catch (IOException e) {
e.printStackTrace();
}
private List<String> readInputStream(InputStream input) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String line;
List<String> headers = new ArrayList<>();
while ((line = reader.readLine()) != null && !line.isEmpty()) {
headers.add(line);
}
return headers;
}
My problem is that the HTML only will be displayed when the input/output stream (and thus also the socket) are closed. As far as I understand, the Socket.InputStream will basically hang until it receives an EOF token - which it receives when the stream closes. But if I want to maintain a persistent connection, it doesn't really make sense to close the streams and client socket. So I was wondering how to maintain a persistent connection while also displaying the content of multiple GET requests from clients (assuming this is the correct approach)? If not, please let me know if I've approached this task wrongly.
I have tried to flush the output stream as suggested here, but the problem still persists.

Unsuccessful in trying to reuse Java client socket

I have a software driver which communicates with a third-party controller; I have an API for using the latter but no visibility of its source code, and the supplier is not co-operative in trying to improve things!
The situation is as follows.
To send a request to the controller, I send an XML packet as the content of an HTTP POST to a servlet, which then sends me the response. The original code, implemented by a previous developer, works stably using java.net.Socket. However, our driver is implemented such that a new socket is created for EVERY request sent and, if the driver gets busy, the third-party controller struggles to keep up in terms of socket handling. In fact, their support guy said to me: "You really need to leave 5 seconds between each request...". This simply isn't commercially acceptable.
To improve performance, I wanted to try leaving our end of the socket open and reusing the socket pretty much indefinitely (given that connections can drop unexpectedly of course, but that's the least of my concerns and is manageable). However, whatever I seem to do, the effect is that if I use Comms.getSocket(false), a new socket is created for each request and everything works OK but bottlenecks when busy. If I use Comms.getSocket(true), the following happens:
Controller is sent first request
Controller responds to first request
Controller is sent second request (maybe 5 seconds later)
Controller never responds to second request or anything after it
postRequest() keeps getting called: for the first 12 seconds, the console outputs "Input shut down ? false" but, after that, the code no longer reaches there and doesn't get past the bw.write() and bw.flush() calls.
The controller allows both HTTP 1.0 and 1.1 but their docs say zilch about keep-alive. I've tried both and the code below shows that I've added Keep-Alive headers as well but the controller, as server, I'm guessing is ignoring them -- I don't think I have any way of knowing, do I ? When in HTTP 1.0 mode, the controller certainly returns a "Connection: close" but doesn't do that in HTTP 1.1 mode.
The likelihood is then that the server side is insisting on a "one socket per request" approach.
However, I wondered if I might be doing anything wrong (or missing something) in the following code to achieve what I want:
private String postRequest() throws IOException {
String resp = null;
String logMsg;
StringBuilder sb = new StringBuilder();
StringBuilder sbWrite = new StringBuilder();
Comms comms = getComms();
Socket socket = comms.getSocket(true);
BufferedReader br = comms.getReader();
BufferedWriter bw = comms.getWriter();
if (null != socket) {
System.out.println("Socket closed ? " + socket.isClosed());
System.out.println("Socket bound ? " + socket.isBound());
System.out.println("Socket connected ? " + socket.isConnected());
// Write the request
sbWrite
.append("POST /servlet/receiverServlet HTTP/1.1\r\n")
.append("Host: 192.168.200.100\r\n")
.append("Connection: Keep-Alive\r\n")
.append("Keep-Alive: timeout=10\r\n")
.append("Content-Type: text/xml\r\n")
.append("Content-Length: " + requestString.length() + "\r\n\r\n")
.append(requestString);
System.out.println("Writing:\n" + sbWrite.toString());
bw.write(sbWrite.toString());
bw.flush();
// Read the response
System.out.println("Input shut down ? " + socket.isInputShutdown());
String line;
boolean flag = false;
while ((line = br.readLine()) != null) {
System.out.println("Line: <" + line + ">");
if (flag) sb.append(line);
if (line.isEmpty()) flag = true;
}
resp = sb.toString();
}
else {
System.out.println("Socket not available");
}
return resp; // Another method will parse the response
}
To ease testing, I provide the socket using an extra Comms helper class and a method called getSocket(boolean reuse) where I can choose to always create a new socket or reuse the one that Comms creates for me, as follows:
public Comms(String ip, int port) {
this.ip = ip;
this.port = port;
initSocket();
}
private void initSocket() {
try {
socket = new Socket(ip, port);
socket.setKeepAlive(true);
socket.setPerformancePreferences(1, 0, 0);
socket.setReuseAddress(true);
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8));
br = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
System.out.println("### CREATED NEW SOCKET");
}
catch (UnknownHostException uhe) {
System.out.println("### UNKNOWN HOST FOR SOCKET");
}
catch (IOException ioe) {
System.out.println("### SOCKET I/O EXCEPTION");
}
}
public BufferedReader getReader() { return br; }
public BufferedWriter getWriter() { return bw; }
public Socket getSocket(boolean reuse) {
if (! reuse) initSocket();
return socket;
}
Can anyone help ?
If we assume that keep-alive thing is working as expected, I think the line while ((line = br.readLine()) != null) is a faulty one, as this is kind of infinity loop.
readline() returns null when there is no more data to read, e.g. a EOF, or when server/client closes the connection, that will break-down your reusing socket solution, since an open stream will never cause a null to a readLine() call, but blocking.
You need to fix the alg about reading a response (why not using implemented http client?), checking content-length, and when read the amount of required data from body, go for next loop by keeping the socket alive.
After that setting flag to true, you have to know what kind of data should be read(considering mime/content-type), besides that, the length of data, so reading data using readLine() may not be a good practice here.
Also make sure server allow for persistence connection, by checking if it respects it by responsing the same connection:keep-alive header.

client socket issues when reading

I am having a real problem trying to find a solution to my problem and hope you guys could help. I have seen many socket examples online but have been unable to modify them for my use. Tbh, im struggling to even get an understanding of sockets. What I have been able to modify so far is below.
My problem, I believe is that my client program is not reading the incoming message from the server, could someone use my example to demonstrate where I am going wrong. Something in my mind tells me that my client socket closes before having a chance to read any incoming message. perhaps getting the client to wait until a message is recieved? If waiting is what is needed, how is this achieved? Thanks in advance.
CLIENT:
try {
Socket socket = new Socket("localhost", 55555);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.write(score);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String rank = in.readLine();
System.out.println(rank);
in.close();
out.close();
socket.close();
}
catch(Exception e) {
System.out.print("Whoops! It didn't work!\n");
}
SERVER:
try {
System.out.println("Waitng for client to connect.....");
ServerSocket server = new ServerSocket(55555);
Socket socket = server.accept();
System.out.print("Client has connected!\n");
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String score = (in.readLine());
scor = Long.parseLong(score);
leaderboard(); ///// A METHOD THAT USES LONG SCORE TO CALCULATE RANKING- RETURNS A STRING VALUE CALLED RANK
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
System.out.print("Sending rank: " + rank);
out.write(rank);
out.close();
socket.close();
server.close();
}
catch(Exception e) {
System.out.print("Whoops! It didn't work!\n")`enter code here`;
}
Your code looks fine...
On your client program you're writing a line using:
out.write(score);
could you change this to:
out.println(score);
Also do the same on your server's program to take care of the reply:
out.println(rank);
Let me know how you go..
also if this helped, don't forget to upvote/mark this as a solution ;)
cheers
(Btw,as to what caused the problem:: in.read'LINE'() waits for the end of a line or newline(\n) for the string value to be saved. if you use out.write() then you have to and a newline character(\n) manually for the string to be read completely. if you use out.printline, then \n is added automatically to every string sent.
The readline method in your program was waiting for a newline character, which is why your program was stuck in that spot)

Android socket inputstream read (followed by an EPIPE)

I am creating some client-side socket software to read events from a server. (For example, streaming stock quotes).
PROBLEM: .read(b); is returning immediately with a value of -1. This causes an infinte loop and the phone becomes very hot. Additionally, all checks to s.isConnected(), isOpen(), isBound() return true. Essentially the socket looks connected. (This is an error scenario, so any value of .setSoTimeout(x) has no effect. 12 minutes, or leave empty. The .read(b) always returns -1 immediately).
When I write to it later, via the getOutputStream(), I receive an exception EPIPE (broken pipe).
Here's the core code (log statements/value checks omitted for brevity).
s.connect(new InetSocketAddress(host, port), CONNECT_TIMEOUT_MILLIS);
byte[] b = new byte[1024];
while (s.isConnected()) {
int bytesToRead = s.getInputStream().read(b);
if (bytesToRead <= 0) {
LOGGER.debug("no bytes read? trying again.");
continue;
}
processFrame(b);
}
If I cycle the 3g on the phone, it works fine. Sometimes it gets into this weird state.
Question
Am I doing something wrong? Is this the expected behavior? Is there existing code I could look at to show the right way to do socket programming on Android?
I use the following code without problem:
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
myLineProcess(line); //here you process you line result
}
good luck.
If read(byte[], ...) returns < 0, the peer has closed the connection, so you must close the socket and exit the loop. There will never be any more data.
Input streams are blocking, so the only way read(byte[], ...) can return zero is if you specify a zero length buffer or a zero length, depending on which overload you call. As you aren't doing that, it will never return zero.
Instead it will do exactly what it says in the Javadoc: either return -1 meaning EOS, or block until at least one byte of data is available.
Am I doing something wrong?
Almost everything. Your loop testing for <= 0 is completely pointless and completely incorrect. And so is testing isConnected(). That only tells you whether you ever connected this Socket. It doesn't change with the state of the connection. The return code of -1 tells you that. Your loop should read:
while ((bytesToRead = s.getInputStream().read(b)) > 0)
{
// do something with b[0..bytesToRead -1].
}
s.close();
Well i don't know whether this would be the appropriate answer for this, but still i will like to give you the code, which does works well every time.
Please keep it simple,
Try using InputStream, InputStreamReader, BufferedReader, OutputStream, PrintWriter.
Client Side:
Socket s = new Socket();
s.connect(new InetSocketAddress("Server_IP",Port_no),TimeOut);
// Let Timeout be 5000
Server Side:
ServerSocket ss = new ServerSocket(Port_no);
Socket incoming = ss.accept();
For Reading from the Socket:
InputStream is = s.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
boolean isDone = false;
String s = new String();
while(!isDone && ((s=br.readLine())!=null)){
System.out.println(s); // Printing on Console
}
For Writing to the Socket:
OutputStream os = s.getOuptStream();
PrintWriter pw = new PrintWriter(os)
pw.println("Hello");

C++ Client and Java Server

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.

Categories