gpt4 book ai didi

Java Swing GUI 客户端和服务器聊天应用程序 TextArea 未更新

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

我正在使用 Java 和 GUI 的 Swing 类创建一个聊天应用程序。

ChatServer 类将是从客户端接收消息并向所有客户端回显的服务器,但我只想为 2 个客户端进行聊天。

ChatClient 类既是客户端。它们在文本区域上显示从服务器发送的内容。并将文本字段中的文本发送到服务器。

ChatClient 类

package chatclient;

import java.net.Socket;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;

public class ChatClient extends javax.swing.JFrame {


public ChatClient() {
initComponents();
}

/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {

scrollPane = new javax.swing.JScrollPane();
textArea = new javax.swing.JTextArea();
btnConnect = new javax.swing.JButton();
btnDisconnect = new javax.swing.JButton();
lblStatus = new javax.swing.JLabel();
lblShowStatus = new javax.swing.JLabel();
txtInput = new javax.swing.JTextField();

setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("Chat Client A");

textArea.setEditable(false);
textArea.setColumns(20);
textArea.setRows(5);
textArea.setText("Welcome to the Chat Server. Type '/close' or Click 'Disconnect' to close.");
textArea.setWrapStyleWord(true);
textArea.setCaretPosition(textArea.getDocument().getLength());
scrollPane.setViewportView(textArea);

btnConnect.setText("Connect");
btnConnect.setActionCommand("btnConnect");
btnConnect.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
btnConnectMouseClicked(evt);
}
});

btnDisconnect.setText("Disconnect");
btnDisconnect.setActionCommand("btnDisconnect");
btnDisconnect.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnDisconnectActionPerformed(evt);
}
});

lblStatus.setText("Status: ");

lblShowStatus.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
lblShowStatus.setForeground(new java.awt.Color(255, 51, 51));
lblShowStatus.setText("Disconnected");

txtInput.setToolTipText("");
txtInput.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
txtInputActionPerformed(evt);
}
});

javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(scrollPane)
.addGroup(layout.createSequentialGroup()
.addComponent(btnConnect)
.addGap(18, 18, 18)
.addComponent(btnDisconnect)
.addGap(42, 42, 42)
.addComponent(lblStatus)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblShowStatus)
.addGap(0, 42, Short.MAX_VALUE))
.addComponent(txtInput))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(scrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 213, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 11, Short.MAX_VALUE)
.addComponent(txtInput, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btnConnect)
.addComponent(btnDisconnect)
.addComponent(lblStatus)
.addComponent(lblShowStatus))
.addContainerGap())
);

pack();
}// </editor-fold>

private void btnConnectMouseClicked(java.awt.event.MouseEvent evt) {
// TODO add your handling code here:
lblShowStatus.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
lblShowStatus.setForeground(new java.awt.Color(0, 204, 51));
lblShowStatus.setText("Connected");

// ADD CODES FOR CONNECTING TO CHAT SERVER

}

private void btnDisconnectActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
lblShowStatus.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
lblShowStatus.setForeground(new java.awt.Color(255, 51, 51));
lblShowStatus.setText("Disconnected");

// ADD CODES FOR DISCONNECTING FROM CHAT SERVER

}

private void txtInputActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
}

