0

EDIT: I have corrected the mistake below in the code, by adding a line into the server code

I'm trying to write some socket code that will allow me to send data from one computer to another for a game (which for simplicity's sake, we can think of as tic-tac-toe, not much data needs to be sent, just a couple of numbers). In order to achieve this I have written two classes, Server and Client. At the moment I am testing through the localhost using port 1234, and I am only using one single instance of the program (though the same problem occurs when trying to use two instances).

Firstly here's the code, and then I can go into more depth about the problem, and what testing I've done to attempt to work out what is going wrong:

public class Server
{
    private ServerSocket server;
    private Socket socket;

    private Client socketHandler;

    private static final int DEFAULT_PORT = 1234;

    public Server() { this(DEFAULT_PORT); }
    public Server(int port)
    {
        Thread thread = new Thread()
        {
            public void run()
            {
                try
                {
                    System.out.println("Attempting to Establish Connection");
                    server = new ServerSocket(port);
                    socket = server.accept();
                    socketHandler = new Client(port, socket); //THIS LINE ADDED
                    System.out.println("Server Online!");
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        };

        thread.setDaemon(true);
        thread.start();
    }

    //ADJUSTED
    Client getSocketHandler()
    {
        return socketHandler;
    }

    public void kill()
    {
        try
        {
            if (socket != null) socket.close();
            if (server != null) server.close();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            socket = null;
            server = null;
        }
    }
}

public class Client
{
    public static final int DEFAULT_PORT = 1234;
    public static final String DEFAULT_HOST = "localhost";
    private static final String THUMP_THUMP = "thump thump";
    private static final int PULSE = 1000;

    private int port;
    private String ip;

    private Socket socket;
    private BufferedReader input = null;
    private PrintWriter output = null;

    boolean closed = true;

    String data = "";

    public Client() { this(DEFAULT_PORT, DEFAULT_HOST, null); }
    public Client(int port) { this(port, DEFAULT_HOST, null); }
    public Client(int port, String ip) { this(port, ip, null); }
    public Client(int port, Socket server) { this(port, DEFAULT_HOST, server); }
    public Client(String ip) { this(DEFAULT_PORT, ip, null); }
    public Client(String ip, Socket server) { this(DEFAULT_PORT, ip, server); }
    public Client(Socket server) { this(DEFAULT_PORT, DEFAULT_HOST, server); }
    public Client(int port, String ip, Socket server)
    {
        socket = server;
        this.ip = ip;
        this.port = port;

        Thread thread = new Thread()
        {
            public void run()
            {                
                try
                {
                    initialise(server);
                    String line;
                    startHeartbeat();
                    while (isClosed()) {} //first it is closed, lets wait for it to open before we start waiting for it to close!
                    System.out.println("We are about to listen!");
                    while (!isClosed())
                    {
                        System.out.println("pre-read"); //this line was used to determine that the code was hanging on the next line
                        line = input.readLine(); //offending line
                        System.out.println("post-read"); //this line was used to determine when the block was lifted
                        if (line != null)// || line != THUMP_THUMP)
                        {
                            System.out.println(line);
                            data += line + "\n";
                        }
                    }
                    System.out.println(data);
                    kill();
                    System.out.println("Connection Closed!");
                }
                catch (SocketException e)
                {
                    e.printStackTrace();
                    System.out.println("Server closed!");
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        };

        thread.setDaemon(true);
        thread.start();
    }

    private void initialise(Socket server)
    {
        try
        {
            if (server == null) socket = new Socket(ip, port);
            input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            output = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
        }
        catch (IOException e) { e.printStackTrace(); }
    }

    public boolean post(String text)
    {
        synchronized(this)
        {
            output.println(text);
            output.flush();
            return !output.checkError();
        }
    }

    public void kill()
    {
        try
        {
            if (input != null) input.close();
            if (socket != null) socket.close();
        }
        catch(IOException e) { e.printStackTrace(); }
        finally
        {
            input = null;
            socket = null;
        }
    }

    public void killOutputStream()
    {
        try
        {
            if (output != null) output.close();
        }
        catch (Exception e) { e.printStackTrace(); }
        finally
        {
            output = null;
        }
    }

    //////////////////////////////////
    ///////// Socket Control /////////
    //////////////////////////////////

    synchronized boolean isClosed()
    {
        return closed;
    }

    synchronized void setClosed(boolean b)
    {
        closed = b;
    }

    //We need to make sure that the socket is still online, to ensure the reading stops when the connection closes.
    void startHeartbeat()
    {
        Thread heartbeat = new Thread()
        {
            public void run()
            {
                while (output != null)
                {
                    setClosed(post(THUMP_THUMP) ? false : true); //post returns true on success
                    synchronized(this)
                    {
                        try
                        {
                            this.wait(PULSE);
                        }
                        catch (InterruptedException e) {}
                    }
                }
                setClosed(true);
            }
        };
        heartbeat.setDaemon(true);
        heartbeat.start();
    }
}

The Problem

When the client is started (after having created the server) it fails to read any data sent through (or even the heartbeat), in fact the code does not go past line = input.readLine() in the reading thread (which is from now on called the offending line), except it seems, until the server is disconnected (see below).

Here is the order of regular testing:

Server() is called and the resulting Server is stored in the serverConnection variable then Client(serverConnection != null ? serverConnection.getSocket() : null) is called and the new Client is stored in clientConnection.

Because we can test whether it is working using the heartbeat no other data needs to be sent, and the server is terminated by calling serverConnection.kill() and then clientConnection.killOutputStream() after letting some time elapse.

and this is the result:

Attempting to Establish Connection Server Online! 
We are about to listen!

Connection Closed!

where the empty line represents the non null data received over the course of the connection, ie that there is none.

I expect this:

Attempting to Establish Connection
Server Online!
We are about to listen!
thump thump
thump thump
thump thump (and so on, every second)
Connection closed!

I spent time performing different tests by commenting out or changing the code slightly with the same testing format (except for the special case, which is number 6) and made these observations:

Observations

  1. Only when the socket is closed and the output stream is closed, does the program move past the offending line.
  2. When the readline() method starts to process (shortly before the heartbeat cuts it off) it detects nothing in the stream, not even THUMP_THUMP.
  3. When the socket is closed, but the output stream is not, the readline() method starts to process, only to detect nothing, heartbeat cuts it off. No SocketException even though it would be expected.
  4. If the socket is NOT closed, and only the output stream is closed, a SocketException is triggered, suggesting the socket is closed.
  5. I used netstat -an in command prompt, and when the server is started the port 1234 is LISTENING. When the client connects, it is still LISTENING, implying that there is no connection.
  6. I set up some python code to connect to itself over port 1234, however I made a mistake in the python code, and as such the server didn't close, and was still open. So I decided to connect the java client to the server and see what happens. I did this by running Client(null) which is the client code for the non-host. It resulted in the port reading ESTABLISHED, and the python server was echoing back the "thump thump", and the java code was successfully reading it. No hanging, it worked perfectly.

This leads me to believe that the problem lies in the server code, as the python server was able to communicate sucessfully with the Java client, but the Java client is unable to communicate with the Java server.

Before performing this testing I had been concentrating on the Client code, believing that it was at fault. All the questions I have found here with similar symptoms (see here, here and here, among others) have turned up blank for me, having written in their solutions (most were due to the output stream not flushing, or the \n ommitted, which I have not failed to do, or the solution not fixing my problem, and so having been removed in favor of the heartbeat in this case). I originally based my code off of this article.

After 4 days of trying to figure out this problem I am at a loss for what to do... What am I missing here? Why is the Server code not working as I expect it to? If anybody needs any more clarification on my code then please ask!

As an after-note, the testing code is run through a simple minimalistic GUI written in javafx (not fxml though), whether that would be a problem or not I'm sure, I would think not, due to it working with the Python server. This code is compiled in Java 8

Community
  • 1
  • 1
J_mie6
  • 720
  • 1
  • 9
  • 25
  • sorry but your code looks confusing, first of all you aren't sending any data from the server to the client, and also the server isn't reading any data from the client.. I suggest you to follow this basic example http://docs.oracle.com/javase/tutorial/networking/sockets/clientServer.html, you also dont need a thread to start the socket in the server side, you just need to create a new thread to handle accepted connections – fmodos Jun 30 '14 at 20:07
  • The server *does* send out data, the host program's server owns it's own client, by passing in the `Socket` you get from `ServerSocket.accept()` to the client... The other client program will not have a server, and so will make a new `Socket` for the ip and port. – J_mie6 Jun 30 '14 at 20:11
  • No. Accepting a connection does not constitute sending data. The server you have posted here does not send out any data at all. If this isn't the real code it was pointless to post it here. Post the real code. – user207421 Jun 30 '14 at 20:26
  • It is the real code. The bit on the testing procedure explained that the hosts client is uniquely set up with this code: `Client(serverConnection != null ? serverConnection.getSocket() : null)` where getSocket() returns the socket returned from accept(), as shown above. But I Do see what you mean, paired with the below answer, only one side of the pair (in this case the server) is sending and receiving data... there is no Client in the testing. – J_mie6 Jun 30 '14 at 20:30
  • In this case the *client* is receiving data. The server is doing nothing except accepting a connection. So the client blocks. Your claim that there is no client is also nonsense. You don't seem to understand your own code. – user207421 Jun 30 '14 at 20:34
  • In testing, the servers socket (that you would typically use to open the server's streams AFAIK) is passed into the Client class, which effectively becomes the servers streams by adopting its socket. If the program is run without a server object having been created, the Client class produces a new Socket instead, with the ip and port, instead of the socket from the server's accept. In the above case the code is running as just a server on it's own, as the "client" is performing the streams for the server. The client that should be on the other end doesn't exist. Client is a bad name really. – J_mie6 Jun 30 '14 at 20:38
  • A Java TCP client is a program that calls 'new Socket()'. If nobody calls 'new Socket()', the 'accept()' method will never return and you can't get to the readLine() call at all, let alone discover that it is blocking. Ergo there is a client. You are not making sense. – user207421 Jun 30 '14 at 20:43
  • Oh... I see now, the code I wrote is meant to do what I specified but you are right, in the end the `serverConnection.getSocket()` always returns null, as there is never a connection made, I completely understand now, sorry! – J_mie6 Jun 30 '14 at 20:48
  • @EJP Yes, as expected, if I set up the server, then connect to it via another instance of the program, THEN set up the client on the server side program, the code works as I previously described, with the server passing it's socket to the client, and everything is fine... it just needed it's own connection first. – J_mie6 Jun 30 '14 at 20:59
  • The client needed the listening socket to already exist so it could connect to it. Surely this is obvious? – user207421 Aug 11 '14 at 00:25
  • @EJP Apparently not haha – J_mie6 Aug 11 '14 at 09:55

1 Answers1

2

I'm a little confused about why you think it would go any furthur than input.readLine() considering there is no handling of inputs/outputs on the server side....

Client/Server connections are like a game of tennis, as one side serves the other must receive the ball and then serve it back(maybe with different information). Your server side must handle the input it recieves from the start heartbeat method, and then send you back a response. the input.readLine() function blocks the thread until it receives data from the other end, so yes the code stops there and waits for your server to send the "tennis ball" back. In the server class you should add an input and output stream that handle the heart beat inputs and send back a string of data to the client.

Server:

OutputStream os = socket.getOutputStream();
InputStream is = socket.getInputStream();
String response = "thump thump";

while(true){
    is.read();
    os.write(response.getBytes());
    os.flush();
}

with this example, the client should remain unchanged and just add the above code to your server.

TheJavaCoder16
  • 591
  • 4
  • 14
  • But why doesn't the Client pick up the heartbeat that it sent out? Is it only one way? – J_mie6 Jun 30 '14 at 20:15
  • @J_mie6 the Client sends the heartbeat to the server, it doesnt send it to itself.... the server is supposed to read the heartbeat, and provide some type of response via its outputstream. – TheJavaCoder16 Jun 30 '14 at 20:18
  • right, I see... However... When the program sets up the Client class on the Host program, it passes the socket obtained from ServerSocket.accept() instead of making it's own, so the Client is basically the server's Socket. Does this make no difference? – J_mie6 Jun 30 '14 at 20:21
  • In any case I will try out your solution tomorrow morning, when I have time to properly concentrate on implementing it. It's been a long 4 days with this haha – J_mie6 Jun 30 '14 at 20:25
  • Yeah, in the comments above I see what I did wrong, I have added the code that I thought would have worked into the Server code, as I originally intended it to work. So when the server connects to a client, it passes its socket to a Client object, which handles the input and output for it! I was just setting it up in the wrong order! – J_mie6 Jun 30 '14 at 21:08