gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-02 02:25:30 24 4
gpt4 key购买 nike

我正在制作一个简单的无线程客户端-服务器程序,其中GUI在两个服务器上都有一个按钮 em> 和客户端 端。当客户端按下按钮时,它将按钮上的文本更改为“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/47858079/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com