Is it possible to re-use InputStream after client disconnects? - java

My server opens a serversocket on a port then waits for clients to connect in an infinite loop. The first client that connects sends some integers and maps then disconnects, however, the server should still wait for other clients. My issue is, that after correctly receiving these from the first client, I can't open any more scanners and printwriters on the same socket's inputstream/outputstream. The client can connect but cannot send nor recieve any data, I just don't understand what's blocking the stream here:
ServerSocket serv = new ServerSocket(port);
while(true) {
Socket sock = serv.accept();
ObjectInputStream instream = new ObjectInputStream(sock.getInputStream());
// receiving and handling the data here
Scanner scan = new Scanner(sock.getInputStream());
// trying to receive lines here from scanner but it's blocked
}
In the client's code I mentioned previously, after sending the maps and other objects, I flush the ObjectOutputStream.
Also, I had a similar problem with scanners before when I closed a scanner on System.in earlier in my code then I couldn't use any scanners on System.in after that, but in this Server code I'm not closing anything after receiving the objects so that's why I don't understand why it's blocked.
So is it possible to re-use the InputStream like this, or should I open another socket for other incoming clients maybe? What should I change if it's not possible?

Related

There is Option to read data from socket without the server close the socket?

I created application in android studio and server in python and I want to connect the client to my server with one socket per client so that I don't need to close the socket and I could send data from the server to any connected client I want.
My server is multi-threaded and it working only if I'm closing every socket after sending data back.
At my java app I created class that contain socket object and methods to contact with the server and I think that my problem is at this method:
private static String getMessageFromServer() throws IOException {
in = new InputStreamReader(sock.getInputStream());
bf = new BufferedReader(in);
return bf.readLine();
}
As you can see this method reads data from the server and the problem is that it keep reading data until the server close the socket, so I'm asking if there is any option to read data from server and stop the reading after I already got the data and without the server closes the socket?
Thanks :-)

Writing to a Socket which wasn't "accepted" on a ServerSocket?