/**
* @param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>

/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new ChatClient().setVisible(true);
}
});

String input = "", serverInput = "";
String host = "localhost";
int port = 1337;
Socket client;

// updateTextArea("TEST UPDATE");

try {
client = new Socket(host, port);
System.out.println("Connected to Server!");

DataInputStream in = new DataInputStream(client.getInputStream());
DataOutputStream out = new DataOutputStream(client.getOutputStream());

System.out.println("Before setting text area");
updateTextArea("trying to update");

do {
// HANDLE INPUT PART HERE
serverInput = in.readUTF();


if(serverInput != null) {
System.out.println("Reached here");
System.out.println(serverInput);
updateTextArea(serverInput);
}

} while(!input.equals("/close"));
System.out.println("Program closed");
}

catch(Exception exc) {
System.err.println(exc.getMessage());
}


}

private static void updateTextArea(String temp) {
textArea.setText(textArea.getText() + "\n" + temp + "\n");
textArea.setCaretPosition(textArea.getDocument().getLength());
}

// Variables declaration - do not modify
private javax.swing.JButton btnConnect;
private javax.swing.JButton btnDisconnect;
private javax.swing.JLabel lblShowStatus;
private javax.swing.JLabel lblStatus;
private javax.swing.JScrollPane scrollPane;
private static javax.swing.JTextArea textArea;
private javax.swing.JTextField txtInput;
// End of variables declaration
}

ChatServer类

package chatserver;

import java.io.BufferedReader;
import java.io.InputStreamReader;
// for testing
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
*
* @author wacats
*/
public class ChatServer {
public static void main(String args[]) {
int port = 1337;

try {
ServerSocket server = new ServerSocket(port);
String inMessage = "";

while(true) {
Socket clientA = server.accept();
DataInputStream inA = new DataInputStream(clientA.getInputStream());
DataOutputStream outA = new DataOutputStream(clientA.getOutputStream());
// outA.writeUTF("Welcome to the Chat Server. Type '/close' or Click 'Disconnect' to close.");

// for testing
// BufferedReader user = new BufferedReader(new InputStreamReader(System.in));

do {
inMessage = inA.readUTF();

outA.writeUTF("testing");

if(inMessage != null) {
outA.writeUTF(inMessage);
}

} while(!inMessage.equals("/close"));
clientA.close();
}



}

catch(Exception ex) {
ex.printStackTrace();
}
}
}

我对这个程序的流程的想法是:

  1. 启动聊天服务器
  2. 启动 ChatClient
  3. 当两个客户端都连接到服务器时,他们就可以开始聊天。
  4. 按“Enter”会将文本字段中的文本发送到服务器。
  5. 服务器将向两个客户端广播文本。
  6. 客户端将更新文本区域,附加从服务器收到的文本。

最佳答案

基本问题是,当您尝试调用 updateTextArea 时,textAreanull,这是因为 invokeLater > 调用尚未执行并构建您的 UI,您基本上有一个竞争条件。

在 Swing 中处理 Socket 的正常方法是使用 SwingWorker

看看Concurrency in SwingWorker Threads and SwingWorker了解更多详情。

有多种方法可以解决您的问题。您可以使用 SwingWorker 从套接字读取文本并生成更新通知(通过 publish/process)方法,这是众所周知的作为“观察者模式”。这很好,因为它可以解耦您的代码并生成更可重用的解决方案。

SocketReader

这个类所做的就是从Socket读取文本并从文本生成ActionEvent,很简单。

public class SocketReader extends SwingWorker<Void, String> {

private List<ActionListener> actionListeners;

public SocketReader() {
actionListeners = new ArrayList<>(25);
}

public void addActionListener(ActionListener listener) {
actionListeners.add(listener);
}

public void removeActionListener(ActionListener listener) {
actionListeners.remove(listener);
}

@Override
protected Void doInBackground() throws Exception {
System.out.println("Connected to Server!");

try (DataInputStream in = new DataInputStream(SocketManager.INSTACNE.getInputStream())) {

System.out.println("Before setting text area");

String serverInput = null;
do {
// HANDLE INPUT PART HERE
serverInput = in.readUTF();

if (serverInput != null) {
System.out.println("Read " + serverInput);
publish(serverInput);
}

} while (!serverInput.equals("/close"));
System.out.println("Program closed");
}
return null;
}

@Override
protected void process(List<String> chunks) {
for (String text : chunks) {
ActionEvent evt = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, text);
for (ActionListener listener : actionListeners) {
listener.actionPerformed(evt);
}
}
}

}

SocketWriter

