问题描述
上一个问题: Java Swing GUI客户端和服务器聊天应用程序TextArea无法更新
我正在使用Swing类在Java上创建聊天应用程序.我已经在服务器部分完成了多线程部分.
I am making a Chat App on Java using Swing Class. I have done the multithreading part on the server part of it.
请注意,该服务器应该为两个相互聊天的客户端提供服务,而不是其中一个作为主机,而其中一个作为客户端.
Note that the server is supposed to serve two clients chatting to each other instead of one of them as host and one of them as client.
ChatServer
public class ChatServer {
public static void main(String args[]) {
final int port = 1337;
ServerSocket serverSocket = null;
Socket socket = null;
try {
serverSocket = new ServerSocket(port);
} catch(Exception ex) {
ex.printStackTrace();
}
while(true) {
try {
socket = serverSocket.accept();
} catch (Exception ex) {
ex.printStackTrace();
}
new Thread(new Handler(socket)).start();
}
}
}
处理程序(用于ChatServer)
public class Handler implements Runnable {
private Socket socket;
public Handler(Socket s) {
socket = s;
}
@Override
public void run() {
try {
String inMessage = "";
while (true) {
System.out.println("Waiting");
System.out.println("Connected");
DataInputStream in = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
do {
inMessage = in.readUTF();
if(inMessage != null) {
out.writeUTF(inMessage);
}
} while(!inMessage.equals("/close"));
socket.close();
}
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
现在使用与ChatClient不同的软件包,我想使我的ChatClient也支持多线程.
Now on a different package as ChatClient, I would like to make my ChatClient support multi-threading as well.
ChatClient
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("\n");
textArea.append(text);
textArea.setCaretPosition(textArea.getDocument().getLength());
txtInput.setText("");
}
});
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");
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, 211, Short.MAX_VALUE))
.addComponent(txtInput))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(25, 25, 25)
.addComponent(scrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 213, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 28, 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:
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);
}
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 static javax.swing.JTextArea textArea;
private javax.swing.JTextField txtInput;
// End of variables declaration
}
SocketReader
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
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
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中的Swing GUI进行与使用ChatServer时相同的方式.
I am not sure whether I should do the same way for the Swing GUI in ChatClient as how I did it with ChatServer.
因此,我想知道在GUI窗口上进行多线程处理的正确方法,尤其是现在它还涉及其他类.
So I would like to know the correct way of doing multithreading on a GUI window especially it now involves other class also.
推荐答案
我为TCP连接创建了过于基本的聊天应用程序.我的代码在三个文件中.一台用于服务器,一台用于客户端,一台用于UI.您可以自己实现UI部分.我只实现了基本的.
I have created too basic chat application for TCP connection. My code is in three files. One for server, one for client and one for UI. You can implement UI part yourself. I have implemented only basic.
Server part:
import java.io.*;
import java.net.*;
public class ChatServer
{ ServerSocket server;
Socket client;
InputStream is;
OutputStream os;
BufferedReader br;
byte buf[];
String msg;
ChatServer(InetSocketAddress ip)
{ try
{ server=new ServerSocket();
server.bind(ip);
br=new BufferedReader(new InputStreamReader(System.in));
buf=new byte[80];
System.out.println("Server Listening on IP : "+server.getInetAddress().toString()+" PORT : "+server.getLocalPort());
}
catch(Exception e)
{ e.printStackTrace(); }
}
public void doChat()
{ try
{ System.out.println("Server Ready");
int r=0;
client=server.accept();
System.out.println("\n\nConnected to client \n IP :"+client.getInetAddress().toString()+" PORT : "+client.getPort());
is=client.getInputStream();
os=client.getOutputStream();
SenderUI sui=new SenderUI("Server",client,os,is);
while(true)
{ if((r=is.read(buf,0,80))!=-1)
{ msg=new String(buf,0,buf.length);
System.out.println("Client : "+msg);
for(int i=0;i<80;i++)
{ buf[i]=0; }
}
else
{ System.out.println("Error in receiving ");
break;
}
}
}
catch(Exception e) //IOE,SocketE,UnkownHost etc
{ e.printStackTrace(); }
finally
{ try{ is.close(); os.close(); client.close(); server.close(); }
catch(Exception e){ e.printStackTrace();}
}
}
public static void main(String[] args)
{ InetSocketAddress ip=null;
try
{ ip=new InetSocketAddress(InetAddress.getByName(args[0]),12345);}
catch(Exception e)
{ e.printStackTrace(); return;}
ChatServer chat=new ChatServer(ip);
chat.doChat();
}
}
服务器将根据客户端要求创建线程.
Server will create thread as per client requirements.
Client Part:
import java.io.*;
import java.net.*;
public class ChatClient
{
Socket client;
InputStream is;
OutputStream os;
BufferedReader br;
String msg;
byte buf[];
ChatClient(InetAddress ip,int port)
{ try
{ client=new Socket(ip,port);
System.out.println("Connected to server : "+ip.toString());
is=client.getInputStream();
os=client.getOutputStream();
br=new BufferedReader(new InputStreamReader(System.in));
buf=new byte[80];
}
catch(Exception e)
{ e.printStackTrace(); }
}
public void doChat()
{ try
{ System.out.println("\n\nCommunication Line acquired \n Start conversation \n");
SenderUI sui=new SenderUI("Client",client,os,is);
int r=0;
while(true)
{ if((r=is.read(buf,0,80))!=-1)
{ msg=new String(buf,0,buf.length);
System.out.println("Server : "+msg);
}
else
{ System.out.println("Error in receiving"); break; }
for(int i=0;i<80;i++)
{ buf[i]=0; }
}
}
catch(SocketException se)
{ System.out.println("\nServer Disconnected");}
catch(Exception e) //IOE,SocketE,UnkownHost etc
{ e.printStackTrace(); }
finally
{ try{ is.close(); os.close(); client.close(); }
catch(Exception e){ }
}
}
public static void main(String[] args)
{ InetAddress ip=null;
int port=0;
try
{ ip=InetAddress.getByName(args[0]);
port=Integer.parseInt(args[1]);
}
catch(NumberFormatException ne)
{ System.out.println("Invalid port number"); return; }
catch(UnknownHostException e)
{ System.out.println("Invalid Server IP/Name"); return; }
catch(Exception e)
{ e.printStackTrace(); }
ChatClient chat=new ChatClient(ip,port);
chat.doChat();
}
}
客户端将向特定端口上的服务器发出请求.
Client will make request to server on specific port.
Demo UI:
import java.io.*;
InputStream is;
Socket osck;
JTextArea msgbox;
JButton sendb;
String uname;
SenderUI(String user,Socket osc,OutputStream ost,InputStream ist)
{ super(user);
this.setSize(300,200);
this.setLayout(null);
this.setVisible(true);
uname=user;
osck=osc;
os=ost;
is=ist;
msgbox=new JTextArea();
msgbox.setBounds(5,5,200,100);
sendb=new JButton("Send");
sendb.setBounds(5,110,80,30);
sendb.addActionListener(this);
this.add(msgbox); this.add(sendb);
this.addWindowListener(this);
}
public void actionPerformed(ActionEvent ae)
{ String msg=msgbox.getText();
if(!msg.equals(""))
{ try
{ os.write(msg.getBytes());
System.out.println(uname+" : "+msg);
}
catch(Exception e)
{ e.printStackTrace();
}
msgbox.setText("");
}
}
public void windowActivated(WindowEvent e)
{ }
public void windowClosed(WindowEvent e)
{ }
public void windowClosing(WindowEvent e)
{ try
{ System.out.println("End of conversation");
os.close(); is.close(); osck.close();
System.exit(0);
}
catch(Exception ee)
{ ee.printStackTrace(); }
}
public void windowDeactivated(WindowEvent e)
{ }
public void windowDeiconified(WindowEvent e)
{ }
public void windowIconified(WindowEvent e)
{ }
public void windowOpened(WindowEvent e)
{ }
}
我已经测试过了.它完美地工作.如果找到任何查询.请在下面发表评论.祝你有美好的一天.
I have tested it. It works perfectly. If you find any query. Please comment below. Have a nice day.
这篇关于在Java Swing GUI聊天应用程序中创建新线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!