简体   繁体   中英

Simple Client-Server program using Swing GUI

I'm making a simple, no thread Client - Server program where GUI has one button on both server and client side. When client presses button it changes text on a button to "C" and sends to server "C" string, so the button on the server side changes text to "C" . Server works similarly to client but sends "S" instead of "C" . They work in turns: when it's the client's turn, server's button is locked and he cannot change his button. Client always starts first.

When client presses button it works fine, but when server presses button it changes button to "S" on the server side but not on client's side. I know what I'm doing wrong.

Server code:

public class Serv implements ActionListener
{
    private JButton button;
    private boolean myTurn;
    private ServerSocket sock;
    private Socket s;
    private BufferedReader input;
    private PrintStream output;

    public Serv() throws UnknownHostException, IOException
    {
        button = new JButton();
        myTurn = false;
        sock = new ServerSocket(9001);
        s = null;
        button = new JButton();
    }

    public void createGUI()
    {
        JFrame frame = new JFrame("TicTacToe - Server");
        JPanel mainPanel = new JPanel();
        mainPanel.setPreferredSize(new Dimension(100, 100));
        button = new JButton("");
        button.setPreferredSize(new Dimension(100, 100));
        button.setFont(new Font(button.getFont().getName(), button.getFont().getStyle(), 70));
        button.setActionCommand("1");
        button.addActionListener(this);
        mainPanel.add(button);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public void startMyGame() throws IOException
    {
        createGUI();
        s = sock.accept();
        input = new BufferedReader(new InputStreamReader(s.getInputStream()));
        output = new PrintStream(s.getOutputStream(), true);
        while(true)
        {
            if(myTurn == false)
            {
                myTurn = true;
                String out = input.readLine();
                button.setText(out);
            }
        }
    }

    public static void main(String args[]) 
    {
        Serv tc = null;
        try
        {
            tc = new Serv();
            tc.startMyGame();

        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            try
            {
                tc.close();
            }
            catch(Exception ex)
            {
                ex.printStackTrace();
            }
        }

    }

    private void close() throws IOException
    {
        this.sock.close();
        this.input.close();
        this.output.close();
    }

    @Override
    public void actionPerformed(ActionEvent e) 
    {
        if(myTurn == true)
        {
            if(e.getActionCommand().equals("1"))
            {
                JButton b = (JButton) e.getSource();
                b.setText("S");
                output.println("S");
                myTurn = false;
            }
        }
    }
}

Client code:

public class Cli implements ActionListener
{
    private JButton button;
    private boolean myTurn;
    private Socket sock;
    private BufferedReader input;
    private PrintStream output;

    public Cli() throws UnknownHostException, IOException
    {
        button = new JButton();
        myTurn = true;
        sock = new Socket("127.0.0.1", 9001);
        input = new BufferedReader(new InputStreamReader(sock.getInputStream()));
        output = new PrintStream(sock.getOutputStream(), true);
    }