这个简单的将文本写入Socket,很简单。从技术上讲,您不需要为此使用 SwingWorker,但我喜欢这样一个事实:它相对容易取消

public class SocketWriter extends SwingWorker<Void, Void> {

private List<String> messages;
private ReentrantLock lock;
private Condition waitCon;

public SocketWriter() {
messages = Collections.synchronizedList(new ArrayList<String>(25));
lock = new ReentrantLock();
waitCon = lock.newCondition();
}

public void write(String text) {
System.out.println("Write " + text);
messages.add(text);
try {
lock.lock();
waitCon.signalAll();
} finally {
lock.unlock();
}
}

@Override
protected Void doInBackground() throws Exception {
try (DataOutputStream out = new DataOutputStream(SocketManager.INSTACNE.getOutputStream())) {
while (!isCancelled()) {
while (messages.isEmpty() && !isCancelled()) {
try {
lock.lock();
waitCon.await();
} finally {
lock.unlock();
}
}
List<String> cache = new ArrayList<>(messages);
messages.clear();
for (String text : cache) {
System.out.println("Send " + text);
out.writeUTF(text);
}
}
}
return null;
}

}

SocketManager

好吧,这对我来说有点过分了,但我想要一个Socket的中央 Controller ,你不必使用单例,你可以简单地使它成为一个简单的类并通过它对您的 ChatClient 的引用,并一直到 SocketReader/Writer,但已经晚了,而且我很懒

public enum SocketManager {
INSTACNE;

private String host = "localhost";
private int port = 1337;
private Socket socket;

public Socket open() throws IOException {
if (socket != null) {
close();
}
socket = new Socket(host, port);
return socket;
}

public void close() throws IOException {
if (socket == null) {
return;
}
socket.close();
}

public boolean isOpen() {
return socket != null
&& socket.isConnected()
&& !socket.isClosed()
&& !socket.isInputShutdown()
&& !socket.isOutputShutdown();
}

public InputStream getInputStream() throws IOException {
Objects.requireNonNull(socket, "Socket is not open");
return socket.getInputStream();
}

public OutputStream getOutputStream() throws IOException {
Objects.requireNonNull(socket, "Socket is not open");
return socket.getOutputStream();
}
}

ChatClient

这一切都很棒,但是你打算如何使用它?

基本上,您将在 ChatClient 中创建 SocketReaderSocketWriter 的实例,并附加一个 ActionListener code> 发送给阅读器,并在触发时更新 JTextArea 并将您想要发送的文本发送到 SocketWriter,例如...

public class ChatClient extends javax.swing.JFrame {

public ChatClient() {
initComponents();
socketReader = new SocketReader();
socketReader.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String text = e.getActionCommand();
textArea.append(text);
textArea.append("\n");
textArea.setCaretPosition(textArea.getDocument().getLength());
}
});
socketReader.execute();

socketWriter = new SocketWriter();
socketWriter.execute();
}

/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {

scrollPane = new javax.swing.JScrollPane();
textArea = new javax.swing.JTextArea();
btnConnect = new javax.swing.JButton();
btnDisconnect = new javax.swing.JButton();
lblStatus = new javax.swing.JLabel();
lblShowStatus = new javax.swing.JLabel();
txtInput = new javax.swing.JTextField();

setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("Chat Client A");

textArea.setEditable(false);
textArea.setColumns(20);
textArea.setRows(5);
textArea.setText("Welcome to the Chat Server. Type '/close' or Click 'Disconnect' to close.");
textArea.setWrapStyleWord(true);
textArea.setCaretPosition(textArea.getDocument().getLength());
scrollPane.setViewportView(textArea);

btnConnect.setText("Connect");
btnConnect.setActionCommand("btnConnect");
btnConnect.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
btnConnectMouseClicked(evt);
}
});

btnDisconnect.setText("Disconnect");
btnDisconnect.setActionCommand("btnDisconnect");
btnDisconnect.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnDisconnectActionPerformed(evt);
}
});

