java - 使用 Swing GUI 的简单客户端-服务器程序

标签 java swing sockets

我正在制作一个简单的无线程客户端-服务器程序,其中GUI在服务器客户端端都有一个按钮。当客户端按下按钮时,它会将按钮上的文本更改为“C”,并将“C”字符串发送到服务器,因此服务器端的按钮将文本更改为“C”服务器的工作方式与客户端类似,但发送“S”而不是“C”。他们轮流工作:轮到客户端时,服务器按钮被锁定,他无法更改他的按钮。 客户端总是首先启动。

客户端按下按钮时,它工作正常,但是当服务器按下按钮时,它会在服务器端将按钮更改为“S”,但在客户端端则不会。我知道我做错了什么。

服务器代码:

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;
            }
        }
    }
}

客户端代码:

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;
                }
            }
        }
    }
}

我删除了导入,因此代码会更短。

最佳答案

您的代码当前存在的问题:

  • 您正在创建一个 Swing GUI,并在 Swing 事件调度线程或 EDT 之外运行它。 GUI 应在事件线程上启动,以便保证所有 Swing 代码在单个线程上运行。
  • 您有一个长时间运行的 while 循环,它正在进行 Swing 突变调用,更新 JButton 的状态。如果此代码在 Swing 事件线程上运行,它将阻止/卡住 GUI。应在非 EDT 的后台线程中显式调用此 block ,并且所有 Swing 调用都应按照 Lesson: Concurrency in Swing 在事件线程上排队。教程。
  • 您在不同的线程中使用非 volatile boolean 值,因此存在变量在应该更改时未被更改的风险
  • 您似乎立即关闭了直播,导致关注点之间无法充分沟通。

正在研究一个更清晰的示例......

例如:

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();
    }    
}

关于java - 使用 Swing GUI 的简单客户端-服务器程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47743139/

相关文章:

java - 无法使用maven部署将java应用程序重新部署到tomcat7

java - PowerMock whenNew 不返回模拟实例

java - 如何为 Spring Boot 请求参数中使用的类中的字段值设置默认值

java - 对于输入字符串: “Mat [ 0*0*CV_32FC1, isCont=true, isSubmat=false, nativeObj=0x78a0dff700, dataAddr=0x0 ]”-错误填充矩阵

sockaddr_in 结构中 "localhost"的 C Linux 宏?

c - C语言的Socket通信。发送文件内容

java - 监听器和适配器有什么区别?

java - 放置在 5 个不同 JPanel 中的 Jbutton 数组中的 Action 监听器

java - 如何在java中的JTextArea上添加JScrollPane?

java - 从 Java 套接字服务器向 C 套接字客户端接收数据时出现问题