Member Avatar for Search_not

I'm making an instant messenger program and I want to allow clients to connect to each other privately(peer-to-peer) using their IP addresses. The only method I know at the moment is a client/server method where the server needs to be online for the client to work. How do I allow for the client to be connected to another client?
e.g.

public class Messenger extends JFrame{

    private Socket connection;
    private ObjectOutputStream output;
    private ObjectInputStream input;
    private String ipAddress = "127.0.0.1";   //would be replaced with the other users ip address

    public Messenger(){
        //GUI is set up here
        //event handler for the JTextField in which the user type their own messages
        //event handler will send the message on the output stream
        //JTextField is setEditable(false) until the connection is made and streams are setup
    }

    public void startRunning(){
        //setup socket
        connection = new Socket(InetAddress.getByName(IpAddress), 6789);  //a port would be opened by all other clients to allow for the connection

        //setup streams
        try{
            output = new ObjectOutputStream(connection.getOutputStream());
            output.flush();
            input = new ObjectInputStream(connection.getInputStream());

        }catch(IOException ioe){

            ioe.printStackTrace();

        }finally{

            whileChatting(); //method that recieves messages and prints them out on the screen
        }
    }

    private void whileChatting(){
        String message = "";

        do{
            try{

                message = (String) input.readObject();
                //print message on screen

            }catch(ClassNotFoundException cnfe){
                cnfe.printStackTrace();
            }
        }while(!message.equals("ENDChat"));  
    }

    public static void main(String args[]){
        Messenger im = new Messenger();  //call constructor
        im.startRunning();  //set up connection
    }

}

The target "client" has to be listening on ServerSocket for the other client to connect, but that's the same as the code in our existing server.
The problem is usually to find the target client's IP. Having a registered domain name is one way, or a dynamic DNS solution like noip.com
Otherwize you can run a small server that clients register with, and can get each other's IP from

Member Avatar for Search_not

So if I wanted to make a program similar to Skype (obviously with less features and cababilities and no calling), then I would have to have all the clients listening on a server socket? I'll be testing the program with a few close friends at first, so getting the IP addresses will be pretty easy.

If you don't want to use a server then yes.
Somebody has to be listening for any connection to be made!

Member Avatar for Search_not

Does ServerSocket allow for more than one connection? I'm still thinking in terms of WhatsApp/Skype where you can send messages to more than one person in your contact list. Can I connect simultaneosly to different the computers on my contact list using ServerSocket(assuming I already know their IP addresses)? I am aware that this will require a server to supply the IP Addresses.

The ServerSocket listens on a given port/IP address. When it receives a connection request it creates a new Socket that you can use to communicate to/from the client. This is done by creating one or two new threads to handle inbound/oubound mesages from/to that client.
The ServerSocket then continues listening for new connections.
So one ServerSocket can create any number of Socket connections (depending on any limits in the operating system itself).

So to send messages to multiple recipients you need to create a Socket connection with each of them. Then you can send the same message to any or all of them.

Member Avatar for Search_not

So I would create run a loop in the backround listening for any new connections? Because the startRunning() method stops listening for new connections once a connection has been established, and if so then how do I implement it? Would I use a Thread object and a new Runnable with a loop that continues waiting for new connections even after an initial connection has been established?

You have an initial Thread that is a simple loop waiting for new connections to the ServerSocket.
When it gets one it opens a Socket and hands that over to a new Thread that handles the ongoing communications with that Socket. The original thread then just goes back to waiting for the next connection.
Ignoring error handling and shutting down, it's kinda like this (pseudo code):

main(...) {
   ...
   new Thread( new ConnectionListener()).start();
   ...
}

class ConnectionListener implements Runnable {
   run() {
       while (true) {
          wait for client to connect to ServerSocket
          open new Socket with client
          new Thread(new ClientHandler(socket)).start();
       }
   }
}

class ClientHandler implements Runnable {
   Socket clientSocket;
   ClientHandler(Socket clientSocket) {
      this.clientSocket = clientSocket;
   }
   run() {
      ... do whatever with the client connection
   }
} 
Member Avatar for Search_not

When I open a new Socket do I need to have a different Socket objects declared? e.g.

Socket first,
       second,
       third;

ServerSocket server;

Or do I get the connecting peers IP address using InetAddress.getHostName and then store it in a String peerIP = InetAddress.getHostName;, and call it like this:
first = new Socket(InetAddress.getByName(peerIP), 82);??

Thanks for the help so far though!

No, no need for any of that at all... just

