使用WebSocket编写聊天室笔记

        本人是java小白,本章节属于我得毕设项目中的小块个功能点,如果有编写不对的地方还请指出,非常感谢!!!
        聊天系统采用最简单的scoket插件写的,真正的聊天系统最好使用原生udp/tcp等方式编写

       本章默认已经将SpringBoot项目搭建完成,下面操作是在SpringBoot的基础上编写的

1、创建SpringBoot项目,添加依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2、创建WebSocketConfig配置
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
3、定义保存用户会话信息的map
//    保存用户的map
    public static ConcurrentHashMap<String,MyWebSocket> users = new ConcurrentHashMap<>();
//    保存客服的map
    public static ConcurrentHashMap<String,MyWebSocket> kf = new ConcurrentHashMap<>();
4、创建控制类
/**
 * @author: 赵帅峰
 * @create: 2023/10/5
 * @FileName: MyWebSocket
 */
@ServerEndpoint(value = "/websocket/{sid}/{identify}",encoders = {ServerEncoder.class})
@Component
public class MyWebSocket extends ChatOperate  {
​
    private static final Lock lock = new ReentrantLock();
​
    private Session session;
​
    private String identify;
​
    private String sid = "";
​
    private static LoginService loginService;
​
    @Autowired
    public void setINoticeService(LoginService loginService) {
        MyWebSocket.loginService = loginService;
    }
​
    @OnOpen
    public void onOpen(@PathParam("sid") String sid, @PathParam("identify") String identify, Session session) throws IOException {
​
        this.session = session;
        this.sid = sid;
        this.identify = identify;
​
        if(this.identify.equals("3")){
            WebSocketUser.kf.put(this.sid,this);
        }else{
            WebSocketUser.users.put(this.sid,this);
        }
​
        if(this.identify.equals("3")){
            this.sendMessage(getServiceDialog(sid),0);
            this.AllUserSend();
//            AllSendMessage(this.sid,"客服","客服已上线",1);
        }else {
            this.sendMessage(getUserDialog(sid),0);
            this.AllKfSend();
//            UserIdToSend(sid,sid,identify,"","已上线",1);
        }
    }
​
​
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
​
        HashMap hashMap = JSON.parseObject(message, HashMap.class);
//        这是接收人的id
        String id = (String) hashMap.get("id");
//        这是发送人的id
        String sid = (String) hashMap.get("sid");
//        这是发送人的标识
        String identify = (String) hashMap.get("identify");
//        这是发送人的用户名
        String name = (String) hashMap.get("name");
​
        String context = (String) hashMap.get("context");
        Dialogue dialogue = new Dialogue(Integer.valueOf(sid), Integer.valueOf(id), name, "", context, Integer.valueOf(identify));
        if(id.equals("All") || id.equals("all")){
//            this.AllSendMessage(sid,name,context,2);
        }else{
//            this.sendMessage(dialogue);
            this.UserIdToSend(dialogue);
//            this.UserIdToSend(sid,id,identify,name,context,2);
        }
    }
​
    public void UserIdToSend(Dialogue dialogue) throws IOException {
        //        将信息保存Redis
        saveService(dialogue);
        saveUserMessage(dialogue);
​
        MyWebSocket myWebSocket = null;
        if(dialogue.getIdentify() == 3){
            myWebSocket = WebSocketUser.users.get(String.valueOf(dialogue.getRecipient()));
            if(myWebSocket==null){
                dialogue.setContext("不在线");
            }else{
                myWebSocket.sendMessage(dialogue,1);
            }
            WebSocketUser.kf.get(String.valueOf(dialogue.getSender())).sendMessage(dialogue,1);
        }else{
            myWebSocket = WebSocketUser.kf.get(String.valueOf(dialogue.getRecipient()));
            if(myWebSocket==null){
                dialogue.setContext("不在线");
            }else{
                myWebSocket.sendMessage(dialogue,1);
            }
            WebSocketUser.users.get(String.valueOf(dialogue.getSender())).sendMessage(dialogue,1);
        }
​
    }
