Hi, I want to code a client/server app such that there will be one server and multiple clients interacting with the server. The clients will send messages, like "Hello, server," and the server will send messages back.

I know how to write a simple client/server app, however im confused as to how to write a simple app that involves multiple clients and the server being able to send replies to the right client.

So Client 1 and Client 2 both send hello messages to the server, i want the server to be able to say back, "Hello, Client1" to client 1 and "Hello, Client 2" to client 2. I want to run these clients and server on the same computer though, as a test before moving on to different computers.

Does anyone know how i can do this? Perhaps a small code example that can help?

Cheers

Recommended Answers

All 16 Replies

It's not too hard to do a basic version of this.

All it involves is creating a Thread for each client that connects to the server.

So if client A is being processed with thread A, and client B with thread B, any messages thread A sends will go to client A, as thread A only knows about this client.

And if you want to send a message to all clients, you could use a static array that stores all clients, and a method to send a message to all clients in the array.

If you already have a Client/Server app written, it shouldn't take too long to add that functionality!

Here's a good tutorial on threads in Java, if you're not familiar with them:

http://download.oracle.com/javase/tutorial/essential/concurrency/index.html

Now, you just have to code it...

I'm still confused about this. I do know how to write multithreading, but how do I manage to gather all the clients into an array when I don't even know how to identify them? That's part of what I was asking about - how do I identify one client from another?

The client's will have to identify themselves if you need to know who each one is.
Otherwise, you can keep the communications with each client separate by using a thread for each client. As each client connects to the server, the server could assign them an id of an int that would be incremented for each new client and then create a new thread to process the communication with that client.

Ok, I see what you mean. I've been working on it and I've been able to accomplish sending messages and identifying each client (you have no idea how happy I am lol). Anyway, I'm using an ExecutorService to execute each thread for each client.

But eventually, I want the clients to terminate. I know how to terminate clients by closing them. What I want to know is if I shut down a client by closing its socket, does that automatically close the thread that was created for the client? Because I really must close the thread.

That depends on how you are creating the Thread, and how you've actually coded the class.

I'm guessing the server enters a loop while the client is connected, if so then when the client terminates the connection, as long as the server leaves the loop close's the socket, and there are no infinite loops after that, the thread should then close.

As I was saying about the static array, you can identify each client by the this keyword in it's thread. For example, say I have a class MultiConnections, that gets instantiated each time a client connects to the server:

public class MultiConnections implements Runnable {
// Variables here...

 private static ArrayList<MultiConnections> connections = new ArrayList<MultiConnections>();
.......
public void run() {   

.....
// Add the client 
connections.add( this );

.....

// Remove the client after they terminate the connection
connections.remove( this );
.....
}

Now the array will hold the actual object associated with the client, making it easy to create a synchronized method to iterate over the ArrayList and send a message to all clients.

Hope that's in someway helpful!

hi chaospie,

thanks for the arraylist suggestion. That would help very well.

This is how I programmed my server:

public class SimpleServer{
public SimpleServer2() {
runClient = Executors.newCachedThreadPool();
try {
serverSocket = new ServerSocket(8888,3);
System.out.println("\nAwaiting connection from clients...");
while(true) {
WorkingThread wt;
wt = new WorkingThread(serverSocket.accept(), ++clientNumber);
runClient.execute(wt);
}
} catch (IOException e) {
e.printStackTrace();
}
}

public class WorkingThread implements Runnable {
//code for processing client in here
}
}

Everytime the server accepts a connection fromm a client, it executes. Now, it seems I'm implementing some kind of infinite loop, but I don't know any other way to do it, so im not sure how to get the server out of the loop.

Also, while running each thread, I noticed that if i put in a code in the server where it's awaiting inputstream from the clients (so say, each client has to send something to the server at some point), for some reason while the server is waiting for an input stream from one client, every other client are stuck in limbo until that client responds! I don't think this should be right, right?

for some reason while the server is waiting for an input stream from one client, every other client are stuck in limbo until that client responds!

Do you mean put the code in the SimpleServer class or the WorkingThread class?

I assume you meant the WorkingThread class, how have you coded that class?

Here's the code of the class:

public class SimpleServer2 {

	ServerSocket serverSocket;
	Socket connection;
	ObjectOutputStream output;
	ObjectInputStream input;
	ExecutorService runClient;
	

	private static int numLeft = 0;
	
	String messageOut = "";
	String messageIn = "";
	
	int clientNumber = 0;
	int executed = 0;