I have a simple server that looks like this
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(4999);
Socket s = ss.accept();
InputStream is = s.getInputStream();
while (true) {
System.out.println(is.read());
}
}
It accepts a single client socket, reads from it forever and prints out the number that was sent from the client socket.
I have a client like this
public static void main(String[] args) throws IOException, InterruptedException {
int id = Integer.valueOf(args[0]);
Socket s = new Socket("localhost", 4999);
OutputStream os = s.getOutputStream();
while (true) {
os.write(id);
Thread.sleep(1000L);
System.out.println("Sent");
}
}
It connects to the server and sends the number it received as command-line argument forever.
I start the server.
I start a client like java -jar client.jar 123.
Then I start another client like java -jar client.jar 234.
No errors happen on neither the server side nor the client side.
Each client prints the Sent message every 1 second, neither gets blocked.
The server only prints 123 until the end of times.
My questions:
What happens with the bytes written by the second client?
I would expect the second client to receive an error or get blocked or something, but nothing happens. Why?
Note: I know that this code is bad and I should handle clients in threads and call ServerSocket.accept() and all that jazz.
Update:
Based on the accepted answer the solution is to create the server like new ServerSocket(4999, 1); where 1 is the size of the backlog. 0 would mean to use whatever the default setting is configured in Java.
By using 1 there can be only one connection in a "non-accepted" state. Anymore client trying to connect gets a connection refused!
The bytes written by the second client will go into the client Socket's send buffer, since you're writing to a socket that doesn't have an established connection yet. Eventually the send buffer will fill up. You could try playing with Socket.setSendBufferSize() to see what happens when it fills up.
A ServerSocket has a listen backlog for connections that haven't been accepted yet. The second client's connection is in the backlog, and if the server would ever get around to accepting it (which it won't, with your code, but there is no way for the client to know that), it would be established and the client's send buffer would be sent merrily along. You could try calling the constructor ServerSocket(int port, int backlog) with a backlog of 1 to see what happens to the client when the listen backlog fills up - it should get connection refused.

How can the client connect to the server when the server is in a busy loop?

Server looks like this:
public class Server {
public static void main(String[] args) {
Server server = new Server();
server.start(5006);
}
private void start(int port) {
try(ServerSocket serverSocket = new ServerSocket(port);
Socket clientSocket = serverSocket.accept();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), "UTF-8"));) {
String line;
while (true) {
while((line = bufferedReader.readLine())!=null){
System.out.println("line = " + line);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
The client looks like this:
public class Client {
public static void main(String[] args) {
Client client = new Client();
client.start("localhost", 5006);
}
private void start(String localhost, int port) {
Random random = new Random();
try (Socket socket = new Socket(localhost, port);
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"))) {
while (true) {
int i = random.nextInt();
bufferedWriter.write(String.valueOf(i));
bufferedWriter.newLine();
System.out.println("i = " + i);
//sleep(bufferedWriter);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void sleep(BufferedWriter bufferedWriter) throws Exception{
//bufferedWriter.flush(); //Has to enabled if the wait below is to work
TimeUnit.SECONDS.sleep(3);
}
Context:
As is evident from the code, the server is single threaded. It accepts a connection from the client and goes into a busy loop processing data from the client socket. The server is intentionally handicapped.
Running one instance of the client program works as intended. The client ends random integers and the same are printed on the server's console.
Questions:
1) While one instance of the client is still running, spin up another instance of the client program.
How is this instance of the client able to connect to the server when the server is still in the busy loop (while(true))?
The client goes so far as to fill up the buffered writer and then just hangs; waiting for the server to consume the stream.
2) In the client program, uncomment the 'sleep' method and re-run.
The client program hangs. The server does not receive anything. Why? I just want to write to the buffer every 3 seconds. Nonsensical but let's suppose it is a sane thing to do for arguments sake. I also put in the sleep after we send the new line to the server just to make sure the server prints its input.
If you uncomment the flush in the sleep method, it starts working again. Why?
How is this instance of the client able to connect to the server when the server is still in the busy loop (while(true))? The client goes so far as to fill up the buffered writer and then just hangs; waiting for the server to consume the stream.
Because of the listen backlog queue. The operating system completes the inbound connection and places it on the backlog queue. accept() blocks while the backlog queue is empty and then removes the first connection from it. The client's connect operation is complete before the corresponding accept() is called, and because the client now has a connection it can now write a certain amount of data to it.
2) In the client program, uncomment the 'sleep' method and re-run. The client program hangs.
It sleeps for three seconds. It doesn't 'hang'. Because of the much slower output, it takes much (much) longer for the BufferedWriter's buffer to fill and output to be flushed to the server.
The server does not receive anything. Why?
Because you haven't flushed the buffer.
I just want to write to the buffer every 3 seconds. Nonsensical but let's suppose it is a sane thing to do for arguments sake. I also put in the sleep after we send the new line to the server just to make sure the server prints its input.
If you uncomment the flush in the sleep method, it starts working again. Why?
See above.
1) Two clients:
A. Client 1 steps:
(As EJP said), Connection established on OS level and placed in backlog queue.
Then it is waiting for ServerSocket to pick it and start reading from clientSocket InputStream
Client 1 writes data to its socket OutputStream - server's reader reads data from clientSocket InputStream
Server is busy, because infinite loop.
B. Client 2 steps:
(As EJP said), Connection established on OS level and placed in backlog queue.
By this code it will never be picked by ServerSocket. Server is indefinitely busy with indefinitely open Socket to Client 1.
All data written from Client 2 stays in the buffer on Client 2 side.
Note: Client 2 will never go beyond that point with current code even if Client 1 process be killed and connection from it to server be broken, server will end. There is no second accept() call to process request from Client 2
2) flush
when Client writes bytes to its socket OutputStream bytes goes to the buffer first not over network to the server.
flush forces OutputStream to send whatever is in the buffer at this moment.
Without flush data be send to the server when buffer is full.
If you do not have flush somewhere in the code flow, server never receives anything. It keeps waiting for data indefinitely in current code. But all data stay in buffer at client side waiting for flush or buffer be full.
3)
The client program hangs.
Assumption is not correct. Client program does not hang. It is running and fill the buffer, but nothing sends to the server until buffer is full.
With or without flush it does not matter is there sleep or not.
"No sleep" - buffer fills very quickly. "sleep" buffer fills once per 3 sec. It just need a time to fill the buffer.
If you uncomment the flush in the sleep method, it starts working again. Why?
Only server starts receiving data earlier, because client does not wait to fill the buffer to send data and flushes buffer after each write.

