介绍
实现图形界面的多人聊天程序(UDP)
➢采用图形界面进行交互
➢通过组播维护聊天室成员(加入或退出)
➢支持发送消息给聊天室内所有成员
➢支持发送消息给聊天室内指定成员
实现效果
登陆界面
如果不输入用户名,用户名自动设置为未知
三个窗口分别来自三个成员,加入顺序为:aa、cc、bb,三个用户之间可以互相发送私信,也可以在聊天室群聊。
完整代码
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.util.HashSet;
import java.util.Set;
public class UDPChat {
private static final int PORT = 12345;
private static final String MULTICAST_ADDRESS = "230.0.0.1"; // Multicast address for group communication
private static Set<String> users = new HashSet<>();
private static DefaultListModel<String> userListModel = new DefaultListModel<>();
public static void main(String[] args) {
// Prompt for username
String username = JOptionPane.showInputDialog(null, "Enter your username:", "Username", JOptionPane.PLAIN_MESSAGE);
if (username == null || username.trim().isEmpty()) {
username = "未知";
}
// 创建主框架
JFrame frame = new JFrame("ChatBox - " + username);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 400);
// 设置布局
frame.setLayout(new BorderLayout());
JTextArea messageDisplayArea = new JTextArea();
messageDisplayArea.setEditable(false);
JScrollPane scrollPane = new JScrollPane(messageDisplayArea);
JPanel userInfoPanel = new JPanel();
userInfoPanel.setLayout(new BorderLayout());
JLabel userInfoLabel = new JLabel("用户:");
JList<String> userList = new JList<>(userListModel);
userInfoPanel.add(userInfoLabel, BorderLayout.NORTH);
userInfoPanel.add(new JScrollPane(userList), BorderLayout.CENTER);
JPanel messageSendPanel = new JPanel();
messageSendPanel.setLayout(new BorderLayout());
JTextField messageTextField = new JTextField();
JButton sendButton = new JButton("Send");
JButton sendPrivateButton = new JButton("私信");
messageSendPanel.add(messageTextField, BorderLayout.CENTER);
messageSendPanel.add(sendButton, BorderLayout.EAST);
messageSendPanel.add(sendPrivateButton, BorderLayout.WEST);
// 添加组件到框架
frame.add(scrollPane, BorderLayout.CENTER);
frame.add(userInfoPanel, BorderLayout.EAST);
frame.add(messageSendPanel, BorderLayout.SOUTH);
// 创建UDP处理器
UDPHandler udpHandler = new UDPHandler(messageDisplayArea, username);
new Thread(udpHandler).start();
// 发送按钮的监听器
String finalUsername = username;
sendButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String message = messageTextField.getText();
if (!message.isEmpty()) {
messageDisplayArea.append(finalUsername + ": " + message + "\n");
udpHandler.sendMessage(message);
messageTextField.setText("");
}
}
});
// 发送私信按钮的监听器
sendPrivateButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String message = messageTextField.getText();
String recipient = userList.getSelectedValue();
if (recipient != null && !message.isEmpty()) {
messageDisplayArea.append("发送私信至 " + recipient + ": " + message + "\n");
udpHandler.sendPrivateMessage(message, recipient);
messageTextField.setText("");
}
}
});
// 添加窗口监听器以处理窗口关闭事件
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
udpHandler.sendLeaveMessage();
super.windowClosing(e);
}
});
// 设置主框架可见
frame.setVisible(true);
// Notify others of new user
udpHandler.sendJoinMessage();
}
public static void updateUserList(String username) {
if (users.add(username)) {
SwingUtilities.invokeLater(() -> {
userListModel.addElement(username);
});
}
}
public static void removeUser(String username) {
if (users.remove(username)) {
SwingUtilities.invokeLater(() -> {
userListModel.removeElement(username);
});
}
}
public static void broadcastUserList(MulticastSocket socket) {
try {
InetAddress group = InetAddress.getByName(MULTICAST_ADDRESS);
StringBuilder userListMessage = new StringBuilder("USERLIST:");
for (String user : users) {
userListMessage.append(user).append(",");
}
byte[] buffer = userListMessage.toString().getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, PORT);
socket.send(packet);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class UDPHandler implements Runnable {
private static final int BUFFER_SIZE = 1024;
private MulticastSocket socket;
private DatagramSocket privateSocket;
private JTextArea messageDisplayArea;
private String username;
private static final int PORT = 12345;
private static final String MULTICAST_ADDRESS = "230.0.0.1";
public UDPHandler(JTextArea messageDisplayArea, String username) {
this.messageDisplayArea = messageDisplayArea;
this.username = username;
try {
socket = new MulticastSocket(PORT);
InetAddress group = InetAddress.getByName(MULTICAST_ADDRESS);
socket.joinGroup(group);
privateSocket = new DatagramSocket();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
byte[] buffer = new byte[BUFFER_SIZE];
while (true) {
try {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
String receivedMessage = new String(packet.getData(), 0, packet.getLength());
handleMessage(receivedMessage);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void handleMessage(String message) {
if (message.startsWith("JOIN:")) {
String newUser = message.substring(5);
UDPChat.updateUserList(newUser);
SwingUtilities.invokeLater(() -> messageDisplayArea.append(newUser + " 加入群聊.\n"));
UDPChat.broadcastUserList(socket);
} else if (message.startsWith("LEAVE:")) {
String leavingUser = message.substring(6);
UDPChat.removeUser(leavingUser);
SwingUtilities.invokeLater(() -> messageDisplayArea.append(leavingUser + " 离开群聊.\n"));
UDPChat.broadcastUserList(socket);
} else if (message.startsWith("USERLIST:")) {
String[] userArray = message.substring(9).split(",");
for (String user : userArray) {
if (!user.isEmpty()) {
UDPChat.updateUserList(user);
}
}
} else if (message.startsWith("PRIVATE:")) {
String[] parts = message.split(":", 4);
String toUser = parts[1];
String fromUser = parts[2];
String privateMessage = parts[3];
if (toUser.equals(username)) {
SwingUtilities.invokeLater(() -> messageDisplayArea.append("私信来自" + fromUser + ": " + privateMessage + "\n"));
}
} else {
SwingUtilities.invokeLater(() -> {
if (!message.startsWith(username + ":")) {
messageDisplayArea.append(message + "\n");
}
});
}
}
public void sendMessage(String message) {
try {
InetAddress group = InetAddress.getByName(MULTICAST_ADDRESS);
String fullMessage = username + ": " + message;
byte[] buffer = fullMessage.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, PORT);
socket.send(packet);
} catch (Exception e) {
e.printStackTrace();
}
}
public void sendPrivateMessage(String message, String recipient) {
try {
InetAddress group = InetAddress.getByName(MULTICAST_ADDRESS);
String fullMessage = "PRIVATE:" + recipient + ":" + username + ":" + message;
byte[] buffer = fullMessage.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, PORT);
privateSocket.send(packet);
} catch (Exception e) {
e.printStackTrace();
}
}
public void sendJoinMessage() {
try {
InetAddress group = InetAddress.getByName(MULTICAST_ADDRESS);
String joinMessage = "JOIN:" + username;
byte[] buffer = joinMessage.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, PORT);
socket.send(packet);
UDPChat.updateUserList(username);
UDPChat.broadcastUserList(socket);
} catch (Exception e) {
e.printStackTrace();
}
}
public void sendLeaveMessage() {
try {
InetAddress group = InetAddress.getByName(MULTICAST_ADDRESS);
String leaveMessage = "LEAVE:" + username;
byte[] buffer = leaveMessage.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, PORT);
socket.send(packet);
UDPChat.removeUser(username);
UDPChat.broadcastUserList(socket);
} catch (Exception e) {
e.printStackTrace();
}
}
}