基本问题是,
textarea是
null,当你尝试和呼叫
updatetextarea,这是因为
invokeLater呼叫尚未执行,并构建了您的UI,你基本上有一个竞争条件。
Socket在Swing中处理s 的通常方法是使用
SwingWorker
有关更多详细信息,请查看Swing和Worker线程中的并发性和SwingWorker。
您可以通过多种方式解决问题。您可以使用
SwingWorker来从套接字读取文本并生成更新通知(通过
publish/
process)方法,这通常称为“观察者模式”。这很好,因为它可以使您的代码脱钩并生成更可重用的解决方案。
SocketReader
这个类所做的只是从中读取文本,
Socket并
ActionEvent从文本生成s,很简单。
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,简单。从技术上讲,您不需要为此使用a
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,您不必使用单例,您可以简单地使其成为一个简单的类,并将其引用传递给您
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
很棒,一切都很好,但是您打算如何使用它呢?
基本上,您将在中创建一个的实例,
SocketReader并
SocketWriter在中将
ChatClient附加
ActionListener到阅读器,并在
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(); } @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 pre 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 pre 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"); } } public static void main(String args[]) { try { //<editor-fold defaultstate="collapsed" desc=" Look and feel setting pre (optional) "> 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(); 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 }您会注意到,我曾
SocketManager#open在
main抱歉,错过了您的“连接”代码。我建议改为将其移至该方法;)
ChatServer
我对此没有做太多改变,但以防万一…
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(); } }}通常,当客户端连接时,您将启动一个新
Thread客户端并让它处理客户端
Socket,但这不是我的重点。
因此,基于所有这些,您需要大量阅读,包括Java中的并发性和全部关于套接字