​
    public void sendMessage(Object o,int flag) throws IOException {
        HashMap<String, Object> result = new HashMap<>();
        if("3".equals(this.identify)){
            result.put("user",WebSocketUser.users.keys());
        }else {
            result.put("user",WebSocketUser.kf.keys());
        }
        result.put("flag",flag);
        result.put("data",o);
        lock.lock();
        try{
            this.session.getBasicRemote().sendObject(result);//同步
            //this.session.getAsyncRemote().sendText(message);//异步
        } catch (EncodeException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
​
    public void AllKfSend(){
       WebSocketUser.kf.forEach(new BiConsumer<String, MyWebSocket>() {
            @SneakyThrows
            @Override
            public void accept(String s, MyWebSocket myWebSocket) {
                myWebSocket.sendMessage(null,1);
            }
        });
    }
​
    public void AllUserSend(){
        WebSocketUser.users.forEach(new BiConsumer<String, MyWebSocket>() {
            @SneakyThrows
            @Override
            public void accept(String s, MyWebSocket myWebSocket) {
                myWebSocket.sendMessage(null,1);
            }
        });
    }
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }
​
    @OnClose
    public void onclose(Session session) throws IOException {
        if (!sid.equals("")) {
            WebSocketUser.users.remove(sid);  //从set中删除
            WebSocketUser.kf.remove(sid);  //从set中删除
        }
    }
}
利用Redis设置会话持久
5、添加redis依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.62</version>
</dependency>
6、创建RedisConfig配置
@Configuration
public class RedisConfig {
​
    @Bean
    public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory con){
        RedisTemplate<String,Object> template = new RedisTemplate<String,Object>();
        template.setConnectionFactory(con);
        template.setKeySerializer(new StringRedisSerializer());  //key采用字符串方式管理
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());  //value使用jdk的序列化
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
}
7、在application.properties中添加redis的配置项
#配置redis
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制) 默认 8
spring.redis.lettuce.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
spring.redis.lettuce.pool.max-wait=-1
# 连接池中的最大空闲连接 默认 8
spring.redis.lettuce.pool.max-idle=8
# 连接池中的最小空闲连接 默认 0
spring.redis.lettuce.pool.min-idle=0
8、定义存储的json格式

客服:

{

客服id:{

用户Id_1:[{},{}],

用户Id_2:[{},{}]

}

客服2:{

用户Id_1:[{内容},{内容}],

用户Id_2:[{内容},{内容}]

}

}

客户:{

用户Id_1:[{内容},{内容},{内容}],

用户Id_2:[{内容},{内容}]

}

9、添加实体类,并实现序列化,get和set方法自己添加
public class Dialogue implements Serializable {
​
//    发送者id
    private int sender = -1;
//    接收者id
    private int recipient = -1;
//    发送者名称
    private String SenderName;
//    接收者名称
    private String recipientName;
//    内容
    private String message;
//    0:客服  1:用户
    private int isCustomer;
    //    创建时间
    private String createDate;
​
    public Dialogue(){}
    public Dialogue(int sender, int recipient, String senderName, String recipientName, String message,int isCustomer) {
        this.sender = sender;
        this.recipient = recipient;
        this.SenderName = senderName;
        this.recipientName = recipientName;
        this.message = message;
        this.isCustomer = isCustomer;
        Date date = new Date();
        this.createDate = new SimpleDateFormat("MM-dd hh:mm:ss").format(date);;
    }
    @Override
    public String toString() {
        return "Dialogue{" +
                "sender=" + sender +
                ", recipient=" + recipient +
                ", SenderName='" + SenderName + '\'' +
                ", recipientName='" + recipientName + '\'' +
                ", message='" + message + '\'' +
                ", isCustomer=" + isCustomer +
                ", createDate='" + createDate + '\'' +
                '}';
    }
}
9、编写对会话存储的代码
/**
 * @author: 赵帅峰
 * @create: 2023/12/13
 * @FileName: ChatOperate
 */
