SpringBoot使用Websocket技术实现聊天功能--后端部分(三)

本文介绍了如何在Spring Boot项目中配置WebSocket,包括添加相关依赖、配置WebSocketConfig以及实现WebSocket的具体类。同时,针对可能出现的问题,如线程同步和云端部署时的Tomcat配置进行了分析,并提供了工具类WebSocketMapUtil用于管理在线用户。此外,还展示了WebSocket如何通过反射调用Service或Mapper层的方法来处理业务逻辑。
摘要由CSDN通过智能技术生成

所需依赖:

<!--        websocketsdk-->
        <dependency>
            <groupId>javax.websocket</groupId>
            <artifactId>javax.websocket-api</artifactId>
            <version>1.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.apache.tomcat.embed</groupId>-->
<!--            <artifactId>tomcat-embed-websocket</artifactId>-->
<!--        </dependency>-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <!--声明spring boot内嵌tomcat的作用范围  在运行时不起作用-->
<!--            <scope>provided</scope>-->
        </dependency>

WebSocket配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {

	/**
	 * 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
	 */
	@Bean
	public ServerEndpointExporter serverEndpointExporter() {
		return new ServerEndpointExporter();
	}

}

【注】这里在本地启动的时候可以正常进行,但是将项目打包部署到Linux服务器的时候可能会出现报错,原因可能为线程不同步,提前创建了WebSocket对象但是对应的配置JavaBean还未生成;也有可能是Pom.xml的tomcat配置没有排除,spring内置了tomcat服务器,但是云端就会出现问题(仅提供bug修改参考意见)


WebSocketMap工具类

此工具类实现将在线的用户添加到哈希map中进行存储,采用ConcurrentHashMap来确保线程安全性

import com.minprogram.renthouse.controller.MyWebSocketServer;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @Author zd
 * @Date 2021/9/6 15:00
 * @Version 1.0
 */
public class WebSocketMapUtil {

public static ConcurrentMap<String, MyWebSocketServer> webSocketMap = new ConcurrentHashMap<String, MyWebSocketServer>();

public static void put(String key, MyWebSocketServer myWebSocketServer){
     webSocketMap.put(key, myWebSocketServer);
    }

public static MyWebSocketServer get(String key){
    return webSocketMap.get(key);
}

public static void remove(String key){
    webSocketMap.remove(key);
}

public static Collection<MyWebSocketServer> getValues(){
    return webSocketMap.values();
}

//判断用户是否存在于Map中
public static boolean isContain(String key){
        return webSocketMap.containsKey(key);
    }
}

WebSocket具体实现类

【注】一定要使用反射的方式来获取service或者mapper层的方法,否则会报错

/**
 * @Author zd
 * @Date 2021/9/2 16:53
 * @Version 1.0
 */

@Component
@ServerEndpoint(value = "/WebSocket/{openId}")  // 这里存放的是小程序前端输入的用户识别码,即openid
@Slf4j
public class MyWebSocketServer {
    @Autowired
    private UserMapper userMapper = MyApplicationContextAware.getApplicationContext().getBean(UserMapper.class);

    // 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    private Session session;
    private String openId;

    /**
     * 连接建立后触发的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("openId") String openId) {
        this.session = session;
        this.openId=openId;
        log.info("onOpen-SessionId:{}",session.getId());
        WebSocketMapUtil.put(openId, this);
        addOnlineCount();
        log.info("有新连接加入,openId:【{}】!当前在线人数为:{}", openId, getOnlineCount());
    }

    /**
     * 连接关闭后触发的方法
     */
    @OnClose
    public void onClose() {/*从map中删除*/
        WebSocketMapUtil.remove(this.openId);
        subOnlineCount();//在线人数减少
        log.info("====== onClose:【{}】,目前在线人数:{} ======",this.openId,getOnlineCount());
    }

    /**
     * 接收到客户端消息时触发的方法
     */
    @OnMessage
    public void onMessage(String requestBody) throws Exception {/*获取服务端到客户端的通道*/

        实现发送逻辑
    }

    /**
     * 发生错误时触发的方法
     */
    @OnError
    public void onError(Session session, Throwable error) throws Exception {
        log.info("{}连接发生错误{}",session.getId(),error.getMessage());
        error.printStackTrace();
    }


// 发送给某人消息的方法
    public void SendMsgToSB(String uopenid,String nid) throws Exception {
        //1.判断用户openid是否存在于websocket之中
        // 存在:数据标记为已读(2)存入数据库然后直接推送 不存在:数据标记为未读(1)存入数据库,不推送
//        String msg= SensitivewordFilter.filterText(requestData.getString("msg"));
        notedto ndto = new notedto();
        ndto.setNid(Integer.parseInt(nid));
        Note n = adminMapper.getNoteDetail(ndto);
        if(WebSocketMapUtil.isContain(uopenid)){
            addToDataBases(n.getNId(),"unRead",uopenid);
            List<Object> nlist = new ArrayList<>();
            nlist.add(n);
            sendMessage(44001,1,nlist,"noteNoRead",null);
        }else{
            addToDataBases(n.getNId(),"unOnline",uopenid);
        }
    }

// 获取未读消息列表
    public void getNoReadMsg(String myOpenid) throws Exception {//房东上线自动查询
        log.info("getNoReadMsg-myOpenid:【{}】",myOpenid);
        MyWebSocketServer myWebSocket = WebSocketMapUtil.get(this.openId);

        JSONObject receiverUserJson=new JSONObject();
        receiverUserJson.put("UOpenId",this.openId);

        notetouserdto ntudto = new notetouserdto();
        ntudto.setUopenid(this.openId);
        ntudto.setNtustatus(1);
        List<NoteToUser> ntulist = noteToUserMapper.getNoteToUserList(ntudto);
        List<Object> nlist = new ArrayList<>();
        Integer sum = 0;
        if(!ntulist.isEmpty()){
            for(NoteToUser ntu:ntulist){
                notedto ndto = new notedto();
                ndto.setNid(ntu.getNId());
                Note n = adminMapper.getNoteDetail(ndto);
                nlist.add(n);
                log.info("向用户推送内容:{}",n.toString());
                sum++;
            }
            myWebSocket.sendMessage(44001,sum,nlist,"noteNoRead",new JSONObject());
        }
    }

// 实现发送操作
    public void sendMessage(int code,int sum,List<Object> content,String type,JSONObject count) throws Exception {
        JSONObject result = new JSONObject();
        result.put("code",code);//插入发送内容

        log.info("发送:{}",result.toString());
        this.session.getAsyncRemote().sendText(result.toString().replace("=",":"));
    }

// 获取当前在线用户人数
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

// 新增上线用户人数
    public static synchronized void addOnlineCount() {
        MyWebSocketServer.onlineCount++;
    }

// 减去一个上线用户人数
    public static synchronized void subOnlineCount() {
        MyWebSocketServer.onlineCount--;
    }
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oct1025

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值