So I'm building this pretty simple top trumps card game as part of an assignment.
I have the game logic built, the swing GUI is in place and I plan to do all the calculation on the server app and have it update the client up with the game's state, round, turn to play and decks.

Obviously, I need a multitude of threads.
So I've built this method which I call by pressing a button in MainGUIServer.java:

public void startRunning() { // Initiates the server.
        try {
            while (true) {
                try {
                    // Connecting and communicating.
                    ServerThread st = new ServerThread(this.packet, this.jta, this.port, this.out, this.in, this.server, this.connection);
                    Thread newThread = new Thread(st);
                    newThread.start();                    
                    Thread.sleep(500);
                } catch (Exception e) {
                    System.out.println(e + "in line 40");
                }
            }
        } catch (Exception e) {
            System.out.println(e + "in line 37.");
        }
    }

Now, there is a class I called ServerThread and it does this:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package prinsu.toptrumps;

import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

/**
 *
 * @author Mirza
 */
public class ServerThread implements Runnable {

    private Packet packet;
    private JTextArea jta;
    private int port;
    private ObjectOutputStream out;
    private ObjectInputStream in;
    private ServerSocket server;
    private Socket connection;

    public ServerThread(Packet packet, JTextArea jta, int port, ObjectOutputStream out, ObjectInputStream in, ServerSocket server, Socket connection) {
        this.packet = packet;
        this.jta = jta;
        this.port = port;
        this.out = out;
        this.in = in;
        this.server = server;
        this.connection = connection;
    }   


    @Override
    public void run() {
        try {
            this.server = new ServerSocket(this.port);
            while (true) {
                try {
                    // Connecting and communicating.
                    waitForConnection();
                    System.out.println("1");
                    setupStreams();
                    System.out.println("2");
                    whileChatting();
                    System.out.println("3");
                } catch (EOFException e) {
                    System.out.println(e + " RUN1");
                } finally {
                    cleanUp();
                }
            }
        } catch (IOException e) {
            System.out.println(e + " RUN2");
        }
    }

    private void waitForConnection() throws IOException { // Waits for client connection.
        showMessage("Waiting for client...");
        while (true) {
            this.connection = this.server.accept();            
            showMessage("Client connected from " + connection.getInetAddress().getHostAddress());
        }
    }

    private void setupStreams() throws IOException { // Send and recieve data (IO stream creation).
        this.out = new ObjectOutputStream(this.connection.getOutputStream());
        this.out.flush();
        this.in = new ObjectInputStream(this.connection.getInputStream());        
    }

    private void whileChatting() throws IOException { // Allows communication.
        String message = "you're connected";
        do {
            try {
              this.packet = (Packet) this.in.readObject();
                showMessage(message);
            } catch (ClassNotFoundException e) {
                System.out.println(e+ "in line 77.");
                System.out.println("Can't read packet or its empty.");
            }
        } while (!message.equals("CLIENT - END"));
    }

    private void cleanUp() { // Closes the connection.
        showMessage("Closing connection...");
        try {
            this.out.close();
            this.in.close();
            this.connection.close();
        } catch (IOException e) {
            System.out.println(e + "in line 89.");
        }
        showMessage("Connection closed!");
    }

    private void showMessage(String msg) { // Prints messages in GUI's console.
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                jta.append("\n"+msg);
            }
        });

    }
}

The console prints this:

run:
java.net.BindException: Address already in use: JVM_Bind RUN2
java.net.BindException: Address already in use: JVM_Bind RUN2
java.net.BindException: Address already in use: JVM_Bind RUN2
java.net.BindException: Address already in use: JVM_Bind RUN2
java.net.BindException: Address already in use: JVM_Bind RUN2
java.net.BindException: Address already in use: JVM_Bind RUN2
java.net.BindException: Address already in use: JVM_Bind RUN2
java.net.BindException: Address already in use: JVM_Bind RUN2
java.net.BindException: Address already in use: JVM_Bind RUN2
BUILD STOPPED (total time: 7 seconds)

During outputting, the GUI freezes (even tho I ran a separate thread, I think). Googling the error message revealed I might be calling ServerSocket on the same port twice, thus blocking it. But I'm not.
What do I fail to see?

PS
Thank you for reading this.