@Component
public class ChatOperate {
​
​
    private static RedisTemplate redisTemplate;
​
    @Autowired
    public void setRedisTemplate(RedisTemplate redisTemplate) {
        ChatOperate.redisTemplate = redisTemplate;
    }
​
    /**
     *  这是在redis中添加客服聊天记录
     * @param dialogue
     * @return
     */
    public Boolean saveService(Dialogue dialogue){
​
        if(dialogue.getSender() == -1 || dialogue.getRecipient()== -1){
            throw new RuntimeException("添加异常,请查看信息是否正确");
        }
​
//        判断当前信息是用户发的还是客服发的,这里判断客户id
        String recipient = String.valueOf(dialogue.getIdentify()==3 ? dialogue.getSender() : dialogue.getRecipient());
//        判断当前信息是用户发的还是客服发的,这里判断用户id
        String Sender = String.valueOf(dialogue.getIdentify()==3 ? dialogue.getRecipient() : dialogue.getSender());
​
//        如果记录为空,则直接添加,否则原有基础上添加
        if (redisTemplate.opsForHash().get("chat:service", recipient)==null){
            HashMap<String, List> map = new HashMap<>();
            ArrayList<Dialogue> d = new ArrayList<>();
            d.add(dialogue);
            map.put(Sender,d);
            redisTemplate.opsForHash().put("chat:service",recipient,map);
        }else{
//        查询redis数据库中改客服与该用户的聊天记录
            HashMap<String,List> dialogues = (HashMap<String,List>) redisTemplate.opsForHash().get("chat:service", recipient);
            List<Dialogue> arr = dialogues.get(Sender)!=null ? dialogues.get(Sender) : new ArrayList<>();
            arr.add(dialogue);
            dialogues.put(Sender,arr);
            redisTemplate.opsForHash().put("chat:service",recipient,dialogues);
        }
        return true;
    }
​
    /**
     * 这是在redis中添加用户聊天记录
     * @param dialogue
     * @return
     */
    public Boolean saveUserMessage(Dialogue dialogue){
​
        if(dialogue.getSender() == -1 || dialogue.getRecipient()== -1){
            throw new RuntimeException("添加异常,请查看信息是否正确");
        }
​
//        判断当前信息是用户发的还是客服发的,这里判断用户
        String recipient = String.valueOf(dialogue.getIdentify()!=3 ? dialogue.getSender() : dialogue.getRecipient());
​
        if(redisTemplate.opsForHash().get("chat:user", recipient) == null){
            ArrayList<Dialogue> d = new ArrayList<>();
            d.add(dialogue);
            redisTemplate.opsForHash().put("chat:user",recipient,d);
        }else{
            List dialogues = (List) redisTemplate.opsForHash().get("chat:user", recipient);
            dialogues.add(dialogue);
            redisTemplate.opsForHash().put("chat:user",recipient,dialogues);
        }
        return true;
    }
​
    /**
     *  按照id查找客服的聊天记录
     * @param id
     * @return
     */
    public HashMap<String,List<Dialogue>> getServiceDialog(String id){
        if(redisTemplate.opsForHash().get("chat:service", id) == null){
            return null;
        }
        return (HashMap) redisTemplate.opsForHash().get("chat:service", id);
    }
​
    /**
     *  按照id查找用户的聊天记录
     * @param id
     * @return
     */
    public List<Dialogue> getUserDialog(String id){
        if(redisTemplate.opsForHash().get("chat:user", id) == null){
            return null;
        }
        return (List<Dialogue>) redisTemplate.opsForHash().get("chat:user", id);
    }
​
    /**
     * 根据指定id删除客服记录
     * @param id
     * @return
     */
    public Long deleteServiceById(String id){
        if(redisTemplate.opsForHash().get("chat:service",id)==null) return 0L;
        return redisTemplate.opsForHash().delete("chat:service",id);
    }
​
    /**
     * 根据指定id删除用户记录
     * @param id
     * @return
     */
    public Long deleteUserById(String id){
        if(redisTemplate.opsForHash().get("chat:user",id)==null) return 0L;
        return redisTemplate.opsForHash().delete("chat:user",id);
    }
}
10、测试方法
@Test
public void 添加记录(){
    Dialogue dialogue = new Dialogue(3,5,"admin","zsf1","nihao",0);
    saveUserMessage(dialogue);
    saveService(dialogue);
    Dialogue dialogue1 = new Dialogue(5,3,"zsf","admin","你好,我在呢",1);
    saveUserMessage(dialogue1);
    saveService(dialogue1);
}
@Test
public void 根据id删除记录(){
    System.out.println(deleteServiceById("3"));
    System.out.println(deleteUserById("4"));
}
@Test
public void 查询用户记录(){
    System.out.println(getServiceDialog("3"));
    System.out.println(getUserDialog("4"));
}
11、前端调用

前端调用时直接用websocket调用即可,将自己的id和身份标识传来

标识:3 -- 客服人员 other(其他) -- 用户

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值