在Java Swing GUI聊天应用程序中创建新线程
上一个问题: 我正在使用Swing类在Java上制作一个聊天应用程序。我已经在它的服务器部分完成了多线程部分 注意,服务器应该服务于两个相互聊天的客户端,而不是其中一个作为主机,另一个作为客户端 ChatServer在Java Swing GUI聊天应用程序中创建新线程,java,multithreading,swing,sockets,user-interface,Java,Multithreading,Swing,Sockets,User Interface,上一个问题: 我正在使用Swing类在Java上制作一个聊天应用程序。我已经在它的服务器部分完成了多线程部分 注意,服务器应该服务于两个相互聊天的客户端,而不是其中一个作为主机,另一个作为客户端 ChatServer public class ChatServer { public static void main(String args[]) { final int port = 1337; ServerSocket serverSocket = null; Soc
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();
}
}
}
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();
}
}
}
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);
}
}
}
}
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;
}
}
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也支持多线程
聊天客户端
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
}
我不确定是否应该在ChatClient中对Swing GUI执行与在ChatServer中相同的操作
所以我想知道在GUI窗口上执行多线程的正确方法,尤其是现在它还涉及其他类 我为TCP连接创建了太基本的聊天应用程序。我的代码在三个文件中。一个用于服务器,一个用于客户端,一个用于UI。您可以自己实现UI部分。我只实现了基本功能
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();
}
}
我已经测试过了。它工作得很好。如果你发现任何疑问。请在下面发表评论。祝你度过愉快的一天。你到底需要什么帮助?这个看起来不错。为
ChatClient
创建线程的方式很好。我会将所有字段放在类的顶部(在ChatClient
中,您的字段位于底部)。这将使它更容易阅读和浏览。这也是它的本意。@对于eth11,变量是这样声明的,因为我正在使用NetBean的拖放GUI生成器,所以它在该位置声明变量。实际上,我现在的问题是我可以创建两个聊天窗口,但聊天内容彼此不同步。正如我在中所述,您的代码的一个问题是服务器只处理一个连接。每个客户端连接都需要一个自己的线程来管理它们(可能使用读写线程),该线程将从客户端“读取”消息,可能将它们发送到某种队列,然后“写入”线程将发送给客户端
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();
}
}
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)
{ }
}