    public void createGUI()
    {
        JFrame frame = new JFrame("TicTacToe - Client");
        JPanel mainPanel = new JPanel();
        mainPanel.setPreferredSize(new Dimension(100, 100));
        button = new JButton("");
        button.setPreferredSize(new Dimension(100, 100));
        button.setFont(new Font(button.getFont().getName(), button.getFont().getStyle(), 70));
        button.setActionCommand("1");
        button.addActionListener(this);
        mainPanel.add(button);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public void startMyGame() throws IOException
    {
        createGUI();
        while(true)
        {
            if(myTurn == false)
            {
                myTurn = true;
                String out = input.readLine();
                button.setText(out);
            }
        }
    }

    private void close() throws IOException
    {
        this.sock.close();
        this.input.close();
        this.output.close();
    }

    public static void main(String args[]) 
    {
        Cli tc = null;
        try
        {
            tc = new Cli();
            tc.startMyGame();

        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            try
            {
                tc.close();
            }
            catch(Exception ex)
            {
                ex.printStackTrace();
            }
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) 
    {
        if(myTurn == true)
        {
            if(e.getActionCommand().equals("1"))
            {
                JButton b = (JButton) e.getSource();
                if(!b.getText().equals("X") || !b.getText().equals("O"))
                {
                    b.setText("C");
                    output.println("C");
                    myTurn = false;
                }
            }
        }
    }
}

I have deleted imports so the codes would be shorter.

Current issues with your code:

  • You're creating a Swing GUI and running it off of the Swing event dispatch thread or EDT. GUI's should be started on the event thread so that all Swing code is guaranteed to run on a single thread.
  • You've got a long-running while loop, and it is making Swing mutational calls, updating the state of a JButton. If this code were running on the Swing event thread, it would block/freeze the GUI. This block should be explicitly called in a background thread that is not the EDT, and all Swing calls should be queued onto the event thread as per the Lesson: Concurrency in Swing tutorial.
  • You're using a non-volatile boolean in different threads, risking the variable not being changed when it should be changed
  • You appear to be closing your streams immediately, preventing adequate communication between concerns.

Working on a cleaner example.......

For example:

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

import javax.swing.*;

public class SimpleServerClient {
    private static final int PORT = 9001;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            try {
                SimpleServer server = new SimpleServer(PORT, "Server", false);
                SimpleClient client = new SimpleClient(PORT, "Client", true);
                server.createGui();
                client.createGui();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }
}

interface SimpleGui {
    void sendLine(String nextLine);
}

// background thread handles listening to the Scanner 
// which scans a Socket's InputStream
class MyWorker extends SwingWorker<Void, Void> {
    public static final String LINE = "line";
    private Scanner inputScanner;
    private SimpleGui gui;
    private String line = "";

    public MyWorker(Scanner inputScanner, SimpleGui gui) {
        this.inputScanner = inputScanner;
        this.gui = gui;
    }

    @Override
    protected Void doInBackground() throws Exception {
        while (inputScanner.hasNext()) {
            // get line from Scanner                
            // use the setter method in case we want to use a PropertyChangeListener later
            setLine(inputScanner.nextLine());

            // send line to the GUI
            gui.sendLine(getLine());
        }
        return null;
    }

    public String getLine() {
        return line;
    }

    // again rigged up to allow use of PropertyChangeListeners
    public void setLine(String line) {
        this.line = line;
        firePropertyChange(LINE, null, line);
    }
}

// code that both the client and server GUI classes share
abstract class DefaultGui implements SimpleGui {

    // this guy ***must**** be volitile!
    private volatile boolean myTurn;
    protected Scanner inputScanner;
    protected PrintStream out;
    protected JButton button = new JButton("Blank");
    protected Socket socket;
    protected String name;
    protected int port;

    public DefaultGui(int port, String name, boolean myTurn) {
        this.port = port;
        this.name = name;
        this.myTurn = myTurn;
    }

    @Override
    public void sendLine(String nextLine) {
        button.setText(nextLine);
        myTurn = true;
    }

    public void createGui() {
        button.addActionListener(e -> actionPerformed(e));
        JPanel panel = new JPanel();
        panel.setPreferredSize(new Dimension(300, 300));
        panel.add(button);

        JFrame frame = new JFrame(getName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(panel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    protected void actionPerformed(ActionEvent e) {
        if (!myTurn) {
            return;
        }
        out.println(getName());
        button.setText(getName());
        myTurn = false;
    }

    public String getName() {
        return name;
    }

}

class SimpleServer extends DefaultGui {
    private ServerSocket serverSocket;

    public SimpleServer(int port, String name, boolean myTurn) throws IOException {
        super(port, name, myTurn);
        serverSocket = new ServerSocket(port);
        new Thread(() -> {
            try {
                // accept() blocks the current thread, so must be called on a background thread
                socket = serverSocket.accept();
                inputScanner = new Scanner(socket.getInputStream());
                out = new PrintStream(socket.getOutputStream(), true);
                new MyWorker(inputScanner, this).execute();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }    
}

class SimpleClient extends DefaultGui {

    public SimpleClient(int port, String name, boolean myTurn) throws IOException {
        super(port, name, myTurn);
        socket = new Socket("localhost", port);
        inputScanner = new Scanner(socket.getInputStream());
        out = new PrintStream(socket.getOutputStream());
        new MyWorker(inputScanner, this).execute();
    }    
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM