Question

I am trying to create a messenger program and have successfully set up client-server connections using sockets. However I am finding it difficult to code the process of having several clients communicating simultaneously. Shown in the code below is the methods for the chats that are held within a ClientThread class that regulates the interaction between client and server using threads stored in a shared ArrayList. How would you implement the code for multiple peer-to-peer chats here?

startChat method:

public void startChat()
        {
            // start the convo!

            // first of all the user chooses who to speak to
            // starts a loop until user enters a valid username or 'Group'
            String line = "";
            boolean validCommand = false;

            while(validCommand == false)
            {
                try {
                    line = in.readLine();
                } catch (IOException e) {
                    System.out.println("Problem reading reply about user chat");
                }

                if(line.equalsIgnoreCase("Group"))
                {
                    validCommand = true;
                    chatAll(); // an integer of negative one starts a chat with everyone
                }
                else
                {
                    synchronized(this){
                    // find user
                        for(int i = 0; i < threads.size(); i++)
                        {
                            if(threads.get(i) != null && threads.get(i).username != null)
                            {
                                if(threads.get(i).username.equals(line)) // means that we have found the index of the thread that the client wants to speak to
                                {
                                    /*// START : BETWEEN THESE CAPITALISED COMMENTS IS MY ATTEMPT TO INITIATE TWO WAY CHAT
                                    int thisIndex = -1;
                                    for(int j = 0; j < threads.size(); j++) // gets the index of this thread object in the array
                                    {
                                    if(threads.get(j) == this)
                                    {
                                    thisIndex = j;
                                    // out.println(j);
                                    }
                                    }
                                    if(thisIndex != -1)
                                    {
                                    threads.get(i).out.println(username + " is trying to connect");
                                    threads.get(i).processChat(thisIndex); // this is the line causing the problem!
                                    }
                                    // END : BETWEEN THESE CAPITALISED COMMENTS IS MY ATTEMPT TO INITIATE TWO WAY CHAT */

                                threads.get(i).out.println(username + " is trying to connect");
                                out.println("Chat with " + threads.get(i).username);
                                processChat(i);


                                validCommand = true;
                                }
                                // if the command is not group and not a username, it is not valid and we ask the user to re-enter
                                else if(i == threads.size() - 1)
                                {
                                    out.println("This command is not valid, please re-enter");
                                }
                            }
                        }

                    } // end of synchronised bit
                } // end of else statement
            } // end of while loop
        } 

allChat method:

void chatAll()
//for the purpose of group chat
        {
            out.println("Group chat initiated");
            boolean d = true;
            while(d == true)
            {
                String message = "";
                try {
                    message = in.readLine();
                } catch (IOException e) {
                    System.out.println("Can't read line from client");
                }
                if(message.contains("goodbye") == true)
                {
                    d = false;
                }
                else
                {
                    synchronized(this)
                    {
                        for(int j = 0; j < threads.size(); j++)
                        {
                            if(threads.get(j) != null)
                            {
                                threads.get(j).out.println(username + ": " + message);
                            }
                        }
                    }
                    }
            }

        }

processChat method:

void processChat(int i)
//for the purpose of talking to pre-defined user
        {

                boolean d = true;
                while(d == true)
                {
                    String message = "";

                    try {
                        message = in.readLine();
                    } catch (IOException e) {
                        System.out.println("Can't read message from client");
                    }

                    if(message.contains("goodbye") == true)
                    {
                        d = false;
                    }

                    else {
                        if(threads.get(i) != null)
                        {
                            threads.get(i).out.println(username + ": " + message);
                        }
                    }   
                }

        }

Just for good measure and a reference here is the overall client class (confusingly labelled ThreadedClient as opposed to ClientThread haha)

ThreadedClient class:

import java.net.*;
import java.io.*;

