现在的java开发基本都是基于springboot,所以我们就基于springboot2环境。
效果展示(基于网页浏览器的,不是桌面程序)

通讯协议:
现在的主流浏览器基本都支持html5标准,所以我们就用websocket作为通信协议。
后端框架:
后端采用springboot作为主框架,配合websocket模块与前端通信,通过springboot整合redis,mybatis,mysql。
消息中转:
使用redis的订阅发布机制作为消息中转,实现消息的顺序消费,即保证数据的实时性,也保证数据的安全性。
聊天方式:
单人聊天,多人群聊,
离线消息:
当群聊时,或者单人聊天时,如果对方不在线,则记录离线消息,当对方上线时会统一接收。
代码参考
redis配置
-
- @Configuration
- public class RedisConfig {
-
- /**
- * redis消息监听器容器
- * 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器
- * 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理
- * @param connectionFactory
- * @param listenerAdapter
- * @return
- */
- @Bean
- RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
- MessageListenerAdapter listenerAdapter) {
-
- RedisMessageListenerContainer container = new RedisMessageListenerContainer();
- container.setConnectionFactory(connectionFactory);
- //订阅了一个叫chat 的通道
- container.addMessageListener(listenerAdapter, new PatternTopic("chat"));
- //这个container 可以添加多个 messageListener
- return container;
- }
-
- /**
- * 消息监听器适配器,绑定消息处理器,利用反射技术调用消息处理器的业务方法
- * @param receiver
- * @return
- */
- @Bean
- MessageListenerAdapter listenerAdapter(RedisReceiver receiver) {
- //这个地方 是给messageListenerAdapter 传入一个消息接受的处理器,利用反射的方法调用“receiveMessage”
- //也有好几个重载方法,这边默认调用处理器的方法 叫handleMessage 可以自己到源码里面看
- return new MessageListenerAdapter(receiver, "receiveMessage");
- }
-
- /**redis 读取内容的template */
- @Bean(name="stringRedisTemplate")
- StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
- return new StringRedisTemplate(connectionFactory);
- }
-
- }
消息中转器
-
- @EnableScheduling
- @Component
- public class RedisReceiver {
-
- @Autowired
- StringRedisTemplate stringRedisTemplate;
- @Autowired
- UserDao userDao;
- @Autowired
- RecordDao recordDao;
-
- public static class SessionInfo {
- public Session session;
- public long userid;
- public SessionInfo(Session session, long userid) {
- this.session = session;
- this.userid = userid;
- }
- }
-
- ObjectMapper objectMapper = new ObjectMapper();
-
- public List
list = new LinkedList(); -
- public void onOpen(Session session, long userid){
- if(isUserOnline(userid)){
- try {
- System.out.println("已经在线");
- send(session,"已经在线");
- session.close();
- return;
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- synchronized (list) {
- list.add(new SessionInfo(session,userid));
- System.out.println("有人上线了:"+list.size());
- stringRedisTemplate.opsForValue().setBit("ONLINE", userid, true);
- sendOnlineOrOffline(userid,"online");
- }
-
- //推送离线消息
- List
records=recordDao.findUnread(userid); - records.forEach(record -> {
- try {
- sendToUserid(userid,record.getContent());
- record.setUsed(true);
- recordDao.save(record);
- } catch (Exception e) {
- e.printStackTrace();
- }
- });
- }
-
- }
用户模型
-
- @Data
- @Entity
- @Table(name="im_user")
- public class User {
- @Id@GeneratedValue
- private Long id;
- private String username;
- private String sign;
- private String status;
- private String avatar;
- private Date created;
- private String password;
- }
消息记录
- @Data
- @Entity
- @Table(name="im_record")
- public class Record {
- @Id
- @GeneratedValue
- private Long id;
- @Column(columnDefinition = "TEXT")
- private String content;
- private Date created;
- @Column(name = "from_user_id")
- private Long from;
- @Column(name = "to_user_id")
- private Long to;
- private Boolean used; //是否已经读取过,true代表读取过,false代表未读
-
- }
群组聊天
-
- @Data
- @Entity
- @Table(name="im_group")
- public class Group {
-
- @Id
- @GeneratedValue
- private Long id;
- private String name;
- private Date created;
-
- @ManyToOne()
- @JoinColumn(name="user_id")
- private User user; //此分组的主人
-
- @ManyToMany()
- @JoinTable(name="im_group_user",
- joinColumns = {@JoinColumn(name="group_id")}, //连接本类
- inverseJoinColumns = {@JoinColumn(name="user_id")})//连接此属性的类
- private List
list; //分组中的好友 - }
使用redis的优势
参考链接 完整项目参考链接点击这里