【Java】Java多執行緒實現的聊天客戶端和伺服器
阿新 • • 發佈:2018-12-30
主要涉及知識
·Java中GUI程式的編寫,包括事件監聽機制。
·Java的網路通訊程式設計,ServerSocket,Socket類的使用。
·Java中多執行緒的程式設計,Thread類,Runnable介面的使用。
原始碼
客戶端
package project1; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; import javax.swing.*; public class Client { private JFrame clientFrame; private JLabel IPLabel; private JLabel PortLabel; private JLabel sayLabel; private JLabel nicknameLabel; private JTextField IPText; private JTextField PortText; private JTextField nicknameText; private JTextField sayText; private JButton connectButton; private JButton nicknameButton; private JButton sayButton; private JPanel jPanelNorth; private JPanel jPanelSouth0; private JPanel jPanelSouth1; private JPanel jPanelSouth2; private JTextArea clientTextArea; private JScrollPane scroller; private BufferedReader reader; private PrintWriter writer; private String nickname; public static void main(String args[]) { Client aClient = new Client(); aClient.startUp(); } // 初始化元件 public Client() { nickname = "客戶端"; clientFrame = new JFrame(); jPanelNorth = new JPanel(); IPLabel = new JLabel("伺服器IP", JLabel.LEFT); IPText = new JTextField(10); PortLabel = new JLabel("伺服器埠", JLabel.LEFT); PortText = new JTextField(10); connectButton = new JButton("連線"); clientTextArea = new JTextArea(); scroller = new JScrollPane(clientTextArea); jPanelSouth0 = new JPanel(); jPanelSouth1 = new JPanel(); jPanelSouth2 = new JPanel(); nicknameLabel = new JLabel("暱稱", JLabel.LEFT); nicknameText = new JTextField(nickname, 30); nicknameButton = new JButton("確認"); sayLabel = new JLabel("訊息", JLabel.LEFT); sayText = new JTextField(30); sayButton = new JButton("確認"); } // 構建GUI private void buildGUI() { // 視窗的設定 clientFrame.setTitle("客戶端"); clientFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); clientFrame.setSize(550, 550); // 北區的元件 jPanelNorth.add(IPLabel); jPanelNorth.add(IPText); jPanelNorth.add(PortLabel); jPanelNorth.add(PortText); jPanelNorth.add(connectButton); clientFrame.getContentPane().add(BorderLayout.NORTH, jPanelNorth); // 中間的元件 clientTextArea.setFocusable(false); scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); clientFrame.getContentPane().add(BorderLayout.CENTER, scroller); // 南區的元件 jPanelSouth1.add(nicknameLabel); jPanelSouth1.add(nicknameText); jPanelSouth1.add(nicknameButton); jPanelSouth2.add(sayLabel); jPanelSouth2.add(sayText); jPanelSouth2.add(sayButton); jPanelSouth0.setLayout(new BoxLayout(jPanelSouth0, BoxLayout.Y_AXIS)); jPanelSouth0.add(jPanelSouth1); jPanelSouth0.add(jPanelSouth2); clientFrame.getContentPane().add(BorderLayout.SOUTH, jPanelSouth0); // 設定視窗可見 clientFrame.setVisible(true); } // 客戶端執行 public void startUp() { buildGUI(); // 接收伺服器訊息的執行緒 Runnable incomingReader = new Runnable() { @Override public void run() { String message; try { while ((message = reader.readLine()) != null) { clientTextArea.append(message + "\n"); } } catch (Exception ex) { ex.printStackTrace(); } } }; // 監聽Connect按鈕,實現伺服器的連線 connectButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String aServerIP = IPText.getText(); String aServerPort = PortText.getText(); if (aServerIP.equals("") || aServerPort.equals("")) { JOptionPane.showMessageDialog(clientFrame, "請輸入 完整的 IP和埠!"); } else { try { @SuppressWarnings("resource") Socket clientSocket = new Socket(aServerIP, Integer.parseInt(aServerPort)); InputStreamReader streamReader = new InputStreamReader(clientSocket.getInputStream()); reader = new BufferedReader(streamReader); writer = new PrintWriter(clientSocket.getOutputStream()); clientTextArea.append("伺服器已連線...\n"); Thread readerThread = new Thread(incomingReader); readerThread.start(); } catch (Exception ex) { JOptionPane.showMessageDialog(clientFrame, "連線不上伺服器!\n請確認 IP 和 埠 輸入正確。"); } } } }); // 監聽nickname,設定暱稱 ActionListener nicknameListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String aText = nicknameText.getText(); if (!aText.equals("")) { nickname = aText; } } }; nicknameButton.addActionListener(nicknameListener); nicknameText.addActionListener(nicknameListener); nicknameText.addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { } @Override public void focusLost(FocusEvent e) { String aText = nicknameText.getText(); if (!aText.equals("")) { nickname = aText; } } }); // 傳送訊息到伺服器 ActionListener SayListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String aText = sayText.getText(); if (aText.equals("")) { JOptionPane.showMessageDialog(clientFrame, "內容不能為空!"); } else { try { writer.println(nickname + ":" + aText); writer.flush(); } catch (Exception ex) { ex.printStackTrace(); } sayText.setText(""); } } }; sayButton.addActionListener(SayListener); sayText.addActionListener(SayListener); } }
伺服器
package project1; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; import java.util.ArrayList; import java.util.Iterator; import javax.swing.*; public class Server { private JFrame serverFrame; private JLabel portLabel; private JLabel sayLabel; private JLabel nicknameLabel; private JTextField portText; private JTextField sayText; private JTextField nicknameText; private JButton startButton; private JButton sayButton; private JButton nicknameButton; private JPanel jPanelNorth; private JPanel jPanelSouth0; private JPanel jPanelSouth1; private JPanel jPanelSouth2; private JScrollPane scroller; private JTextArea serverTextArea; private ArrayList<PrintWriter> clientOutputStreams; private String nickname; public static void main(String[] args) { Server aServer = new Server(); aServer.startUp(); } // 初始化元件 public Server() { nickname = "伺服器"; serverFrame = new JFrame(); jPanelNorth = new JPanel(); portLabel = new JLabel("埠", JLabel.LEFT); portText = new JTextField(30); startButton = new JButton("開始"); serverTextArea = new JTextArea(); scroller = new JScrollPane(serverTextArea); nicknameLabel = new JLabel("暱稱", JLabel.LEFT); nicknameText = new JTextField(nickname, 30); nicknameButton = new JButton("確認"); jPanelSouth0 = new JPanel(); jPanelSouth1 = new JPanel(); jPanelSouth2 = new JPanel(); sayLabel = new JLabel("訊息", JLabel.LEFT); sayText = new JTextField(30); sayButton = new JButton("確認"); } // 構建GUI private void buildGUI() { // 視窗的設定 serverFrame.setTitle("伺服器"); serverFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); serverFrame.setSize(550, 550); // 北區的元件 jPanelNorth.add(portLabel); jPanelNorth.add(portText); jPanelNorth.add(startButton); serverFrame.getContentPane().add(BorderLayout.NORTH, jPanelNorth); // 中間的元件 serverTextArea.setFocusable(false); scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); serverFrame.getContentPane().add(BorderLayout.CENTER, scroller); // 南區的元件 jPanelSouth1.add(nicknameLabel); jPanelSouth1.add(nicknameText); jPanelSouth1.add(nicknameButton); jPanelSouth2.add(sayLabel); jPanelSouth2.add(sayText); jPanelSouth2.add(sayButton); jPanelSouth0.setLayout(new BoxLayout(jPanelSouth0, BoxLayout.Y_AXIS)); jPanelSouth0.add(jPanelSouth1); jPanelSouth0.add(jPanelSouth2); serverFrame.getContentPane().add(BorderLayout.SOUTH, jPanelSouth0); // 設定視窗可見 serverFrame.setVisible(true); } // 伺服器執行 public void startUp() { buildGUI(); // 監聽Start按鈕,建立埠 ActionListener startListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { clientOutputStreams = new ArrayList<PrintWriter>(); String aPort = portText.getText(); if (aPort.equals("")) { JOptionPane.showMessageDialog(serverFrame, "請輸入正確的埠號!"); } else { try { // 等待客戶端連線的執行緒 Runnable serverRunnable = new Runnable() { @Override public void run() { ServerSocket serverSocket; try { serverSocket = new ServerSocket(Integer.parseInt(aPort)); serverTextArea.append("正在等待客戶端連線...\n"); while (true) { Socket clientSocket = serverSocket.accept(); serverTextArea.append("客戶端已連線...\n"); PrintWriter writer = new PrintWriter(clientSocket.getOutputStream()); clientOutputStreams.add(writer); Thread t = new Thread(new ClientHandler(clientSocket)); t.start(); } } catch (NumberFormatException | IOException e) { e.printStackTrace(); } } }; Thread serverThread = new Thread(serverRunnable); serverThread.start(); } catch (Exception ex) { ex.printStackTrace(); } } } }; startButton.addActionListener(startListener); portText.addActionListener(startListener); // 監聽nickname,設定暱稱 ActionListener nicknameListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String aText = nicknameText.getText(); if (!aText.equals("")) { nickname = aText; } } }; nicknameButton.addActionListener(nicknameListener); nicknameText.addActionListener(nicknameListener); nicknameText.addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { } @Override public void focusLost(FocusEvent e) { String aText = nicknameText.getText(); if (!aText.equals("")) { nickname = aText; } } }); // 監聽Say按鈕,傳送訊息 ActionListener SayListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String aText = sayText.getText(); if (!aText.equals("")) { aText = nickname + ":" + aText; sendToEveryClient(aText); serverTextArea.append(aText + "\n"); sayText.setText(""); } else { JOptionPane.showMessageDialog(serverFrame, "內容不能為空!"); } } }; sayButton.addActionListener(SayListener); sayText.addActionListener(SayListener); } // 多客戶端的執行緒 public class ClientHandler implements Runnable { BufferedReader bReader; Socket aSocket; public ClientHandler(Socket clientSocket) { try { aSocket = clientSocket; InputStreamReader isReader = new InputStreamReader(aSocket.getInputStream()); bReader = new BufferedReader(isReader); } catch (Exception ex) { ex.printStackTrace(); } } @Override public void run() { String message; try { while ((message = bReader.readLine()) != null) { sendToEveryClient(message); serverTextArea.append(message + "\n"); } } catch (Exception ex) { ex.printStackTrace(); } } } // 傳送訊息給所有客戶端的方法 private void sendToEveryClient(String message) { Iterator<PrintWriter> it = clientOutputStreams.iterator(); while (it.hasNext()) { try { PrintWriter writer = (PrintWriter) it.next(); writer.println(message); writer.flush(); } catch (Exception ex) { ex.printStackTrace(); } } } }