Java多線程實(shí)現(xiàn)聊天客戶端和服務(wù)器
本文實(shí)例為大家分享了java聊天室代碼,供大家參考,具體內(nèi)容如下
主要涉及知識(shí)
- Java中GUI程序的編寫,包括事件監(jiān)聽(tīng)機(jī)制。
- Java的網(wǎng)絡(luò)通信編程,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("服務(wù)器IP", JLabel.LEFT);
IPText = new JTextField(10);
PortLabel = new JLabel("服務(wù)器端口", 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("確認(rèn)");
sayLabel = new JLabel("消息", JLabel.LEFT);
sayText = new JTextField(30);
sayButton = new JButton("確認(rèn)");
}
// 構(gòu)建GUI
private void buildGUI() {
// 窗口的設(shè)置
clientFrame.setTitle("客戶端");
clientFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
clientFrame.setSize(550, 550);
// 北區(qū)的組件
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);
// 南區(qū)的組件
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);
// 設(shè)置窗口可見(jiàn)
clientFrame.setVisible(true);
}
// 客戶端運(yùn)行
public void startUp() {
buildGUI();
// 接收服務(wù)器消息的線程
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();
}
}
};
// 監(jiān)聽(tīng)Connect按鈕,實(shí)現(xiàn)服務(wù)器的連接
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, "請(qǐng)輸入 完整的 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("服務(wù)器已連接...\n");
Thread readerThread = new Thread(incomingReader);
readerThread.start();
} catch (Exception ex) {
JOptionPane.showMessageDialog(clientFrame, "連接不上服務(wù)器!\n請(qǐng)確認(rèn) IP 和 端口 輸入正確。");
}
}
}
});
// 監(jiān)聽(tīng)nickname,設(shè)置昵稱
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;
}
}
});
// 發(fā)送消息到服務(wù)器
ActionListener SayListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String aText = sayText.getText();
if (aText.equals("")) {
JOptionPane.showMessageDialog(clientFrame, "內(nèi)容不能為空!");
} else {
try {
writer.println(nickname + ":" + aText);
writer.flush();
} catch (Exception ex) {
ex.printStackTrace();
}
sayText.setText("");
}
}
};
sayButton.addActionListener(SayListener);
sayText.addActionListener(SayListener);
}
}
服務(wù)器
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 = "服務(wù)器";
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("確認(rèn)");
jPanelSouth0 = new JPanel();
jPanelSouth1 = new JPanel();
jPanelSouth2 = new JPanel();
sayLabel = new JLabel("消息", JLabel.LEFT);
sayText = new JTextField(30);
sayButton = new JButton("確認(rèn)");
}
// 構(gòu)建GUI
private void buildGUI() {
// 窗口的設(shè)置
serverFrame.setTitle("服務(wù)器");
serverFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
serverFrame.setSize(550, 550);
// 北區(qū)的組件
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);
// 南區(qū)的組件
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);
// 設(shè)置窗口可見(jiàn)
serverFrame.setVisible(true);
}
// 服務(wù)器運(yùn)行
public void startUp() {
buildGUI();
// 監(jiān)聽(tīng)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, "請(qǐng)輸入正確的端口號(hào)!");
} 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);
// 監(jiān)聽(tīng)nickname,設(shè)置昵稱
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;
}
}
});
// 監(jiān)聽(tīng)Say按鈕,發(fā)送消息
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, "內(nèi)容不能為空!");
}
}
};
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();
}
}
}
// 發(fā)送消息給所有客戶端的方法
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運(yùn)行截圖:

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot啟動(dòng)后立即執(zhí)行的幾種方法小結(jié)
在項(xiàng)目開發(fā)中某些場(chǎng)景必須要用到啟動(dòng)項(xiàng)目后立即執(zhí)行方式的功能,本文主要介紹了SpringBoot啟動(dòng)后立即執(zhí)行的幾種方法小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下2023-05-05
SpringBoot3.4.0無(wú)法找到StringRedisTemplate?bean的問(wèn)題Consider?def
本文主要介紹了SpringBoot3.4.0無(wú)法找到StringRedisTemplate?bean的問(wèn)題Consider?defining?a?bean?of?type?‘org.springframework,具有一定的參考價(jià)值,感興趣的可以了解一下2025-03-03
web項(xiàng)目WEB-INF下沒(méi)有web.xml的解決方法
新手如果在web項(xiàng)目創(chuàng)建后WEB-INF下面沒(méi)有出現(xiàn)web.xml,怎么辦?別慌,沒(méi)有web.xml文件的原因是因?yàn)樵趧?chuàng)建web項(xiàng)目的時(shí)候沒(méi)有把創(chuàng)建web.xml勾上。這篇文章主要介紹了web項(xiàng)目WEB-INF下沒(méi)有web.xml的解決方法,需要的朋友可以參考下2022-12-12
教你利用springboot集成swagger并生成接口文檔
有很多小伙伴不會(huì)利用springboot集成swagger并生成接口文檔,今天特地整理了這篇文章,文中有非常詳細(xì)的代碼圖文介紹及代碼示例,對(duì)不會(huì)這個(gè)方法的小伙伴們很有幫助,需要的朋友可以參考下2021-05-05
Spring Boot LocalDateTime格式化處理的示例詳解
這篇文章主要介紹了Spring Boot LocalDateTime格式化處理的示例詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-10-10
SpringBoot整合Lettuce redis過(guò)程解析
這篇文章主要介紹了SpringBoot整合Lettuce redis過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
Java調(diào)用第三方接口封裝實(shí)現(xiàn)
本文主要介紹了Java調(diào)用第三方接口封裝實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
SpringBoot整合SpringSecurityOauth2實(shí)現(xiàn)鑒權(quán)動(dòng)態(tài)權(quán)限問(wèn)題
這篇文章主要介紹了SpringBoot整合SpringSecurityOauth2實(shí)現(xiàn)鑒權(quán)-動(dòng)態(tài)權(quán)限,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06

