在Java Swing GUI Chat App中创建一个新线程

时间:2017-05-24 15:57:23

标签: java multithreading swing sockets user-interface

上一个问题: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窗口上进行多线程的正确方法,特别是它现在也涉及其他类。

1 个答案:

答案 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)
{ }
}

我测试了它。它完美地运作。如果您发现任何查询。请在下面评论。祝你有愉快的一天。