Java Netty‑Based Chat Application with Swing UI and MySQL Integration
This article details the design and implementation of a Java chat system that uses Netty for server‑side networking, Swing for the client UI, and MySQL with C3P0 for data persistence, covering features such as user authentication, friend management, single‑chat messaging, and online status detection.
1. Function Implementation
Implemented features include password, nickname, and signature modification; adding and deleting friends; single‑chat messaging; and determining whether a friend is online.
2. Module Division
The project is divided into server‑side controllers, client handlers, UI frames, and utility classes. (The original diagram is omitted for brevity.)
3. Knowledge Used
Netty
Swing
SynchronousQueue
MySQL CRUD operations
C3P0 connection pool
JSON string handling
4. Partial Code Implementation
nettyController.java
package chat.Project.controller;
import chat.Project.bean.information;
import chat.Project.constant.EnMsgType;
import chat.Project.dao.*;
import chat.utils.CacheUtil;
import chat.utils.JsonUtils;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.netty.channel.Channel;
import java.util.ArrayList;
import java.util.Iterator;
public class NettyController {
private static UserDao userDao = new UserDaoImpl();
private static informationDao informationDao = new informationDaoImpl();
private static friendDao friendDao = new friendDaoImpl();
public static String processing(String message, Channel channel) {
// Parse client message
ObjectNode jsonNodes = JsonUtils.getObjectNode(message);
String msgtype = jsonNodes.get("msgtype").asText();
if (EnMsgType.EN_MSG_LOGIN.toString().equals(msgtype)) {
return loginOperation(jsonNodes, channel);
} else if (EnMsgType.EN_MSG_MODIFY_SIGNATURE.toString().equals(msgtype)) {
return modifySignature(jsonNodes);
} else if (EnMsgType.EN_MSG_MODIFY_NICKNAME.toString().equals(msgtype)) {
return modifyNickname(jsonNodes);
} else if (EnMsgType.EN_MSG_GETINFORMATION.toString().equals(msgtype)) {
return getInformation(jsonNodes);
} else if (EnMsgType.EN_MSG_VERIFY_PASSWORD.toString().equals(msgtype)) {
return verifyPasswd(jsonNodes);
} else if (EnMsgType.EN_MSG_CHAT.toString().equals(msgtype)) {
return SingleChat(jsonNodes);
} else if (EnMsgType.EN_MSG_GET_ID.toString().equals(msgtype)) {
return getId(jsonNodes);
} else if (EnMsgType.EN_MSG_GET_FRIEND.toString().equals(msgtype)) {
return getFriend(jsonNodes);
} else if (EnMsgType.EN_MSG_ADD_FRIEND.toString().equals(msgtype)) {
return addFriends(jsonNodes);
} else if (EnMsgType.EN_MSG_DEL_FRIEND.toString().equals(msgtype)) {
return delFriend(jsonNodes);
} else if (EnMsgType.EN_MSG_ACTIVE_STATE.toString().equals(msgtype)) {
return friendIsActive(jsonNodes);
}
return "";
}
// Determine friend online status
private static String friendIsActive(ObjectNode jsonNodes) {
int friendId = jsonNodes.get("friendId").asInt();
ObjectNode objectNode = JsonUtils.getObjectNode();
objectNode.put("msgtype", EnMsgType.EN_MSG_ACK.toString());
objectNode.put("srctype", EnMsgType.EN_MSG_ACTIVE_STATE.toString());
Channel channel = CacheUtil.get(friendId);
if (channel == null) {
objectNode.put("code", 200);
} else {
objectNode.put("code", 300);
}
return objectNode.toString();
}
// Delete friend
private static String delFriend(ObjectNode jsonNodes) {
Integer friendId = jsonNodes.get("friendId").asInt();
int userId = jsonNodes.get("id").asInt();
String localName = jsonNodes.get("localName").asText();
ObjectNode objectNode = JsonUtils.getObjectNode();
objectNode.put("msgtype", EnMsgType.EN_MSG_ACK.toString());
objectNode.put("srctype", EnMsgType.EN_MSG_DEL_FRIEND.toString());
objectNode.put("code", 200);
information information = informationDao.getInformation(friendId);
String friendName = information.getNickname();
boolean exist = friendDao.isExist(friendName, userId);
if (exist) {
friendDao.delFriend(userId, friendName);
friendDao.delFriend(friendId, localName);
objectNode.put("code", 300);
}
return objectNode.toString();
}
// Add friend
private static String addFriends(ObjectNode jsonNodes) {
Integer friendId = jsonNodes.get("friendId").asInt();
int userId = jsonNodes.get("id").asInt();
String localName = jsonNodes.get("localName").asText();
boolean exists = userDao.verifyExistFriend(friendId);
ObjectNode objectNode = JsonUtils.getObjectNode();
objectNode.put("msgtype", EnMsgType.EN_MSG_ACK.toString());
objectNode.put("srctype", EnMsgType.EN_MSG_ADD_FRIEND.toString());
objectNode.put("code", 200);
if (exists) {
objectNode.put("code", 300);
information information = informationDao.getInformation(friendId);
String friendNickname = information.getNickname();
friendDao.addFriends(userId, localName, friendNickname);
friendDao.addFriends(friendId, friendNickname, localName);
}
return objectNode.toString();
}
// Get friend list
private static String getFriend(ObjectNode jsonNodes) {
int uid = jsonNodes.get("uid").asInt();
ArrayList
friends = friendDao.getFriends(uid);
ObjectNode objectNode = JsonUtils.getObjectNode();
objectNode.put("msgtype", EnMsgType.EN_MSG_ACK.toString());
objectNode.put("srctype", EnMsgType.EN_MSG_GET_FRIEND.toString());
int i = 0;
for (String f : friends) {
objectNode.put("res" + i, f);
i++;
}
objectNode.put("count", i);
return objectNode.toString();
}
// Get user ID by nickname
private static String getId(ObjectNode jsonNodes) {
String nickname = jsonNodes.get("nickname").asText();
information information = informationDao.nicknameGetId(nickname);
int uid = information.getUid();
ObjectNode objectNode = JsonUtils.getObjectNode();
objectNode.put("msgtype", EnMsgType.EN_MSG_ACK.toString());
objectNode.put("srctype", EnMsgType.EN_MSG_GET_ID.toString());
objectNode.put("uid", uid);
return objectNode.toString();
}
// Single‑chat handling
private static String SingleChat(ObjectNode jsonNodes) {
int id = jsonNodes.get("id").asInt();
ObjectNode objectNode = JsonUtils.getObjectNode();
objectNode.put("msgtype", EnMsgType.EN_MSG_ACK.toString());
objectNode.put("srctype", EnMsgType.EN_MSG_CHAT.toString());
Channel channel = CacheUtil.get(id);
if (channel == null) {
objectNode.put("code", 200);
} else {
objectNode.put("code", 300);
channel.writeAndFlush(jsonNodes.toString());
}
return objectNode.toString();
}
// Verify and change password
private static String verifyPasswd(ObjectNode jsonNodes) {
int id = jsonNodes.get("id").asInt();
String oldPasswd = jsonNodes.get("oldPasswd").asText();
String newPasswd = jsonNodes.get("newPasswd").asText();
boolean exists = userDao.verifyPassword(oldPasswd, id);
ObjectNode objectNode = JsonUtils.getObjectNode();
objectNode.put("msgtype", EnMsgType.EN_MSG_VERIFY_PASSWORD.toString());
objectNode.put("code", 200);
if (exists) {
userDao.modifyPasswd(newPasswd, id);
objectNode.put("code", 300);
}
return objectNode.toString();
}
// Get user information
private static String getInformation(ObjectNode jsonNodes) {
int id = jsonNodes.get("id").asInt();
information information = informationDao.getInformation(id);
ObjectNode objectNode = JsonUtils.getObjectNode();
objectNode.put("msgtype", EnMsgType.EN_MSG_ACK.toString());
objectNode.put("srctype", EnMsgType.EN_MSG_GETINFORMATION.toString());
objectNode.put("Nickname", information.getNickname());
objectNode.put("Signature", information.getSignature());
return objectNode.toString();
}
// Modify nickname
private static String modifyNickname(ObjectNode jsonNodes) {
int id = jsonNodes.get("id").asInt();
String nickname = jsonNodes.get("nickname").asText();
informationDao.storeNickname(nickname, id);
return "";
}
// Modify signature
private static String modifySignature(ObjectNode jsonNodes) {
int id = jsonNodes.get("id").asInt();
String signature = jsonNodes.get("signature").asText();
informationDao.storeSignature(signature, id);
return "";
}
// Login operation
private static String loginOperation(ObjectNode objectNode, Channel channel) {
int id = objectNode.get("id").asInt();
String passwd = objectNode.get("passwd").asText();
boolean exists = userDao.getInformation(id, passwd);
ObjectNode jsonNodes = JsonUtils.getObjectNode();
jsonNodes.put("msgtype", EnMsgType.EN_MSG_ACK.toString());
jsonNodes.put("srctype", EnMsgType.EN_MSG_LOGIN.toString());
jsonNodes.put("code", 300);
if (exists) {
jsonNodes.put("code", 200);
CacheUtil.put(id, channel);
}
return jsonNodes.toString();
}
}ClientHandler.java
package chat.Project.netty;
import chat.Frame.chat.ChatFrame;
import chat.Frame.chat.linkmen;
import chat.Frame.chat.login;
import chat.Project.constant.EnMsgType;
import chat.util.JsonUtils;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.concurrent.SynchronousQueue;
public class ClientHandler extends SimpleChannelInboundHandler
{
public static SynchronousQueue
queue = new SynchronousQueue<>();
public static String Nickname;
public String Signature;
@Override
protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
// Not used
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(msg);
ObjectNode jsonNodes = JsonUtils.getObjectNode((String) msg);
String msgtype = jsonNodes.get("msgtype").asText();
if (EnMsgType.EN_MSG_ACK.toString().equals(msgtype)) {
String srctype = jsonNodes.get("srctype").asText();
if (EnMsgType.EN_MSG_LOGIN.toString().equals(srctype)) {
queue.offer(jsonNodes.get("code").asInt());
} else if (EnMsgType.EN_MSG_GETINFORMATION.toString().equals(srctype)) {
Nickname = jsonNodes.get("Nickname").asText();
Signature = jsonNodes.get("Signature").asText();
linkmen.label_1.setText(Nickname);
linkmen.field.setText(Signature);
} else if (EnMsgType.EN_MSG_CHAT.toString().equals(srctype)) {
queue.offer(jsonNodes.get("code").asInt());
} else if (EnMsgType.EN_MSG_GET_ID.toString().equals(srctype)) {
queue.offer(jsonNodes.get("uid").asInt());
} else if (EnMsgType.EN_MSG_GET_FRIEND.toString().equals(srctype)) {
int count = jsonNodes.get("count").asInt();
login.friend = new String[count];
for (int i = 0; i < count; i++) {
login.friend[i] = jsonNodes.get("res" + i).asText();
System.out.println(jsonNodes.get("res" + i));
}
} else if (EnMsgType.EN_MSG_ADD_FRIEND.toString().equals(srctype)) {
queue.offer(jsonNodes.get("code").asInt());
} else if (EnMsgType.EN_MSG_DEL_FRIEND.toString().equals(srctype)) {
queue.offer(jsonNodes.get("code").asInt());
} else if (EnMsgType.EN_MSG_ACTIVE_STATE.toString().equals(srctype)) {
queue.offer(jsonNodes.get("code").asInt());
}
} else if (EnMsgType.EN_MSG_VERIFY_PASSWORD.toString().equals(msgtype)) {
queue.offer(jsonNodes.get("code").asInt());
} else if (EnMsgType.EN_MSG_CHAT.toString().equals(msgtype)) {
String message = " " + jsonNodes.get("message").asText();
ChatFrame.sb.append(message + "\n");
ChatFrame.displayTextPanel.setText(ChatFrame.sb.toString());
}
}
}linkmen.java
package chat.Frame.chat;
import chat.Frame.operation.alterColumn.changeNickname;
import chat.Frame.operation.alterColumn.changePassword;
import chat.Frame.operation.alterColumn.changeSignature;
import chat.Frame.operation.friendHandle.addFriend;
import chat.Frame.operation.friendHandle.delFriend;
import chat.Frame.tipFrame;
import chat.Project.services.sendServers;
import io.netty.channel.Channel;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
public class linkmen extends JFrame {
private JFrame frame;
private JLabel label_2, label_3, label_4, label;
public static JLabel label_1;
private JComboBox box, box_1, box_2;
private ImageIcon icon_1, icon;
private JTextField field_1;
public static JTextField field;
private JPanel panel_1, panel_3, panel;
public JScrollPane panel_2;
public static JList
list;
private Channel channel;
private Integer id;
public static JLabel label_5, label_6;
private String[] fd;
public static DefaultListModel
model;
public linkmen(Integer id, Channel channel, String[] fd) {
this.id = id;
this.channel = channel;
this.fd = fd;
}
public void init() {
// Initialize UI components (panels, labels, list, combo boxes, etc.)
// ... (omitted for brevity)
}
public void mian() {
model = new DefaultListModel<>();
for (int i = 0; i < fd.length; i++) {
model.addElement(fd[i]);
}
init();
new sendServers(channel).update(id);
label_1.setFont(new Font("宋体", Font.PLAIN, 18));
field.setFont(new Font("宋体", Font.PLAIN, 18));
}
private static boolean judgeDigit(String string) {
for (int i = 0; i < string.length(); i++) {
if (!Character.isDigit(string.charAt(i))) {
return false;
}
}
return true;
}
}tipFrame.java
package chat.Frame;
import chat.Frame.chat.linkmen;
import chat.Frame.operation.alterColumn.changeNickname;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class tipFrame extends JDialog {
private Container container;
public JLabel label;
private JButton button;
public tipFrame() {}
public void init(String msg) {
container = getContentPane();
label = new JLabel(msg);
label.setBounds(70, 0, 200, 70);
label.setFont(new Font("微软雅黑", Font.PLAIN, 20));
container.add(label);
button = new JButton("确认");
button.setBounds(35, 50, 140, 40);
container.add(button);
setBounds(780, 170, 220, 140);
setLayout(null);
setVisible(true);
container.setBackground(new Color(0xD8FFD5));
setAlwaysOnTop(true);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
tipFrame.this.dispose();
}
});
}
}5. Runtime Screenshots
The article includes screenshots of the login window, contacts list, chat window, and various operation flows, demonstrating that all implemented features are functional.
Overall, the project showcases a complete Java‑based instant messaging client and server, illustrating the integration of Netty networking, Swing UI, MySQL persistence, and JSON message handling.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.