lblStatus.setText("Status: ");

lblShowStatus.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
lblShowStatus.setForeground(new java.awt.Color(255, 51, 51));
lblShowStatus.setText("Disconnected");

txtInput.setToolTipText("");
txtInput.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
txtInputActionPerformed(evt);
}
});

javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(scrollPane)
.addGroup(layout.createSequentialGroup()
.addComponent(btnConnect)
.addGap(18, 18, 18)
.addComponent(btnDisconnect)
.addGap(42, 42, 42)
.addComponent(lblStatus)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblShowStatus)
.addGap(0, 42, Short.MAX_VALUE))
.addComponent(txtInput))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(scrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 213, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 11, Short.MAX_VALUE)
.addComponent(txtInput, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btnConnect)
.addComponent(btnDisconnect)
.addComponent(lblStatus)
.addComponent(lblShowStatus))
.addContainerGap())
);

pack();
}// </editor-fold>

private void btnConnectMouseClicked(java.awt.event.MouseEvent evt) {
// TODO add your handling code here:
lblShowStatus.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
lblShowStatus.setForeground(new java.awt.Color(0, 204, 51));
lblShowStatus.setText("Connected");

// ADD CODES FOR CONNECTING TO CHAT SERVER
}

private void btnDisconnectActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
lblShowStatus.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
lblShowStatus.setForeground(new java.awt.Color(255, 51, 51));
lblShowStatus.setText("Disconnected");

// ADD CODES FOR DISCONNECTING FROM CHAT SERVER
}

private void txtInputActionPerformed(java.awt.event.ActionEvent evt) {
if (SocketManager.INSTACNE.isOpen()) {
socketWriter.write(txtInput.getText());
} else {
System.out.println("!! Not open");
}
}

/**
* @param args the command line arguments
*/
public static void main(String args[]) {

try {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(ChatClient.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>

SocketManager.INSTACNE.open();

/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new ChatClient().setVisible(true);
}
});

} catch (IOException ex) {
ex.printStackTrace();
}
//</editor-fold>

}

private SocketWriter socketWriter;
private SocketReader socketReader;

// Variables declaration - do not modify
private javax.swing.JButton btnConnect;
private javax.swing.JButton btnDisconnect;
private javax.swing.JLabel lblShowStatus;
private javax.swing.JLabel lblStatus;
private javax.swing.JScrollPane scrollPane;
private javax.swing.JTextArea textArea;
private javax.swing.JTextField txtInput;
// End of variables declaration
}

你会注意到,我在main中使用了SocketManager#open,抱歉,错过了你的“连接”代码。我建议将其转移到该方法;)

聊天服务器

我没有对此做太多改变,但以防万一......

public class ChatServer {

public static void main(String args[]) {
int port = 1337;

try {
ServerSocket server = new ServerSocket(port);
String inMessage = "";

while (true) {
System.out.println("Waiting");
Socket clientA = server.accept();
System.out.println("Connected");
DataInputStream inA = new DataInputStream(clientA.getInputStream());
DataOutputStream outA = new DataOutputStream(clientA.getOutputStream());
// outA.writeUTF("Welcome to the Chat Server. Type '/close' or Click 'Disconnect' to close.");

// for testing
// BufferedReader user = new BufferedReader(new InputStreamReader(System.in));
do {
inMessage = inA.readUTF();

if (inMessage != null) {
outA.writeUTF(inMessage);
}

} while (!inMessage.equals("/close"));
clientA.close();
}

} catch (Exception ex) {
ex.printStackTrace();
}
}
}

通常,当客户端连接时,您会启动一个新的线程并让它处理客户端Socket,但这不是我的重点。

因此,基于此,您需要阅读大量内容,包括 Concurrency in JavaAll About Sockets

关于Java Swing GUI 客户端和服务器聊天应用程序 TextArea 未更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44155909/

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