ServerSocket serverSocket = new ServerSocket(...
Socket clientSocket;
while (true) {
   clientSocket = serverSocket.accept();
   new Thread(new ClientHandler(clientSocket)).start();
}

then in the client handler (see previous post) open the input and output streams from the Socket and start communicating.

Member Avatar for Search_not

And would this function close all the streams at once, even if there are 20 of them?

private void closeCrap(){
    try{
        input.close();  //ObjectInputStream

        output.close(); //ObjectOutputStream

        connection.close();  //socket

    }catch(IOException ioe){
        ioe.printStackTrace();
    }
}
Member Avatar for Search_not

I'm having some weird problems... When I run my program (only one instance of it) it sets up the ServerSocket and connects to itself. When I try to send a message I get the Null Pointer exception. When I run two instances of the program then I get the Address already in use: JVM_Bind error on the second instance of the program.
Here's an example of the code:

    ServerSocket server;
    Thread watch;
    ObjectOutputStream output;
    ObjectInputStream input;
    Socket connection;

    final public void startRunning(){   //Main part of the program
        try{
            while(true){
                try{
                    if(!connectToPeer()){   //boolean function that first tries to connect to another peer
                        server = new ServerSocket(6789, 5);   //setup ServerSocket
                        watch = new Thread(new ConnectionListener());   //initiate thread
                        watch.start();   //start thread
                    }else{
                        System.out.println("Now connected to: " + connection.getInetAddress().toString());
                    }
                    whileChatting();   //function that recieves messages via input stream
                }catch(EOFException eof){
                    eof.printStackTrace();
                }
            }
        }catch(IOException ook){
            note.showNote("Error: " + ook.getMessage());
        }
    }    

    private class ConnectionListener implements Runnable { 

        Thread client;

        @Override
        public void run(){
            while(true){
                try{
                    waitForConnection();   //method further down
                    client = new Thread(new ClientHandler(connection));
                }catch(IOException lol){

                }
            }   
        }

        private void waitForConnection() throws IOException{
            note.showNote("Waiting for connection...");
            connection = server.accept();
        }

    }

    private class ClientHandler implements Runnable {

        Socket clientSocket;

        public ClientHandler(Socket socket){
            this.clientSocket = socket;
        }

        @Override
        public void run(){
            try{
                setupStreams(clientSocket);
            }catch(IOException ek){

            }
        }

        private void setupStreams(Socket socket) throws IOException{

            note.showNote("Setting up streams...");

            try{
                output = new ObjectOutputStream(socket.getOutputStream());
                output.flush();
                input = new ObjectInputStream(socket.getInputStream());
                note.showNote("Streams are now set-up!");
            }catch(IOException ioe){
                note.showNote("Error: Couldn't set-up streams...");
            }
        }
    }

Are you trying to do the server and the client in the same program? That's going to cause problems. Don't you want multiple clients and one server?

Of course, if you try to start a second instance of the server on the same machine it will find the listening port already in use (by the first instance).

You also have an empty catch block, so I not going to waste any time looking at the code when there could be a perfectly informative Exception being ignored. Nor will I waste time looking for an NPE when you chose not to say which line it's on!

Member Avatar for Search_not

I'm trying to connect two clients together in a p2p configuration without the use of a dedicated server. Almost like Skype.
NPE was found on line: 18 with the whileChatting() method

private void whileChatting() throws IOException{
        String message;


        do{
            try{
                message = (String) input.readObject();

                if(message.equals("END_CHAT")){    //end-of-chat message
                    break;
                }

                //method that prints out message in JTextArea
                showMessage(message);  

            }catch(ClassNotFoundException cln){
                note.showNote("Error: " + cln.getMessage());
                cln.printStackTrace();
            }
        }while(true);
    }

Another NPE was found at the sendMessage() method here:

private void sendMessage(String message){
        try{
            output.writeObject(userName + " - " + message);   //NPE error found here
            output.flush();
            showOwnMessage(message);//shows message on JTextArea
        }catch(IOException ioe){
            note.showNote("Message not sent!");
            ioe.printStackTrace();
        }
    }

In that case one needs to be the client and the other the server. You can't do anything with just 2 clients or just 2 servers. If you want to have both functions in every instance then either have one machine per instance, or fake it temporarily by using different listen sockets for eacg server.

Line 18 is cln.printStackTrace(); - no way that throws an NPE!
Second NPE - the only variable that will throw an NPE on that line is output, so you need to trace that back to find out why. (But first fix the empty catch block)

Member Avatar for Search_not

Line 18 of the previous post(the 87 line long one), sorry.

I tried to make each instance of the program act as a peer when it first starts up (line 10 of the post from 1 hour ago) and if no server program is found then it switches to the ServerSocket and starts listening for any connections. In that way I was hoping to run one instance and wait for it to switch to a Server and then run another instance of the program that will first search for another server program, and when it finds it then it connects and sets up streams in order to start sending messages

Don't have a 87 line listing. There's an 81 line listing, but line 18 of that is whileChatting(); so if that's it, it looks like the same as the other NPE?

OK, understand your architectural design now. That should work, although handling errors and shutdown/resart coluld be interesting.
If you end up with two ServerSockets at the same time (JVMBind error) it implies the second instance's attempt to connect as a client has failed.

If this were my program I would set that aside and construct the smallest possible program to test that try-to-connect-and-open-serversocket-if-that-fails design - there's too much going on in your code to see what's happening clearly.

ps: From bitter experience of testing Server implementations I know that its easy to end up with an instance of some failed test version still running and holding on to the port, even if it will no longer handle it properly. If you get weird problems, log out and log on again to terminate any "lost" live instances.

Member Avatar for Search_not

Yeah, its the whileChatting(); method that gives an NPE, so its the same problem.
But thanks for the advice! I'll try that small test program suggestion now and move on from there. I'll try logging on and off if my program starts up with a JVMBind error. But wouldn't closing the application in task manager have have the same effect? Or is it something just closing the application won't fix?

Closing it in task manager will do the trick - and just by looking in TM you'll see if there are any ghost instances.

How will a new instance know the ip address(es) of existing servers to try to connect to them?

Member Avatar for Search_not

I was thinking of reading from a text file that any users of the program will store in their C drive "C:\addresses.txt", and then reading IP addresses from that textfile and attempting connections with the servers there. I'll keep the ServerSocket port the same in all instances

OK, I can see that would work, although you will presumably need some way of updating that file. Remember that if you have two servers in the same LAN behind a NAT router and you want to access them from the WAN then they will have to be mapped to different ports on the router's IP, so you can't always have the same port for all servers.

Member Avatar for Search_not

I'll keep that in mind. Is there a way to catch the JVMBind error so that I can change the port if necessary?

Member Avatar for Search_not

I've finished the test program. Please tell me if there's something wrong with the code. I used a JLabel to show the output...

package testapp;

import java.net.*;
import java.io.*;
import javax.swing.*;

public class TestApp extends JFrame{

    private ServerSocket server;
    private Socket connection;
    private ObjectOutputStream out;
    private ObjectInputStream in;
    private JLabel lbl;

    protected TestApp(){
        super("Test Program");
        setSize(300, 150);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        lbl = new JLabel();
        add(lbl);
        setVisible(true);
        try{
            if(!connectToPeer()){
                display("Couldn't connect to peer...");
                server = new ServerSocket(6789, 5);
                connection = server.accept();
                display("Connected to client at: " + connection.getInetAddress().getHostAddress());
            }else{
                display("Connected to peer: " + connection.getInetAddress().getHostAddress());
            }
        }catch(IOException ioe){
            ioe.printStackTrace();
            display(ioe.getMessage());
        }
    }

    private boolean connectToPeer(){
        try{
            connection = new Socket(InetAddress.getByName("127.0.0.1"), 6789);
            return true;
        }catch(IOException ik){
            ik.printStackTrace();
            return false;
        }
    }

    private void display(String message){
        lbl.setText(message);
    }

    public static void main(String[] args) {
        TestApp app = new TestApp();
    }

}

I don't like the way that connection is updated in different methods - makes it hard to follow its value (especially if connectToPeer is sucessful).
Personally I would prefer to return a Socket from connectToPeer, or null if the connect fails. Then the mainline code would be much clearer, eg

Socket connection = connectToPeer();
if(connection != null){
   display("Connected to server peer: " + connection.getInetAddress().getHostAddress());
} else {   
    display("Couldn't connect to peer...");
    server = new ServerSocket(6789, 5);
    connection = server.accept();
    display("Connected to client peer at: " + connection.getInetAddress().getHostAddress());
}
Member Avatar for Search_not

Thanks! It works and its much easier to read than what I was doing perviously...
About my previous question though, is there a way to catch the JVMBind Error so that I can change the port if necessary?

Yes, just the usual try/catch structure. Create the ServerSocket inside the try, and catch any BindException

ps: I'm about to disappear on holiday for a week, so don't be surprised if I suddenly stop replying
J

Member Avatar for Search_not

Okay, thanks James!

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.