3

I'm creating a chat for a game that runs on a server and client system, and then of course the game on the server client system, but I cannot get my multithreaded program to work, and I'll explain how below. What I can say is the server client system works when single threaded.

To start, my connection class uses these methods:

public void sendObjects(Object obj) throws IOException{
        output.writeObject(obj);
        output.flush();

}   
public Object receiveObjects() throws IOException, ClassNotFoundException{
        return input.readObject();
}

The class I'm running everything on is the Frame class:

public class Frame implements ActionListener{

private int width;
private int height;
private JFrame jframe;
private Board board;
private JTextArea textBox;
private JScrollPane pane;
private Connection connection;
private JTextArea userText;
private JScrollPane userPane;
private JButton send;
private NetworkReceiver net;


public Frame(int width, int height, Connection connection, Board player, Board opponent){
    this.width = width;
    this.height = height;
    this.connection = connection;

    board = new Board(player, opponent);
    init();
    textBox = new JTextArea("CHAT WINDOW");
    textBox.setWrapStyleWord(true);
    textBox.setLineWrap(true);
    textBox.setBackground(Color.GRAY);
    textBox.setBounds(height, 0, width - (height) - 20, height-40);
    textBox.setEditable(false);

    userText = new JTextArea("Enter Messages Here");
    userText.setWrapStyleWord(true);
    userText.setLineWrap(true);
    userText.setBackground(Color.WHITE);
    userText.setBounds(height, height-40, width - (height) - 20, 40);

    pane = new JScrollPane(textBox, 
            ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
            ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    pane.setBounds(height, 0, width - (height), height-40);

    userPane = new JScrollPane(userText, 
            ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
            ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    userPane.setBounds(height, height-40, width - (height) - 20, 40);

    send = new JButton();
    send.setIcon(Utility.getImageIcon(Utility.getBufferedImage(Assets.send)));
    send.setBounds(width - 20, height - 40, 20, 40);
    send.addActionListener(this);
    send.setBackground(Color.WHITE);


    jframe = new JFrame();
    jframe.setBackground(Color.DARK_GRAY);
    jframe.getContentPane().setPreferredSize(new Dimension(width, height));
    jframe.setLayout(null);
    jframe.pack();

    jframe.setLocationRelativeTo(null);
    jframe.add(pane);
    jframe.add(userPane);
    jframe.add(send);

    for(Space[] s: board.getSpaces()){
        for(Space space: s){
            jframe.add(space);
        }
    }
    for(Space[] s: board.getSpaces()){
        for(Space space: s){
            space.addActionListener(this);
        }
    }

    for(Space[] s: board.getSpaces()){
        for(Space space: s){
            if(space.getTile() != null){
                space.setBackground(space.getTile().getColor());
            }
        }
    }
    for(Space[] s: board.getSpaces()){
        for(Space space: s){
            if(space.getPiece() != null){
                space.setIcon(Utility.getImageIcon(space.getPiece().getImage()));
            }
        }
    }

    jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    jframe.setVisible(true);

    net = new NetworkReceiver(connection, this);
    net.start();
}

private void init(){        
  //stuff
}

@Override
public void actionPerformed(ActionEvent e) {
    if(e.getSource() == send){
        send();
    }

}

private synchronized void send() {
    String message = "YOU- " + userText.getText();
    userText.setText("");
    String totalMessage = textBox.getText();
    textBox.setText(totalMessage + "\n" + message);
    new NetworkSender(connection, message).start();
}

public synchronized void showMessage(String s){
    String totalMessage = "Opponent- " + textBox.getText();
    textBox.setText(totalMessage + "\n" + s);
}

I do not want to delete anything more in the constructor above in case it is in some way causing the issue(which I doubt but since I cannot find the issue better safe than sorry)

Here is the NetworkSender and NetworkReceiver classes:

public class NetworkSender extends Thread{

private Connection c;
private String msg;

public NetworkSender(Connection c, String msg){
    this.c = c;
    this.msg = msg;
}

public synchronized void run(){
    try {
        System.out.println("Trying to send");
        c.sendObjects(msg);
        System.out.println("Sent");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

}

public class NetworkReceiver extends Thread{

private Connection c;
private boolean running = true;
private Frame f;

public NetworkReceiver(Connection c, Frame f){
    this.c = c;
    this.f = f;
}

public synchronized void run(){
    while(running){
        System.out.println("running");
        Object obj = null;
        try {
            obj = c.receiveObjects();
            System.out.println("received");
        } catch (ClassNotFoundException e) {
            System.out.println("failed - class exception");
            e.printStackTrace();
        } catch (IOException e) {
            System.out.println("failed - IO exception");
            e.printStackTrace();
            running = false;
        }
        if(obj != null){
            if(obj instanceof String){
                f.showMessage((String)obj);
            }
        }
    }
}

public void kill(){
    running = false;
}

}

The exact spot of breaking is in the NetworkSender class. In the console, I receive "Trying to send" but never "sent." This leads me to think that I'm setting up the threads wrong, the threads are somehow unable to talk to each other (sender and receiver threads), or the server/client system is done improperly for multi-threading.

I have looked up this issue extensively, but I could not find a solution anywhere that worked, so I apologize if there is a solution I over looked. If everything is checking out, I can post my connection, server, and client classes if needed, but I'm assuming they are functional since they work on the EDT. As I put nearly 4 hours scouring online, I ask that if you think this is a duplicate question you comment a link first, as I am almost sure I have seen it already lol.

Dak3117
  • 53
  • 5
  • I would use Data Stream for writing Strings or even Writer/Reader. Your writer could block if nothing is reading it although it has to buffer a lot of data before this happens. – Peter Lawrey Aug 23 '16 at 05:01
  • I'm also planning on writing Objects(the point of instanceof in NetworkReceiver), but If I cant get a messenger to work how can i make a game? :P – Dak3117 Aug 23 '16 at 05:04
  • I highly recommend separating your network handling code from your GUI code. You should be able to test the network code without using a GUI e.g in automated unit tests. Trying to test networking via a GUI is much more complicated. – Peter Lawrey Aug 23 '16 at 05:07
  • 1
    There is no point in making the `run()` method synchronized here. You need to catch `EOFException` separately here, and break out of the read loop when you get it. All other `IOExceptions` except `SocketTimeoutException` are also fatal to the connection and should be logged and cause the read loop to exit. – user207421 Aug 23 '16 at 05:16
  • @EJP, is an OEFException a type of IOException? If so would it not be caught by the iOException clause and exit anyway? – Dak3117 Aug 23 '16 at 05:26
  • does anyone know what I am dong wrong? – Dak3117 Aug 27 '16 at 22:13

0 Answers0