How server-socket with more than one backlog works?

I'm new to socket programming, I have problem in understanding serversocket.
assume we create a serversocket like this:
loadbalancerSocket = new ServerSocket(port, 20);
connection = loadbalancerSocket.accept();
and then after some stuff, write something in its buffer:
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream()));
writer.write("Hello!");
writer.flush();
writer.close();
My question is : How the connection understand which client should get the response of server? our backlog is 20 , and 20 client can connect to the server socket at the same time(As I understood).
In your example the first connected client gets the response. The backlog parameter does not mean number of clients that can connect in parallel. It is the maximum number of clients waiting for accepting connection.
The ServerSocket is not connected to any particular client. The connected socket is the socket returned from accept(). If you want to handle multiple clients in parallel you have to call accept() multiple times and handle connections separately. You can create a special thread for each connection for example.
accept() is typically called in a loop and the newly created connected socket returned from accept() is typically passed to a handler that is responsible for particular client.

Socket stream hangs when using IOUtils.copy()

I want to send an InputStream containing XML from the client to a server. Said server manipulates the XML stream using Streaming API for XML (StAX) and sends it back to the client.
I mange to send the XML to the server, the client receives the answer, but doesn't exit because the server never finishes reading the InputStream form the client.
But when the server is just dumping the input to System.out instead of sending something, reading finishes...
edit I forgot to mention that IOUtils.copy() is running in two separate threads on the client.
Here some code to reproduce this, I've replaced the StAX part with IOUtils.copy() on the server.
Server
ServerSocket serverSocket = new ServerSocket(port);
Socket clientSocket = serverSocket.accept();
InputStream in = clientSocket.getInputStream();
OutputStream out = clientSocket.getOutputStream();
IOUtils.copy(in, out);
Client
Socket socket = new Socket(host, port);
FileInputStream fin = new FileInputStream(file);
OutputStream out = socket.getOutputStream();
IOUtils.copy(fin, out)
InputStream in = socket.getInputStream();
IOUtils.copy(in, System.out)
You must flush and close InputStream and OutputStream.
like this:
ServerSocket serverSocket = new ServerSocket(port);
Socket clientSocket = serverSocket.accept();
InputStream in = clientSocket.getInputStream();
OutputStream out = clientSocket.getOutputStream();
IOUtils.copy(in, out);
//add
out.flush();
in.close();
out.close;
You are probably creating a deadlock.
Client - sends data to the server.
Server - loops data back to the client.
Client - reads data (that the server looped back)
So, while the client is sending data to the server, the server is sending it back to the client.
The client isn't reading that data, it's busy sending.
At some point the TCP buffers fill up, and the server blocks on sending more data to the client. This means the server is blocked on sending data, so eventually the TCP buffers on the receiving side of the server fills up as well, and the client blocks on sending data. Since the client is now blocked on sending, it'll never get to the part where it reads the data that is looped back, so the server will never un-block.
If you're sending a small document, you might not experience this, as all the data fits in the buffers, but once you fill up the buffers, the system will deadlock.
You have to multiplex the sending/receiving. e.g. you could start up a thread on the client that does
InputStream in = socket.getInputStream();
IOUtils.copy(in, System.out)
That way receiving can be done in parallel with the client sending data
Or you could use 2 threads on the server side. One for receiving the data, posting data to be sent back to another thread doing the sending (you'll have to build up an internal queue in this case between the threads).
As far as I remember IOUtils.copy() does not flush and does not close the stream. It seems that you do not call neither flush nor close too. Try it. I believe that this is your problem.

Categories

Resources