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
    评论
在Java的springboot应用中,可以使用WebSocket实现实时消息推送功能。要实现后端传给前端编码,前端显示表情的功能,可以采用以下步骤: 1. 后端发送消息时,将消息中的表情编码转换为对应的表情图片链接,然后再发送给前端。可以使用Java的正则表达式匹配消息中的表情编码,然后替换为对应的表情图片链接。 2. 前端接收到消息后,使用HTML的img标签显示表情图片。在显示表情图片时,可以将表情图片链接作为img标签的src属性值,这样浏览器就会自动加载并显示表情图片。 下面是一个简单的示例代码: 后端Java代码: ```java // 将消息中的表情编码转换为对应的表情图片链接 private String convertEmojis(String message) { // 定义正则表达式匹配表情编码 Pattern pattern = Pattern.compile(":\\w+:"); Matcher matcher = pattern.matcher(message); StringBuffer buffer = new StringBuffer(); while (matcher.find()) { // 获取表情编码 String emojiCode = matcher.group(); // 根据表情编码获取对应的表情图片链接 String emojiUrl = getEmojiUrl(emojiCode); // 替换表情编码为表情图片链接 matcher.appendReplacement(buffer, "<img src=\"" + emojiUrl + "\">"); } matcher.appendTail(buffer); return buffer.toString(); } // 获取表情图片链接 private String getEmojiUrl(String emojiCode) { // 假设表情编码为:smile:,对应的表情图片链接为http://example.com/emoji/smile.png return "http://example.com/emoji/" + emojiCode.substring(1, emojiCode.length() - 1) + ".png"; } // 发送消息 private void sendMessage(String message) { // 将消息中的表情编码转换为对应的表情图片链接 String formattedMessage = convertEmojis(message); // 发送消息给所有连接的客户端 for (WebSocketSession session : sessions) { try { session.sendMessage(new TextMessage(formattedMessage)); } catch (IOException e) { e.printStackTrace(); } } } ``` 前端HTML代码: ```html <!-- 显示消息的区域 --> <div id="messageArea"></div> <script> // 创建WebSocket连接 var socket = new WebSocket("ws://example.com/chat"); // 监听WebSocket连接事件 socket.onopen = function(event) { console.log("WebSocket连接已打开"); }; // 监听WebSocket消息事件 socket.onmessage = function(event) { // 将消息添加到显示区域 var messageArea = document.getElementById("messageArea"); var message = event.data; messageArea.innerHTML += message; }; // 发送消息 function sendMessage() { // 获取输入框中的消息 var inputBox = document.getElementById("inputBox"); var message = inputBox.value; // 发送消息给后端 socket.send(message); // 清空输入框 inputBox.value = ""; } </script> ``` 在上面的示例代码中,后端将消息中的表情编码转换为对应的表情图片链接,然后发送给前端。前端通过WebSocket接收到消息后,将消息添加到指定的显示区域中,并且浏览器会自动加载并显示表情图片。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

oct1025

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

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

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

打赏作者

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

抵扣说明:

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

余额充值