public class ThreadedClient implements Runnable {

// client socket
private static Socket clientSocket = null;

//I/O streams to and from the server
private static BufferedReader in = null;
private static PrintStream out = null;

// Input stream to read user input
private static BufferedReader inputReader = null;
private boolean open = true;

public ThreadedClient(String host, int port)
{
    startConnection(host, port);
}

public void startConnection(String host, int port)
{
    //open up the socket
    try {
        clientSocket = new Socket(host, port);
    } catch (UnknownHostException e) {
        System.out.println("The host name '" + host + "' isn't known");
    } catch (IOException e) {
        // TODO Auto-generated catch block
        System.out.println("Cannot create socket");
    }

    // connect I/O streams
    try {
        in = new BufferedReader(new InputStreamReader(new DataInputStream(clientSocket.getInputStream())));
        out = new PrintStream(clientSocket.getOutputStream());
        inputReader = new BufferedReader(new InputStreamReader(System.in));
    } catch (IOException e) {
        System.out.println("Problem connecting streams");
    }

    // process the chat itself

    // the thread deals with input coming in
    Thread thread = new Thread(this);
    thread.start();

    // the loop deals with output
    while(open == true)
    {
            String message;
            try {
                message = inputReader.readLine();
                out.println(message);
                if(message.contains("goodbye") == true)
                {
                    open = false;
                }
            } catch (IOException e) {
                System.out.println("Problem sending messages");
            }

    }



    // chat is done, so we can close resources
    try {
        in.close();
        inputReader.close();
        out.close();
        clientSocket.close();
    } catch (IOException e) {
        System.out.println("Problem closing resources");
    }


}

// run method for sending input out.  I imagine this will not be necessary in the GUI implemented version, as we can use 
// an action listener for the send function, e.g. one that reads a text field into a output stream everytime the user clicks enter
public void run() {

    while(open == true)
    {
        try {
        String response = in.readLine();
        if(response.contains("goodbye") == true)
        {
            open = false;
        }
        System.out.println(response);
    } catch (IOException e) {
        System.out.println("Problem recieving messages");
    }

    }


}


public static void main(String[] args)
{
    ThreadedClient socket = new ThreadedClient("localhost", 50000);
}

}

I know that this code may not be as advanced as some others I have seen on this forum as well as DreamInCode and others but I was trying to build it from scratch and have been stuck here for what feels like a millennia. Trawling through the internet has not helped :(

Any suggestions and criticisms would be an absolute God send!

Thanks in advance guys.

Was it helpful?

Solution

OK. You can do like this: Im focus on Console Application
- Define a class call Message:

class Message
{
   public String username; // the sender that send this message to u.So you can reply back to this user
   public boolean groupMessage; // this message is group message or not
   public String message;
}
  • Define a global variable: ArrayList messages; to hold all incomming messages.
  • So when you start chat with a client --> create new Thread to read message from him.When you receive a message . You have to put that message to the array list: messages ( you have to remember to sync it. because it will be invoked by many thread)

    synchorized(messages){ messages.add(....); // new message here }

  • Then , you create a new Thread to show message & can reply back to the sender. In this read you will pop a message from array list messages & show it.

    while(isrunning) {

    synchorized(messages){
       if(messages.size()<=0) messages.wait(); // when you receive a new message you have to notify
    }
    synchorized(messages){
       Message msg = messages.get(0);
       messages.remove(0);
       showmessage_to_ouput(msg); // something like this.
    
       String s = read from input // to reply to this message.
       Reply(....)// here you can check if this message is group message--> reply to all,..etc
    }
    

    }

P/S: That's a idea :) good luck

OTHER TIPS

I can give you a solution , but you have to implement it
We have:
- Server A, Client B & C. B & C already connected to Server via TCP connection
- The first, client B want to chat with C. So B have to send a message by UDP to server
- 2nd, Server will receive a UDP messages from B ==> Server know which ip & port of B that B connected to Server by UDP. Then server send to C a message (TCP) that contains info about UDP ip:port of B .
- 3rd: Client C will receive that message from server via TCP . So C know ip:port that B is listenning .--> If C accept chat with B . C have to send a UDP message to Server to tell server that C accept to talk with B.
- 4th: Server will receive that message via UDP . So Server also know ip:port of C in UDP.
- 5th : The server will transfer UDP ip:port of C to B via TCP (or UDP if you want).
- 6th: Client B will receive it & know udp ip:port of C. So they can start to chat via UDP protocol now.

IT is call UDP/TCP Hole punching. You can research more about it to implement.

P/S: But this method doesnt work with Symetric NAT

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top