上一个问题:Java Swing GUI Client and Server Chat App TextArea not updating
我正在使用Swing Class在Java上创建一个聊天应用程序。我在服务器部分完成了多线程部分。
请注意,服务器应该为两个客户端提供服务,而不是其中一个作为主机,其中一个作为客户端。
的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支持多线程。
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的方式。
所以我想知道在GUI窗口上进行多线程的正确方法,特别是它现在也涉及其他类。
答案 0 :(得分:0)
我为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();
}
}
服务器将根据客户要求创建线程。
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)
{ }
}
我测试了它。它完美地运作。如果您发现任何查询。请在下面评论。祝你有愉快的一天。