WebScoket最详细聊天(进阶版)

进阶版代码在原始版基础上改的,这里就不在发整个项目了


没有看过原始版的可以先去看下,把项目下载下来,网盘和博客都有https://blog.csdn.net/qq_45777315/article/details/107669617


我还是比较喜欢先奉上效果,看是不是你们想要的

根据用户uid建立通信,实现永久唯一通信id,及时用户下线重连还是可以继续通信

在这里插入图片描述
在这里插入图片描述

最细讲解,代码+注释+数据库表

表结构 user 用户表和 chatting 聊天记录表

用户表
在这里插入图片描述
聊天记录表
在这里插入图片描述

代码

两个新表的实体类需要创建,具体的setget不要忘了,也可以直接用lombok
在这里插入图片描述

新建一个UserService,进行连接数据库操作

代码上的静态调用的注释需要注意,因为不是静态方法可能在spring运行时,没有及时注入
静态调用方法https://blog.csdn.net/qq_45777315/article/details/107369039


import org.springframework.stereotype.Service;
import zyh.dd.entity.User;
import zyh.dd.mapper.UserMapper;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

/**
 * @author: 云
 * @version: 1.0
 */
@Service
public class UserService {
	//调用mapper
    @Resource
    UserMapper userMapper;

    //静态调用
    public static UserService userService;
    @PostConstruct
    public void init(){
        userService = this;
    }

    /**
     * 聊天
     */
    //查询用户信息  sql select * from user where uid=#{uid}
    public static User selectUser(int uid){
        return userService.userMapper.selectUser(uid);
    }
    //添加消息记录 sql INSERT INTO chatting(fid,tid,message,`type`,stime,`status`) 
    public static int instUser(int fid,int tid,String message,String type,int status){
        return userService.userMapper.instUser(fid,tid,message,type,status);
    }
    
}

剩下的就全是 MyWebScoket 修改了

import com.fasterxml.jackson.databind.ObjectMapper;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import zyh.dd.entity.SocketEntity;
import zyh.dd.entity.User;
import zyh.dd.service.UserService;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * zyh
 * 2020/07/29
 */

/**
 * 虽然@component默认是单例模式的,但在spring boot 中还是会为每一个webscoket连接初始化一个bean,所以这里使用一个静态的set保存spring boot创建的bean--MyWebscoket
 */
@ServerEndpoint(value = "/websocket/{userId}")
@Component
public class MyWebScoket {
    //用户的Service层 数据库操作
    @Autowired
    UserService userService;

    public static MyWebScoket myWebScoket;

    @PostConstruct
    public void init() {
        myWebScoket = this;
    }

    //用来存储每个客户端对应的MyWebScoket对象
    private static CopyOnWriteArraySet<MyWebScoket> webScoketset = new CopyOnWriteArraySet<MyWebScoket>();
    //用来记录sessionid和session之间的绑定关系。

    private static Map<String, Session> map = new HashMap<String, Session>();
    private Session session;//当前会话的session
    private String name;
    private String userId;
    User user = null;//用户实体类

    /**
     * 成功建立连接调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        //查询 发送人用户信息
        user = UserService.selectUser(Integer.parseInt(userId));
        //存储信息
        this.session = session;
        //取用户的唯一uid 强转下类型
        this.userId = String.valueOf(user.getUid());
        //取用户的名字
        this.name = user.getUsername();
        //用户的唯一uid 和
        map.put(String.valueOf(user.getUid()), session);
        webScoketset.add(this);//加入set中
        this.session.getAsyncRemote().sendText(user.getUsername() + "上线了" + "我的频道号是" + user.getUid() + ",当前连接人数为:" + webScoketset.size());
    }


    /**
     * 连接关闭调用的方法
     *
     * @param session
     */
    @OnClose
    public void onClose(Session session) {

        webScoketset.remove(this);//加入set中
        Boolean aa = map.remove(String.valueOf(user.getUid()), session);
    }

    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 收到客户消息后调用的方法
     *
     * @param session
     */
    @OnMessage
    public void OnMessage(String message, Session session) throws IOException {
        //message不是普通的string,而是我们定义的SocketEntity,json字符串
        SocketEntity socketEntity = new ObjectMapper().readValue(message, SocketEntity.class);
        //解析json
        JSONObject jsonObject = JSONObject.fromObject(message);

        //单聊
        if (socketEntity.getType() == 1) {
            //单聊:需要找到发送者和接收者即可
            socketEntity.setFromUser(String.valueOf(user.getUid()));//发送者
            //确定发送人
            Session fromsession = map.get(socketEntity.getFromUser());
            //确定受信人
            Session tosession = map.get(socketEntity.getToUser());
            //通道保持畅通
            if (tosession != null) {
                // 存储聊天记录  已读  存进聊天记录表
                UserService.instUser(user.getUid(), Integer.parseInt(socketEntity.getToUser()),jsonObject.get("message").toString(),jsonObject.get("type").toString(),1);

                fromsession.getAsyncRemote().sendText(name + ":" + socketEntity.getMessage());//发送消息
                tosession.getAsyncRemote().sendText(name + ":" + socketEntity.getMessage());//发送消息
            } else {
                // 存储聊天记录  未读  存进聊天记录表
                UserService.instUser(user.getUid(), Integer.parseInt(socketEntity.getToUser()),jsonObject.get("message").toString(),jsonObject.get("type").toString(),0);

                fromsession.getAsyncRemote().sendText("系统消息:对方不在线或者您输入的频道号有误");//发送消息
            }
        } else {
            //   广播群发给每一个客户端
            broadcast(socketEntity, name);
        }

    }


    /**
     * 群发消息
     *
     * @param socketEntity
     */
    private void broadcast(SocketEntity socketEntity, String name) {
        for (MyWebScoket myWebScoket : webScoketset) {
            //发送消息
            myWebScoket.session.getAsyncRemote().sendText(name + ":" + socketEntity.getMessage());
        }
    }


}

这里需要注意因为我加了判断是什么消息类型,在前端代码需要加一行代码

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值