	public SimpleServer2() {
		runClient = Executors.newFixedThreadPool(1);
		try {
			serverSocket = new ServerSocket(8888);
			System.out.println("\nAwaiting connection from clients...");
			while(true) {
				WorkingThread wt;
				wt = new WorkingThread(serverSocket.accept(), ++clientNumber);
				runClient.execute(wt);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public class WorkingThread implements Runnable {
		
		private Socket connection;
		private int number;
		
		public WorkingThread(Socket connection, int number) {
			this.connection = connection;
			this.number = number;
			
			System.out.println("Connection received from " + connection.getInetAddress() + ", Client " + number);
			System.out.println("More clients will connect for authentication...");
		}
		
		public void run() {
			
			try {
				output = new ObjectOutputStream(connection.getOutputStream());
				output.flush();
				
				input = new ObjectInputStream(connection.getInputStream());
				System.out.println("Input/Output streams initialised...");
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			try {
				++numLeft;
				
				System.out.println("Number Count: " + numLeft);
				System.out.println("Thread numbers: " + Thread.activeCount());
				messageOut = "\nClient " + number + " has been initialised.\n"
				+ "Client is running under thread: " + Thread.currentThread().getName()
				+ "\nPlease present code...";
				output.writeObject(messageOut);
				output.flush();
				
				System.out.println("\nAwaiting Client reply...");
				messageIn = (String)input.readObject();
				System.out.println("\nClient " + number + " sent: " + messageIn);
				System.out.println("Processing...");
				Random r = new Random();
				Thread.sleep(r.nextInt(3000));
				System.out.println("\nClient " + number + " processed." 
						+ "\nClient sent: " + messageIn);
				
				messageOut = "Client " + number + "has passed test.";
				output.writeObject(messageOut);
				output.flush();
				
				

				shutDown();
				System.out.println("\nNumber Count: " + numLeft);
				System.out.println("Thread number: " + Thread.activeCount());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
		
		public void processStreams() {
			try {
				output = new ObjectOutputStream(connection.getOutputStream());
				output.flush();
				
				input = new ObjectInputStream(connection.getInputStream());
				System.out.println("Input/Output streams initialised...");
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	
		public void shutDown() {
			System.out.println("Client has shut down connection");
			try {
				//runClient.shutdown();
				connection.close();
				input.close();
				output.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}		
		}
	}

So, when I run the server it enters that while loop in the and waits for a connection from a client. When I run a client, the client enters the connection, the server then creates an instance of the inner runnable class, WorkingThread, runs the thread in the thread pool, and assigns the client to that thread.

Have you tried it with more than one thread in the threadpool?

Have you tried it with more than one thread in the threadpool?

Damn, how stupid of me. Totally forgot.

Okay, I increased the number of the thread pool (as an alternative as well I used runClient = Executors.newCachedThreadPool()), and I seem to be able to run to run individual threads so that they each give the clients the ability to send messages to the server, instead of before, when one client would have to wait until the other was done.

However, there is another problem that crept up. When I send messages from one client to the server, the server received it fine and sent back confirmation to that client. When I sent messages from the other client, I got an exception: Connection reset by peer: socket write error.

Note that I've already commented out the shutDown() method which closes the socket connections by this time, because it seems the server does use only one socket despite there being different threads.

I think the problem now is with the input and output stream :(

Sorry, I'll retract that, as I was wrong!

I forgot you had initialised the streams inside the WorkingThread class.

But a Socket called connection is declared twice.

I edited my previous post because I believed I was incorrect...

But I think the problem lies here:

public class SimpleServer2 {

  ServerSocket serverSocket;
  Socket connection;
  
  ObjectOutputStream output;
  ObjectInputStream input;
  
  ExecutorService runClient;

Your declaring the streams outside the inner class, you also declare a Socket called connection in both the outer and inner classes.

You should declare the streams inside the inner class, and only declare the Socket connection in there as well:

public class WorkingThread implements Runnable {
    private Socket connection;
    private ObjectOutputStream output;
    private ObjectInputStream input;
    private int number;

Your problem is where you are declaring the ObjectInputStream and ObjectOutputStream :

public class SimpleServer2 {

  ServerSocket serverSocket;
  // No need to declare this Socket here either.
  Socket connection; 
  ObjectOutputStream output;
  ObjectInputStream input;
  ExecutorService runClient;

You declared the ObjectInputStream and ObjectOutputStream outside of the WorkingThread inner class.

They should be declared in the WorkingThread class:

public class WorkingThread implements Runnable {
    
    private Socket connection;
    private int number;
    private ObjectOutputStream output;
    private ObjectInputStream input;

Now when you close the streams, you will only be closing the ones associated with that specific thread.

Also you've declared a Socket called connection in both the inner and outer class!

I declared a socket in the outer class because it's the outer class that I run to start up the server and accept connections. The accepted connection is then passed into the inner class.

If I take out the socket declaration from the outer class, there will be no way to accept client connections from there, and the while loop which is supposed to be a way for the server to remain open in case any clients want to connect, will just keep creating threads forever, with nothing to stop it or nothing to say, "Oh, hang on, let's wait for a client to come first."

However, declaring the objectinputstream in the inner class did help out to an extent. While i was able to connect with two or more clients before your suggestion, after sending a message with one client and receiving the same message from the server, i couldn't send a message with the other client.

Now I can do that, and for that, dude, I thank you!!!

But there's still one more problem lol sorry, I keep coming up with issue i need to solve.

I added this code in my server:

public void sendToClient() {
try {
messageOut = "Hello, Client " + number + " of " + Thread.currentThread().getName();
output.writeObject(messageOut);
output.flush();
				
System.out.println("\nAwaiting Client reply...");
messageIn = (String)input.readObject();
System.out.println("\nClient " + number + " sent: " + messageIn);
				
System.out.println("Processing...");
Random r = new Random();
Thread.sleep(r.nextInt(10000));
System.out.println("\nClient " + number + " processed.");
				
messageOut = "Client " + number + "has passed test.";
output.writeObject(messageOut);
System.out.println("Closing " + Thread.currentThread().getName() + " for Client" + number);
output.flush();

shutDown();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

Each thread calls this method and message is exchanged between client and server. Works perfectly fine. Remember, the idea of this program is that when i send messages from client 1 to server, server sends back that same message to client 1, thereby proving that it knows that its dealing with client 1. And if client 2 sent a message to the server, the server would reply in kind

However, I put in the random number code to simulate a thread taking time to process stuff, just to see how it affected the message exchange, and I found out that the message sent last was recieved by both clients.

So, basically, if clien 1 sends to server thread 1, and thread 1 is still sleeping, and then client 2 sends a message to thread 2, when thread 1 awakens it will deliver the message meant for client 2 to client 1, and thread 2 will rightfully deliver the message meant for client 2 to client 2.

I've tried putting a synchronized keyword on the method, but it's not working. Now, I'm thinking I should put it somewhere within the method... Or am I headed in the wrong direction?

Sorry for taking so much of your time :)

If I take out the socket declaration from the outer class, there will be no way to accept client connections from there, and the while loop which is supposed to be a way for the server to remain open in case any clients want to connect, will just keep creating threads forever, with nothing to stop it or nothing to say, "Oh, hang on, let's wait for a client to come first."

Yes, thats true, but its the ServerSocket object you use to accept the connection, not the Socket object:

public class SimpleServer2 {

  ServerSocket serverSocket;
  
  // This declaration of a socket is not needed
  Socket connection;
  
  .......

  public SimpleServer2() {
    runClient = Executors.newFixedThreadPool(2);
    try {
      serverSocket = new ServerSocket(8888);
      System.out.println("\nAwaiting connection from clients...");
      while(true) {
        WorkingThread wt;
        // The ServerSocket object is used here to accept connections
        // Not the Socket object
        wt = new WorkingThread(serverSocket.accept(), ++clientNumber);
        runClient.execute(wt);
      }
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

As for the message sending bug, I can't seem to reproduce it! :(

But I think you may be on the right track with synchronization.. Not quite sure though, until I can reproduce the bug!

Instead of synchronizing the whole method maybe if you try synchronizing the ObjectOutputStream output in each block it's used, like this:

public synchronized void sendToClient() {
      try {

        // Synchronize here...
        synchronized( output ) {
          messageOut = "Hello, Client " + number + " of " + Thread.currentThread().getName();
          output.writeObject(messageOut);
          output.flush();
        }  
        System.out.println("\nAwaiting Client reply...");
        messageIn = (String)input.readObject();
        System.out.println("\nClient " + number + " sent: " + messageIn);
                
        System.out.println("Processing...");
        Random r = new Random();
        Thread.sleep(r.nextInt(10000));
        System.out.println("\nClient " + number + " processed.");
                
        messageOut = "Client " + number + "has passed test.";

        // Synchronize here...
        synchronized( output ) {
          output.writeObject(messageOut);
          System.out.println("Closing " + Thread.currentThread().getName() + " for Client" + number);
          output.flush();
        }
        shutDown();
        
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      } 
}

chaospie, yes, yes, that is true, i use the serversocket. Sorry, I've been writing java for hours straight. So eager to learn that I miss crucial things. I'll try the synchronisation suggestion you made

Ok, I decided to use a modified version of your earlier arraylist suggestion, because it seems putting a thread to sleep notifies the other threads and they go ahead and modify the variables "messageIn" and "messageOut".

Everything works fine now.

Thanks, thanks, thanks so much, chaospie!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.