0
votes

I'm creating a simple socket server using Java. I am able to connect one client at a time, I tried to implement Threads to handle multiple client. In my Server constructor I created a thread that handles the ServerSocket, and should keep listening for new clients. Once a socket is connected then I tried to create another thread to handle the client Socket. But I am still not able to connect more than one client. The second client I try to connect will not get IO streams.

public class Server extends JFrame {

private JTextField enterField;
private JTextArea displayArea;
private ObjectOutputStream output;
private ObjectInputStream input;

private ServerSocket server;
private Socket connection;
private int counter = 1;

public Server() {
    super("Server");
    enterField = new JTextField();
    enterField.setEditable(false);
    enterField.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent event) {
            sendData(event.getActionCommand());
            enterField.setText("");

        }
    });

    add(enterField, BorderLayout.NORTH);
    displayArea = new JTextArea();
    add(new JScrollPane(displayArea));
    setSize(300, 150);
    setLocation(500, 500);
    setVisible(true);

    new Thread(new Runnable() {
        public void run() {
            try {
                server = new ServerSocket(50499, 100);
                displayMessage("Listening on Port: "
                        + server.getLocalPort() + "\n");
                for (;;) {
                    Socket nextClient = server.accept();
                    displayMessage("Client Connected");
                    new ClientThread(nextClient).start();
                    nextClient = null;
                }
            } catch (IOException exception) {
                exception.printStackTrace();
            }
        }
    }).start();
}

private void closeConnection() {
    displayMessage("\nTerminating connection\n");
    setTextFieldEditable(false);
    try {
        output.close();
        input.close();
        connection.close();
    } catch (IOException ioException) {
        ioException.printStackTrace();
    }
}

private void displayMessage(final String string) {
    SwingUtilities.invokeLater(new Runnable() {

        @Override
        public void run() {
            displayArea.append(string);
        }
    });
}

private void setTextFieldEditable(final boolean editable) {
    SwingUtilities.invokeLater(new Runnable() {

        @Override
        public void run() {
            enterField.setEditable(editable);

        }

    });

}

private void sendData(String message) {
    try {
        output.writeObject("SERVER>>> " + message);
        output.flush();
        displayMessage("\nSERVER>>> " + message);
    } catch (IOException ioException) {
        displayArea.append("\nError Writing Object");
    }
}

private class ClientThread extends Thread {

    public ClientThread(Socket socket) throws IOException {
        try {
            connection = socket;
            output = new ObjectOutputStream(socket.getOutputStream());
            output.flush();
            input = new ObjectInputStream(socket.getInputStream());
            displayMessage("Got I/O Stream\n");
            displayMessage("Connection " + counter + " received from: "
                    + 
                            connection.getInetAddress().getHostName());
            counter++;
            String message = "Connection Sucessful";
            sendData(message);
            setTextFieldEditable(true);
            do {

                message = (String) input.readObject();
                displayMessage("\n" + message);
            } while (!message.endsWith(">>> TERMINATE"));

        } catch (ClassNotFoundException classNotFoundException) {
            displayMessage("\nUnknown object type recieved");

        } finally {
            closeConnection();
        }

    }
  }
}
2

2 Answers

2
votes

You're doing the connection stuff inside the ClientThread constructor. Therefore the new ClientThread(...) never returns until you send a TERMINATEcommand. Put the logic inside the run() method.

private class ClientThread extends Thread {
  private Socket socket;

  // The queue, thread-safe for good measure
  private Queue<String> queue = new ConcurrentLinkedQueue<String>();

  public ClientThread(Socket socket) throws IOException {
    this.socket = socket;
  }

  public void send(String message) {
    if (message != null) {
      this.sendQueue.add(message);
    }
  }

  public void run() {
    try {
      connection = socket;
      output = new ObjectOutputStream(socket.getOutputStream());
      output.flush();
      input = new ObjectInputStream(socket.getInputStream());
      displayMessage("Got I/O Stream\n");
      displayMessage("Connection " + counter + " received from: " 
          + connection.getInetAddress().getHostName());
      counter++;
      String message = "Connection Sucessful";
      sendData(message);
      setTextFieldEditable(true);
      do {
        // Purge the queue and send all messages.
        while ((String msg = queue.poll()) != null) {
          sendData(msg);
        }
        message = (String) input.readObject();
        displayMessage("\n" + message);
      } while (!message.endsWith(">>> TERMINATE"));
    } catch (ClassNotFoundException classNotFoundException) {
      displayMessage("\nUnknown object type recieved");
    } finally {
      closeConnection();
    }
  }
}

Tipically, you'll send messages to the connection from other threads:

ClientThread client = new ClientThread(newClient);
client.start();
client.send("Hi there");

Personally, I would have used a non-blocking (NIO) networking library such as Netty or Mina to implement this sort of stuff. There's some learning to do to use them but I think its worth it. Non-blocking means that you don't dedicate a separate thread for each connection but rather you're notified when something is received in the socket.

0
votes

You did not provide implementation of run method for Thread Class.

Put processing logic of ClientThread inside run method

To keep your code modularised and more manageable make your ClientThread class public and share only necessary resources between those thread.

public class ClientThread extends Thread {  //make class public
  private Socket socket;
// define all other references 
ObjectOutputStream output = null;
//...

  public ClientThread(Socket socket) throws IOException {
    this.socket = socket;
  }

  public void run() {
    try {
      connection = socket;
        output = new ObjectOutputStream(socket.getOutputStream());
        output.flush();
        input = new ObjectInputStream(socket.getInputStream());
        displayMessage("Got I/O Stream\n");
        displayMessage("Connection " + counter + " received from: "
                + 
                        connection.getInetAddress().getHostName());
        counter++;
        String message = "Connection Sucessful";
        sendData(message);
        setTextFieldEditable(true);
        do {

            message = (String) input.readObject();
            displayMessage("\n" + message);
        } while (!message.endsWith(">>> TERMINATE"));

    } catch (ClassNotFoundException classNotFoundException) {
        displayMessage("\nUnknown object type recieved");

    } finally {
        closeConnection();
    }

}
}}