Recommended Answers

All 4 Replies

Oh! As a means of communication I have an object called Packet which sets the sates of everything.

I imagine using ObjectOutputStream's writeObject(); and ObjectInputStream's readObject(); to transfer the game state back and forth.

public class Packet {
    int round;
    Player p1;
    Player p2;
    Card onTable;

    public Packet(int round, Player p1, Player p2, Card onTable) {
        this.round = round;
        this.p1 = p1;
        this.p2 = p2;
        this.onTable = onTable;
    }

}

startRunning creates an infinite nuber of concurrent server threads, each of which tries to open the same server socket. That's the wrong threading strategy.
To make things worse, you have that startRunning infinite loop exceuting on the Swing event dispatch thread, so it blocks all GUI activity for ever.

You need to start one thread with a ServerSocket, looping to accept connections.
As soon as it gets a connection it thens starts a new thread to handle that one Socket connection. The new thread opens the input and output streams and processes all input from that socket.

Ie you have one server socket thread plus one additional thread per socket connection. (plus Swing's EDT, of course)

SOLUTION!
The trick was to pass serverRunning(); into public void run(); since class Server.java implements Runnable.
Later, I simply called this from the main GUI:

this.newConnection = new Thread(this.s);
this.newConnection.start();

Where this.newConnection is a Thred and this.s is an instance of Server.java.

Here is the change in Server.java:

@Override
    public void run() {
        startRunning();
    }

    // Server setup

    public void startRunning() { // Initiates the server.
        try {
            this.server = new ServerSocket(this.port);
            while (true) {
                try {
                    // Connecting and communicating.
                    waitForConnection();
                    setupStreams();
                    whileChatting();
                } catch (Exception e) {
                    System.out.println(e + "in line 40");
                } finally {
                    cleanUp();
                }
            }
        } catch (Exception e) {
            System.out.println(e + "in line 37.");
        }
    }

That made didn't cause the UI to freeze and it waited and handled clients as expected. However, when I hit a button in the GUI, I want it to send a message.

public void sendPacket() { // Sends Packet object.
        try {
//            this.out.writeObject(this.packet);
            String message = "YAAAY!";
            this.out.writeObject(message);
            this.out.flush();
        } catch (IOException e) {
            System.out.println(e + "in line 100");
        }
    }

Right now, I'm just trying to send a String to see that it works.
But when I hit "Send" it causes a NullExceptionPointer:

run:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at prinsu.toptrumps.Server.sendPacket(Server.java:116)
    at prinsu.toptrumps.MainGUIServer.playRound_BActionPerformed(MainGUIServer.java:317)
    at prinsu.toptrumps.MainGUIServer.access$100(MainGUIServer.java:19)
    at prinsu.toptrumps.MainGUIServer$2.actionPerformed(MainGUIServer.java:151)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
    at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2346)
    at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
    at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
    at java.awt.Component.processMouseEvent(Component.java:6525)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3322)
    at java.awt.Component.processEvent(Component.java:6290)
    at java.awt.Container.processEvent(Container.java:2234)
    at java.awt.Component.dispatchEventImpl(Component.java:4881)
    at java.awt.Container.dispatchEventImpl(Container.java:2292)
    at java.awt.Component.dispatchEvent(Component.java:4703)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4898)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4533)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4462)
    at java.awt.Container.dispatchEventImpl(Container.java:2278)
    at java.awt.Window.dispatchEventImpl(Window.java:2739)
    at java.awt.Component.dispatchEvent(Component.java:4703)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:751)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:702)
    at java.awt.EventQueue$3.run(EventQueue.java:696)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86)
    at java.awt.EventQueue$4.run(EventQueue.java:724)
    at java.awt.EventQueue$4.run(EventQueue.java:722)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:721)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
BUILD SUCCESSFUL (total time: 30 seconds)

I've googled around a bit and found out that apparently ObjectOutputStream out is never initialized, even tho it should be since this:

private void setupStreams() throws IOException { // Send and recieve data (IO stream creation).
        this.out = new ObjectOutputStream(this.connection.getOutputStream());
        this.out.flush();
        this.in = new ObjectInputStream(this.connection.getInputStream());
    }

Is called in startRunning(); which has it's own thread.

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.