1

I'm relatively new to Java and I'm having problems creating a multi-client/server chat.

I need to implement the server to where it can receive multiple messages from one or more clients and it will send all of those messages to all of the clients. So, say there are two clients and client1 sends the message "hi" to the server. The server will send that message to client1 and client2 (it does a send back to all possible clients which are connected to the server).

Also, how do I associate the client's username with their message? If client1 sent "hi", I'd want the TextAreas in Client1 and Client2 that display the messages to say "client1: hi".

The implementations are done in JavaFX.

Server code:

package server;

import java.io.*;
import java.net.*;
import java.util.*;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextArea;

public class ServerFormController implements Initializable{
    @FXML
    public TextArea svrLog;
    private int clientNo = 0;

    ArrayList<HandleAClient> clients = new ArrayList<>();

   //@Override
  // public void run()
  // {

  // }

  @Override
  public void initialize(URL url, ResourceBundle rb) {
    new Thread( () ->
    {
        try{

            ServerSocket serverSocket = new ServerSocket(8000);
            svrLog.appendText("Server started at " + new Date() + '\n');

            while (true)
            {
                Socket socket = serverSocket.accept();
                svrLog.appendText("Worked \n");

                clientNo++;

                //svrLog.appendText("Starting thread for client " + clientNo + "at"
               // + new Date() + '\n');

               // InetAddress inetAddress = socket.getInetAddress();
                //svrLog.appendText("Client " + clientNo + "'s host name is "
                //+ inetAddress.getHostName() + "\n");
              // svrLog.appendText("Client " + clientNo + "'s IP Address is "
               // + inetAddress.getHostAddress() + "\n");

                new Thread(new HandleAClient(socket)).start();
            }

        } catch (IOException ex) {
            svrLog.appendText("Couldn't create ServerSocket! \n");
        }
    }).start();
   }

   /** Creates individual client socket*/
   class HandleAClient implements Runnable {
      private Socket socket;
      DataInputStream inputFromClient = null;
      DataOutputStream outputToClient = null;
      //ServerFormController svr;

      public HandleAClient(Socket socket)
      {
          this.socket = socket;
      }

      public void run()
      {
          try{
              inputFromClient = new DataInputStream(socket.getInputStream());
              outputToClient = new DataOutputStream(socket.getOutputStream());

              while (true)
              {
                  String msg = inputFromClient.readUTF();
                 // for(int i = 0; i < clients.size(); i++) // probably because object in client contain null datastreams
                 //   clients.get(i).outputToClient.writeUTF(msg); // nope.
                  outputToClient.writeUTF(msg);
                  svrLog.appendText(msg);
                  svrLog.appendText("New message received from client. Sent to all clients.");

              }
          } catch (IOException ex) {
              svrLog.appendText("Could not create data stream with client! \n");
          }
      }
  }
 }

Client code:

package handlerclient;

import java.io.*;
import java.net.*;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;

public class HandlerClientFormController implements Initializable {
    @FXML
    private TextField ipAdrText; // TextField to get the IP Address
    @FXML
    private TextField usernameText; // TextField to get the user's chat name
    @FXML
    private Button clientConnectBtn;  // Tries to connect to server
    @FXML
    private TextArea msgLogTxtArea; // Displays all the messages between the clients
    @FXML
    private TextArea msgSendTxtArea; // TextArea to get the message from the client
    @FXML
    private Button clientSendBtn;  // Sends the message from the client to the server

    DataOutputStream toServer = null;
    DataInputStream fromServer = null;

    /**
     * Initializes the controller class.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
    }    

    @FXML
    private void onConnectBtnClick(ActionEvent event) {
    try{
        Socket socket = new Socket(ipAdrText.getText(), 8000);
        fromServer = new DataInputStream(socket.getInputStream());
        toServer = new DataOutputStream(socket.getOutputStream());
    }
    catch (IOException ex)
    {
        msgLogTxtArea.appendText("Could not connect to server!");
    }
}

    @FXML
    private void onSendBtnClick(ActionEvent event) {
       try{
            toServer.writeUTF(msgSendTxtArea.getText());
            toServer.flush();

            String newmsg = fromServer.readUTF();

            msgLogTxtArea.appendText(newmsg + '\n');
        }
        catch (IOException ex)
        {
            msgLogTxtArea.appendText("Could not send/receive message! \n");
        }
    }

}

1 Answers1

0

For instance it can be done by extending HandleAClient's constructor.
Idea - to save in client handler client's information.
In your ServerFormController at the moment of creating HandleAClient's instance we have information about client's number. We can give it to HandleAClient as additional parameter of constructor, so when client handler receives message from client it will be able to make 'personalized' message with name of concrete client.

RomanTheKat
  • 56
  • 1
  • 5
  • Would you happen to know what is needed in ServerFormController to make it send the messages to all the clients? Like I mentioned in my question, "I need to implement the server to where it can receive multiple messages from one or more clients and it will send all of those messages to all of the clients. So, say there are two clients and client1 sends the message "hi" to the server. The server will send that message to client1 and client2 (it does a send back to all possible clients which are connected to the server)." Also, thanks for the response. It was helpful. – cajuncoder54 Nov 09 '14 at 17:50
  • I think yours original idea was close to do that - about commented code // for(int i = 0; i < clients.size(); i++) but clients collection wasn't ever updated to really be able to perform it's for each handling. If it is possible - we can try to add new client to that collection during HandleAClient instance creation. – RomanTheKat Nov 09 '14 at 18:28
  • Wow. Just realized I never added anything into clients so no wonder that part never worked. Whoops. Thanks for the help! – cajuncoder54 Nov 09 '14 at 18:34
  • p.s. clients collection should be multithread-safe in that concept. java.util.concurrent, or Collections.synchronizedList() like in http://stackoverflow.com/questions/2444005/how-do-i-make-my-arraylist-thread-safe-another-approach-to-problem-in-java can help with that – RomanTheKat Nov 09 '14 at 18:36