【Java】Java多執行緒實現的聊天客戶端和伺服器

widiot1發表於2016-10-14

主要涉及知識

·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();
			}
		}
	}

}

GUI執行截圖


相關文章