基于Socket+Java Swing实现聊天室
服务端
首先要实现群聊,需要先实现一个服务端用于接收客户端发送的消息,再群发给所有客户端,所以我们先讲解服务端如何实现。
public class ChatServer {
private List<ClientHandler> clients = new ArrayList<>();// 创建一个记录在线客户端的列表
public static void main(String[] args) {
System.setProperty("file.encoding", "UTF-8");//设置CMD运行的编码
new ChatServer().start();//启动服务
}
public void start() {
try (ServerSocket serverSocket = new ServerSocket(5000)) {
System.out.println("服务器已启动,端口为: 5000,IP地址为:"+ InetAddress.getLocalHost().getHostAddress()+"(客户端请填写该IP地址或通过命令ipconfig查看本机IP地址)");
while (true) { //循环等待接受客户端连接,添加进列表中
Socket socket = serverSocket.accept();
ClientHandler clientHandler = new ClientHandler(socket);
clients.add(clientHandler);
clientHandler.start();
}
} catch (IOException ex) {
System.out.println("错误: " + ex.getMessage());
}
}
public void broadcast(String message, ClientHandler sender) {// 将消息广播至所有客户端
for (ClientHandler client : clients) {
if (client != sender) {// 判断如果是发送者则不发送
client.sendMessage(message);
}
}
}
public void broadcastOnlineUsers(String status,List<String> userList, ClientHandler sender) {// 广播在线用户列表
for (ClientHandler client : clients) {
client.sendMessage(status+"::"+userList.toString());
}
}
public void removeClient(ClientHandler clientHandler) {
clients.remove(clientHandler);// 移除客户端
}
class ClientHandler extends Thread {
private Socket socket;
private String userName;
private BufferedReader reader;
private PrintWriter writer;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public String toString() {
return "ClientHandler{" +
"socket=" + socket +
", userName='" + userName + '\'' +
", reader=" + reader +
", writer=" + writer +
'}';
}
public ClientHandler(Socket socket) {
this.socket = socket;
}
public void run() {
try {
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
userName = reader.readLine();
writer.println("["+LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm")) + "] " + userName + " 加入聊天室!");
writer.println("欢迎加入聊天室,当前在线人数:" + clients.size());
broadcast(userName + " 加入聊天室!", this);
List<String> list = clients.stream().map(l -> l.getUserName()).collect(Collectors.toList());
broadcastOnlineUsers("JOIN",list, this);
System.out.println("["+LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm")) + "] " + userName + " 加入聊天室!");
// Start reading messages from the client
String message;
while ((message = reader.readLine()) != null) {
broadcast("["+LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm")) + "] " + userName + ": " + message, this);
System.out.println("["+LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm")) + "] " + userName + ": " + message);
}
} catch (IOException ex) {
System.out.println("Error: " + ex.getMessage());
} finally {
try {
socket.close();
removeClient(this);
broadcast("["+LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm")) + "] " + userName + " 已离开.当前聊天室在线人数:" + clients.size(), this);
List<String> list = clients.stream().map(l -> l.getUserName()).collect(Collectors.toList());
broadcastOnlineUsers("QUIT",list, this);
System.out.println(userName + " 离开聊天室!");
} catch (IOException ex) {
System.out.println("错误: " + ex.getMessage());
}
}
}
public void sendMessage(String message) {
writer.println(message);
}
}
}
首先启动Socket服务,然后循环等待客户端的连接,在客户端发送消息时,进行广播该消息,当有新的客户端连接时,则广播一次在线用户列表实时更新,有客户端退出时同理。
当有客户端连接时会同步输出日志
客户端
客户端是聊天时使用的,通过客户端连接服务端进行在线聊天,直接看代码
import javax.swing.*;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
public class ChatClient extends JFrame {
private JTextArea messageArea;
private JTextField inputField;
private JButton sendButton;
private String userName;
private Socket socket;
private BufferedReader in;
private PrintWriter out;
private DefaultListModel<String> userListModel;
private JList<String> userList;
private JScrollPane userListScrollPane;
JScrollPane scrollPane;
// 构造函数设计UI
public ChatClient(String title) {
super(title);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
messageArea = new JTextArea();
messageArea.setEditable(false);
JPanel chatPanel = new JPanel(new BorderLayout());
chatPanel.add(messageArea, BorderLayout.CENTER);
chatPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
scrollPane = new JScrollPane(chatPanel);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
// 设置聊天框的背景和字体颜色
chatPanel.setBackground(Color.WHITE);
chatPanel.setForeground(Color.BLACK);
add(scrollPane, BorderLayout.CENTER);
// 定义颜色和字体样式
SimpleAttributeSet selfStyle = new SimpleAttributeSet();
StyleConstants.setForeground(selfStyle, Color.BLUE);
StyleConstants.setBold(selfStyle, true);
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(new BorderLayout());
inputField = new JTextField();
sendButton = new JButton("发送");
sendButton.setEnabled(false);
bottomPanel.add(inputField, BorderLayout.CENTER);
bottomPanel.add(sendButton, BorderLayout.EAST);
add(bottomPanel, BorderLayout.SOUTH);
userListModel = new DefaultListModel<String>();
userList = new JList<String>(userListModel);
userListScrollPane = new JScrollPane(userList);
// 将userScrollPane和标题放置在userPanel中
JPanel userPanel = new JPanel(new BorderLayout());
JLabel userListTitleLabel = new JLabel("在线用户列表");
JPanel userListPanel = new JPanel(new BorderLayout());
userListPanel.add(userListTitleLabel, BorderLayout.NORTH);
userListPanel.add(userListScrollPane, BorderLayout.CENTER);
userPanel.add(userListPanel, BorderLayout.CENTER);
// Set the dimensions of the list
Dimension userListScrollPaneDimension = new Dimension(200, 400);
userListScrollPane.setPreferredSize(userListScrollPaneDimension);
userListScrollPane.setMinimumSize(userListScrollPaneDimension);
// Add the user list to the GUI
add(userPanel, BorderLayout.EAST);
inputField.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER && sendButton.isEnabled()) {
sendMessage();
}
}
});
sendButton.addActionListener(e -> sendMessage());
String serverAddress = JOptionPane.showInputDialog(this, "请输入服务器地址:", "127.0.0.1");//方便测试直接设置默认值
String userName = JOptionPane.showInputDialog(this, "请输入用户名:", "用户" + (int) (Math.random() * 1000));// 自动生成名称
connect(serverAddress, userName);
}
private void connect(String serverAddress, String userName) {
try {
socket = new Socket(serverAddress, 5000);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
out.println(userName);
this.userName = userName;
setTitle("聊天室 - " + userName);
sendButton.setEnabled(true);
inputField.requestFocus();
new Thread(this::receiveMessage).start();
} catch (IOException e) {
JOptionPane.showMessageDialog(this, "连接服务器失败!");
System.exit(1);
}
}
private void sendMessage() {
String message = inputField.getText().trim();
if (!message.isEmpty()) {
out.println(message);
messageArea.append("["+LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm")) + "] " + userName + ":" + message + "\n");
inputField.setText("");
}
inputField.requestFocus();
// 获取聊天框对应的垂直滚动条
JScrollBar verticalScrollBar = scrollPane.getVerticalScrollBar();
// 将滚动条滚动到最底部
verticalScrollBar.setValue(verticalScrollBar.getMaximum());
}
private void receiveMessage() {
try {
while (true) {
String message = in.readLine();
if (message.startsWith("JOIN::")) {
userListModel.clear();
message = message.replace("JOIN::", "");
ArrayList<String> arrayList = new ArrayList<>(Arrays.asList(message.substring(1, message.length() - 1).split(", ")));
for (String user : arrayList) {
if (userName.equals(user)) {
userListModel.addElement(user + "(我)");
} else {
userListModel.addElement(user);
}
}
} else if (message.startsWith("QUIT::")) {
userListModel.clear();
message = message.replace("QUIT::", "");
ArrayList<String> arrayList = new ArrayList<>(Arrays.asList(message.substring(1, message.length() - 1).split(", ")));
for (String user : arrayList) {
userListModel.addElement(user);
}
} else {
messageArea.append(message + "\n");
// 获取聊天框对应的垂直滚动条
JScrollBar verticalScrollBar = scrollPane.getVerticalScrollBar();
// 将滚动条滚动到最底部
verticalScrollBar.setValue(verticalScrollBar.getMaximum());
}
}
} catch (IOException e) {
JOptionPane.showMessageDialog(this, "与服务器连接断开!");
System.exit(1);
}
}
public static void main(String[] args) {
System.setProperty("file.encoding", "UTF-8");
ChatClient client = new ChatClient("聊天室");
client.setSize(600, 400);
client.setLocationRelativeTo(null);
client.setVisible(true);
}
}