I couldn't find a similar post here, so here it is:
When I start the server and a couple of clients, I want the users on all clients to be able to input data to be processed. Also, I want the server to be able to exit the listening loop once all client sessions are terminated.
Server code listening loop:
//Console console = System.console();
// or
//InputStreamReader str = new InputStreamReader(System.in);
//BufferedReader uinp = new BufferedReader(str);
// ...
try (ServerSocket serverSocket = new ServerSocket(portNumber)) {
while (listening) {
new SocketServerThread(serverSocket.accept()).start();
//String serverInput = console.readLine("> ");
//if ( serverInput.equalsIgnoreCase("exit") ) { listening = false; }
// or
//System.out.print("> ");
//if ( (uinp.readLine()).equalsIgnoreCase("exit") ) { listening = false; }
}
} catch (IOException e) {
System.err.println("Could not listen on port " + portNumber);
System.exit(-1);
}
Here's the thing: 1) If I code the server to be able to receive input and process it (like an "exit" string to tell the server to exit the listening loop), it seems to me that the second client's thread won't run, which I find quite peculiar. 2) If I don't code the server to be able to exit the listening loop, how can the server exit the loop at the user's will?
As you can see, I've tried (a) Console and (b) InputStreamReader and BufferedReader, but none of them worked.
So, my question is this: how can I have the server quit the listening loop at the user's will and allow the second client's thread run at the same time?
I think you have confused the purpose of client server architecture.
A server is a centralized system to which clients can connect and request one or more services. So what you're saying is that you want to stop the centralized system i-e the server and still let other clients keep running? This is not possible. Because a client can not exist without a server (It won't be called a client if it doesn't has a server in the first place)
So if you quit server's listening loop, no client can connect to it. Hence no client thread can be executed.
If you want to that all server-client sessions are terminated at the will of the clients' users. U can do a workaround.
Keep a count variable at the server end. Increment it with each new client coming and decrement on the client leaving. Also, keep a boolean variable like toClose initialised with false at server end again. Now let's say client 'A' tells server to close (like "exit" command as u have already done), you change the value of toClose to true.
Now at the end of each client's session, you check something like this:
if (toClose == true && count <=0 )
exitListeningLoop()
Related
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.
I'm trying to write a client server application.
the clients post service requests and the server receives them and display them in a tech room.
everything was fine until I tried to send a screen capture to a special admin client, that should receive a screen capture of the list of live requests.
my main server listening thread looks like this :
while (true) {
clientSocket = echoServer.accept();
log.debug("Server Started Listening");
is = new DataInputStream(clientSocket.getInputStream());
os = new PrintStream(clientSocket.getOutputStream());
// An option for a stop listening button. currently not available !
if( listening==true ) {
line = is.readUTF();
os.println(line);
System.out.println(line);
RequestReciever.pharseToRequest(line);
}
The pharseToRequest Method looks like this :
public static void pharseToRequest(String input) {
List<String> list = new ArrayList<String>(Arrays.asList(input.split(";;;")));
if (list.get(0).equalsIgnoreCase("Login") && list.get(1).equalsIgnoreCase("Login") && list.get(2).equalsIgnoreCase("5"))
{
_adminClients.add(list.get(4));
updateScreenCapture();
AdminClientUpdate tmp = new AdminClientUpdate(list.get(4));
Thread aCU = new Thread (tmp);
aCU.start();
}
else
{
ServerRequest newReq = new ServerRequest(list.get(0), list.get(1), Integer.parseInt(list.get(2)),list.get(3),list.get(4));
addRequest(newReq);
}
}
when the client sends "Login" as the data, I treat him as an ADMIN CLIENT, and try to send it a screencapture, via the AdminclientUpdate thread.
and when a client sent regular data, I just update it on the server screen with the "addRequest" method.
the problem I have is when I try to send the screen capture, it will find its way to the admin client and will be updated there correctly, but my server gets an error and stops working.
I suspect it might have something to do with the listening thread still have data in it from the old request while the AdminclientUpdate thread transferring data, or something.
This is the Error I get from the server , after sending the screen capture :
java.io.EOFException
at java.io.DataInputStream.readUnsignedShort(Unknown Source)
at java.io.DataInputStream.readUTF(Unknown Source)
at java.io.DataInputStream.readUTF(Unknown Source)
at ListeningThread.run(ListeningThread.java:50)
at java.lang.Thread.run(Unknown Source)
The serversocket.accept() continutes reading as is just received a new request from a client ,
when it really did not...
instead of pausing and waiting for a new request...
I'm stuck with this for two weeks now, and very frustrated.
please HELP.
David.
In my case The server should start a new thread to handle every accepted connection, instead of processing each one in-line in the accept thread.
But also I tried to get the first update via the serversocket instead of the login initialization.
now, after getting the 1st update while logging in, I added a Server Socket on the client side so it will keep listening for further updates from server.
EJP's idea gave me the first lead.
I have implemented a socket with a server and single client. The way it's structured currently, the server closes whenever the client closes. My intent is have the server run until manual shutdown instead.
Here's the server:
public static void main(String args[])
{
;
try
{
ServerSocket socket= new ServerSocket(17);
System.out.println("connect...");
Socket s = socket.accept();
System.out.println("Client Connected.");
while (true)
{
work with server
}
}
catch (IOException e)
{
e.getStackTrace();
}
}
I've tried surrounding the entire try/catch loop with another while(true) loop, but it does nothing, the same issue persists. Any ideas on how to keep the server running?
It looks like what's going to happen in your code there is that you connect to a client, infinitely loop over interactions with the client, then when someone disrupts the connections (closes clearning, or interrupts it rudly - e.g., unplug the network cable) you're going to get an IOException, sending you down to the catch clause which runs and then continues after that (and I'm guessing "after that" is the end of your main()?)...
So what you need to do is, from that point, loop back to the accept() call so that you can accept another, new client connection. For example, here's some pseudocode:
create server socket
while (1) {
try {
accept client connection
set up your I/O streams
while (1) {
interact with client until connection closes
}
} catch (...) {
handle errors
}
} // loop back to the accept call here
Also, notice how the try-catch block in this case is situated so that errors will be caught and handled within the accept-loop. That way an error on a single client connection will send you back to accept() instead of terminating the server.
Keep a single server socket outside of the loop -- the loop needs to start before accept(). Just put the ServerSocket creation into a separate try/catch block. Otherwise, you'll open a new socket that will try to listen on the same port, but only a single connection has been closed, not the serverSocket. A server socket can accept multiple client connections.
When that works, you probably want to start a new Thread on accept() to support multiple clients. Simplest way to do so is usually to add a "ClinentHandler" class that implements the Runnable interface. And in the client you probably want to put reading from the socket into a separate thread, too.
Is this homework / some kind of assignment?
I'm trying to create a multi-client/server application in Java. I'm having quite some issues because my threads seem to get entangled... Here's what I'm trying to do.
I have a Server-class that accepts clients by using this piece of code:
while(true){
Socket socket = serverSocket.accept();
}
My server should remember the clients that are connected so I create a new Thread called ClientThread with that socket and place that Thread in a List on the Server
That Thread listens to Command-objects that are sent from the client. If it receives a Command, it needs to send it to the Server instance for further processing (without creating a new instance of Server on that ClientThread). I've tried to do this by adding the Server-instance to this Thread when it's created. (Is this the right way?)
My Server should also be able to send Objects back to the clients (1 or more) at any time. I'm trying to do this by using socket.getOutputStream() that is kept in the ClientThread
How should I organize my Threads so that every client is constantly listening to accept objects from the server AND that they can send objects to the Server at any time.
I know this isn't really a specific question, but if you know some info or tutorials that might be helpful for this use case, I'd be really thankful.
btw: I know how to create sockets and send (serializable) objects and so on.. I'm just stuck on how to organize everything
You appear to have tangled Thread and objects. I would
a) make sure you are not extending Thread anywhere or calling your objects XxxxThread. Using an ExecutorService to manage your threads is a good idea.
b) have a simple model for responding to client commands, e.g. each client thread reads a task and then performs a task.
c) Have a wrapper for each connections, e.g. with a sendMessage method.
Since you already know about sockets and threads, I send you the idea pseudo code (case need a specific part of code just let me know)
One thing you did not mention is how to keep track of clients, by its IP o by any other method like an ID? Can any given device open more than one connection with different client ID? Or you'll only accept one connection per device? In any case, if a client is already in the list, what do you suposse to do? Will you communicate the created thread the new socket? Will you destroy that thread and create a new one? Or maybe you'll ignore this new request?
This is my idea (taken from a working application):
Server prepares the server socket and wait in the accept state.
Once a client connects, the server start a thread to attend the client passing the socket it just created with the accept command. When the thread which attends the client starts, the very first message it receives from the client should be a password o special signature to let the client gets in (this is optional).
Server code:
Prepares the server socket which listen in a well known port
Clear client list;
While (!Terminated)
{
// if you want to impose a limit for connections, check it here:
if (Is the list of connected client full?)
{
Sleep(reasonable time in seconds or miliseconds);
continue;
}
ClientSocket = ServerSocket.Accept();
if the client's IP is already in the list
{
depends on what you want to do.
}
else
{
Add client's IP to the list
Start (create) new client Tread(ClientSocket);
}
}
// when server finish
If (client list is not empty?)
{
Kill all threads
or
Wait until all threads are done
or
Wait an amount of time and then kill those remaining.
}
thread client code:
// This is optional, just to make sure a valid client is connected
Read packet from ClientSocket
if (!Is_the_passport_packet)
{
close socket;
return;
}
// if passport is not required, start here
Try
{
While (!Terminated)
{
if (read packet from client);
{
switch (packet.Command)
{
// In your question you said you want the Server thread to process the request
// I guess you have your requirements to do so,
// anyway, you must use a mutex o some other synchronization method.
case TASK_1:
[sync] process TASK_1(packet, ClientSocket);
break;
case TASK_2:
[sync] process TASK_2(packet, ClientSocket);
break;
etc ….
case WORK_DONE:
Close Socket;
return;
default:
Log(received an unknown command: packet.command);
break;
}
}
else if (Client has quit (closed/broken socket))
{
// as you may know, a socket is consider shutdown when you received a 0 length data
// and a broken connection when received -1 in either case all you have to do is
Close Socket;
return;
}
}
}
catch (Exception e)
{
Log(received an exception: e.message);
}
finally
{
Remove this client from the client's list
}
I have following Socket server's code that reads stream from connected Socket.
try
{
ObjectInputStream in = new ObjectInputStream(client.getInputStream());
int count = 10;
while(count>0)
{
String msg = in.readObject().toString(); //Stucks here if this client is lost.
System.out.println("Client Says : "+msg);
count--;
}
in.close();
client.close();
}
catch(Exception ex)
{
ex.printStackTrace();
}
And I have a Client program, that connects with this server, sends some string every second for 10 times, and server reads from the socket for 10 times and prints the message, but if in between I kill the Client program, the Server freezes in between instead of throwing any exception or anything.
How can I detect this freeze condition? and make this loop iterate infinitely and print whatever client sends until connection is active and stable?
The problem is that the server side of the socket has no way of knowing that the client connection closed because the client code terminates without calling .close() on the client side of the socket, and therefore never sends the TCP FIN signal.
One possible way of fixing this would be to create a new Watcher thread that just periodically inspects the socket to see if it is still active. The problem with that approach is that the isConnected() on the Socket will not work for the same reason stated above so the only real way to inspect the connection is to attempt to write to it. However, this may cause random garbage to be sent to a potentially listening client.
Other options would be to implement some type of keep-alive protocol that the client should agree to (i.e., send keep-alive bits every so often so the Watcher has something to look for). You could also just move to the java.nio approach, which I believe does a better job at dealing with these conditions.
This thread is old, but provides more detail: http://www.velocityreviews.com/forums/t541628-sockets-checking-for-dropped-connections-and-close.html.