记录一下使用JavaFX + Netty开发仿微信聊天程序 --- 米虫IM。
功能需求
米虫IM已经完成的功能有:
- 用户注册功能
- 用户登录功能
- 搜索好友功能
- 添加好友功能
- 文本聊天功能
- 离线消息功能
架构设计
米虫IM,主要划分为界面层,通讯层和基础层,界面采用JavaFX开发,可独立运行调试,通讯层采用Netty开发,采用C/S架构,分为服务端和客户端。
单机演示
单机版不需要运行服务端,单纯为JavaFX程序,可以独立运行,调试:
单机调试只需要实现通讯接口即可进行测试、调试:
public interface Caller { void register(String nickname, String username, String password); void login(String username, String password); void fetchChatList(); void fetchFriendList(); void readMessage(long friendId); void chat(long friendId, String message); void queryFriend(String nickname); void addFriend(long friendId); void applyAddFriend(long friendId); }
一个实现案例:
public class DebugCaller implements Caller { @Override public void register(String nickname, String username, String password) { UserRet ret = new UserRet(); ret.setId(1); ret.setNickname(nickname); ret.setUsername(username); FXListenerContext.onRegister(0, ret, null); } @Override public void login(String username, String password) { UserRet ret = new UserRet(); ret.setId(1); ret.setNickname("米虫2022"); ret.setUsername(username); FXListenerContext.onLogin(0, ret, null); } @Override public void fetchChatList() { String[] users = new String[]{"阿德莱德", "东南墨尔本凤凰", "独行侠", "南安普顿", "步行者"/*, "JavaScript"*/}; String[] messages = new String[]{"F1意大利大奖赛第二次练习赛", "全运会-羽毛球男团决赛", "2021ECEA东亚电竞锦标赛英雄联盟G3", "英超第4轮 曼联", "世界职业拳王争霸赛"/*, "全运会-跳水男子三米板决赛 全运会"*/}; List<ChatRet> chatRetList = new ArrayList<>(); for (int i = 0; i < users.length; i++) { ChatRet chatRet = new ChatRet(); chatRet.setType(ChatRet.TEXT); chatRet.setFriendId(i); chatRet.setNickname(users[i]); chatRet.setMessage(messages[i]); chatRet.setTimestamp(System.currentTimeMillis() - new Random().nextInt(10) * 24 * 60 * 60 * 1000); chatRet.setMessageCount(new Random().nextInt(150)); if (i == users.length - 1) { chatRet.setMessageCount(0); } chatRetList.add(chatRet); } ChatRet ret = new ChatRet(); ret.setFriendId(1001); ret.setTimestamp(System.currentTimeMillis()); ret.setType(ChatRet.ADD_FRIEND); ret.setNickname("米虫2022"); ret.setMessageCount(1); ret.setMessage("1001"); chatRetList.add(ret); FXListenerContext.onFetchChatList(0, chatRetList, null); } @Override public void fetchFriendList() { String[] users = new String[]{"阿德莱德", "东南墨尔本凤凰", "独行侠", "南安普顿", "步行者", "珀斯野猫"}; List<UserRet> userRetList = new ArrayList<>(); for (int i = 0; i < users.length; i++) { UserRet userRet = new UserRet(); userRet.setId(i); userRet.setNickname(users[i]); userRetList.add(userRet); } FXListenerContext.onFetchFriendList(0, userRetList, null); } @Override public void readMessage(long friendId) { } @Override public void chat(long friendId, String message) { ChatRet chatRet = new ChatRet(); chatRet.setFriendId(friendId); chatRet.setType(ChatRet.TEXT); chatRet.setNickname("机器人自动回复"); chatRet.setMessage("我是复读机:" + message); chatRet.setTimestamp(System.currentTimeMillis()); FXListenerContext.onChat(chatRet); } @Override public void queryFriend(String nickname) { String[] users = new String[]{"Java", "C", "C++", "Python", "Go", "JavaScript"}; List<UserRet> userRetList = new ArrayList<>(); for (int i = 0; i < users.length; i++) { UserRet userRet = new UserRet(); userRet.setId(i); userRet.setNickname(users[i]); userRetList.add(userRet); } FXListenerContext.onQueryFriend(0, userRetList, null); } @Override public void addFriend(long friendId) { } @Override public void applyAddFriend(long friendId) { new Thread(() -> { try { TimeUnit.MICROSECONDS.sleep(30); } catch (InterruptedException e) { } ChatRet ret = new ChatRet(); ret.setFriendId(1001); ret.setTimestamp(System.currentTimeMillis()); ret.setType(ChatRet.ADD_FRIEND_RET); ret.setNickname("米虫2022"); ret.setMessageCount(1); ret.setMessage("我们已经是好友了,开始聊天吧!"); FXListenerContext.onChat(ret); }).start(); } }
通讯演示
通讯版需要先运行Netty开发的服务端,然后运行多个客户端进行通讯:
Netty通讯版只要引入JavaFX包实现上面的Caller即可。
public class ImCaller implements Caller { @Override public void register(String nickname, String username, String password) { RegisterBO bo = new RegisterBO(); bo.setName(nickname); bo.setUsername(username); bo.setPassword(password); ImContext.sendBO(Type.register, bo); } @Override public void login(String username, String password) { LoginBO bo = new LoginBO(); bo.setUsername(username); bo.setPassword(password); ImContext.sendBO(Type.login, bo); } @Override public void fetchChatList() { ImContext.sendBO(Type.fetch_chat, new EmptyBO()); } @Override public void fetchFriendList() { ImContext.sendBO(Type.fetch_friend, new EmptyBO()); } @Override public void readMessage(long friendId) { ImContext.sendBO(Type.rend_message, new StringBO(String.valueOf(friendId))); } @Override public void chat(long friendId, String message) { ChatBO bo = new ChatBO(); bo.setContent(message); bo.setToUserId(friendId); bo.setType(ChatBO.Type.text); ImContext.sendBO(Type.chat, bo); } @Override public void queryFriend(String nickname) { ImContext.sendBO(Type.query_friend, new StringBO(nickname)); } @Override public void addFriend(long friendId) { StringBO bo = new StringBO(); bo.setContent(String.valueOf(friendId)); ImContext.sendBO(Type.add_friend, bo); } @Override public void applyAddFriend(long friendId) { ImContext.sendBO(Type.apply_add_friend, new StringBO(String.valueOf(friendId))); } }
关于源码
需要源码的扫描左侧头像微信小程序或私信联系,单机版源码结构如下:
. ├── App.java ├── AppStarter.java ├── caller │ ├── Caller.java │ └── DebugCaller.java ├── component │ ├── FX.java │ ├── FXContext.java │ ├── FXImage.java │ └── FXUI.java ├── javascript │ ├── HTMLBuilder.java │ └── JSMember.java ├── listener │ ├── ChatListener.java │ ├── ErrorListener.java │ ├── FXListenerContext.java │ ├── FetchChatListListener.java │ ├── FetchFriendListListener.java │ ├── LoginListener.java │ ├── QueryFriendListener.java │ ├── RegisterListener.java │ └── ret │ ├── ChatRet.java │ └── UserRet.java ├── model │ ├── AppMO.java │ ├── ChatMO.java │ ├── FriendMO.java │ ├── LoginMO.java │ └── RegisterMO.java └── ui ├── UserDataController.java ├── chat │ ├── ChatAddFriendDialog.java │ ├── ChatItemController.css │ ├── ChatItemController.fxml │ ├── ChatItemController.java │ ├── ChatListController.css │ ├── ChatListController.fxml │ ├── ChatListController.java │ ├── ChatMainController.css │ ├── ChatMainController.fxml │ └── ChatMainController.java ├── friend │ ├── FriendAddController.fxml │ ├── FriendAddController.java │ ├── FriendAddItemController.fxml │ ├── FriendAddItemController.java │ ├── FriendAddStage.java │ ├── FriendItemController.css │ ├── FriendItemController.fxml │ ├── FriendItemController.java │ ├── FriendListController.css │ ├── FriendListController.fxml │ ├── FriendListController.java │ ├── FriendProfileController.css │ ├── FriendProfileController.fxml │ └── FriendProfileController.java ├── login │ ├── LoginController.fxml │ ├── LoginController.java │ ├── LoginForm.java │ └── LoginStage.java ├── main │ ├── MainController.css │ ├── MainController.fxml │ └── MainController.java └── register ├── RegisterController.fxml ├── RegisterController.java └── RegisterForm.java 12 directories, 60 files
分类:
项目实战
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)