使用websocket踩到的坑儿,,,

使用websocket踩到的坑,贴出来,大家有需要可以借鉴一下,如有问题欢迎指正~~~~


1.websocket主要是服务器主动向客户端推送消息,与客户端保持长连接,当然前提是客户端不刷新页面,否则无意义

2.使用websocket

(1)前端

(2)后台

装信息的并发容器

public class MyWebSocketMap {
    private static ConcurrentMap myWebSocketConcurrentHashMap = new ConcurrentHashMap();

    public static void put(String key, MyWebSocket myWebSocket) {
        myWebSocketConcurrentHashMap.put(key, myWebSocket);
    }

    public static MyWebSocket get(String key) {
        return myWebSocketConcurrentHashMap.get(key);
    }

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

    public static Collection getValues() {
        return myWebSocketConcurrentHashMap.values();
    }

    public static boolean containsKey(String key) {
        return myWebSocketConcurrentHashMap.containsKey(key);
    }

    public static List getAllKey() {
        List allKey = new ArrayList();
        for (String key : myWebSocketConcurrentHashMap.keySet()) {
            allKey.add(key);
        }
        return allKey;
    }
}
MyWebSocket用于通信
@ServerEndpoint(value = "/addressFileWebSocket",configurator = WebSocketConfig.class)
public class MyWebSocket implements Serializable {
    protected Logger logger = LoggerFactory.getLogger(getClass());
    //private static MemcachedUtils memcachedUtils = MemcachedUtils.UECACHE;
    // 与客户端的链接会话,通过它实现定向推送,比如某个用户
    private Session session;
    //线程安全的静态变量,表示在线连接数
    private static volatile int onlineCount = 0;
    // 与多个客户端通信,比如聊天室,通知多个用户
    //public static CopyOnWriteArraySet webSocketSet = new CopyOnWriteArraySet();
    ApplicationContext act = SpringUtil.getApplicationContext();
    TDmAddressFileService addressFileService =  act.getBean(TDmAddressFileService.class);
   

    /**
     * 

打开列表界面:初始化连接成功调用的方法

* * @param session 可选的参数 * @throws Exception */ @OnOpen public void onOpen(Session session) throws Exception { this.session = session; // 记录页面来源:列表 String query = this.session.getQueryString(); String sessionId = this.session.getId(); MyWebSocketMap.put(query, this);// 放到缓存中 addOnlineCount(); // 线程数+1 logger.info(String.format("%s页面,新连接加入,sessionId:%s,当前在线人数:%s", getPageDesc(query), sessionId, getOnlineCount())); //webSocketSet.add(this); } /** *

连接关闭调用的方法

* * @throws Exception */ @OnClose public void onClose() throws Exception { // 从map中删除 MyWebSocketMap.remove(session.getQueryString()); subOnlineCount(); // 在线数-1 logger.info("有一个链接关闭,剩余在线人数:" + getOnlineCount()); // webSocketSet.remove(this); } /** *

收到客户端消息后调用的方法

* * @param message 客户端发送过来的消息,time :20190703 * @param session 可选的参数 * @throws java.io.IOException */ @OnMessage public void onMessage(String message, Session session) throws IOException { logger.info("收到客户端webSocket请求,time=>" + message); //synchronized (session) { if (StringUtils.isNotEmpty(message) && message.indexOf("-") > 0) { String time = message.split("-")[0]; String flag = message.split("-")[1]; if ("close".equals(flag)) { // 前端关闭时,主动清理连接,不推送消息 System.out.println(" 前端关闭时,主动清理连接,不推送消息"); if (MyWebSocketMap.containsKey(time)) { MyWebSocketMap.remove(time); } } else { MyWebSocketMap.put(time, this); //由于使用websocket,需要提前获取登录用户信息,不然再service中获取不到 User user = (User) session.getUserProperties().get("user"); //调用service,进行相应的处理,,,, addressFileService.createAddressFile(user); } } //} } /** *

发生错误时调用

* * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { subOnlineCount(); // 在线数-1 logger.error(String.format("发送错误异常,剩余在线人数:%s,异常:%s", getOnlineCount(), Exceptions.getStackTraceAsString(error))); } /** *

服务器给客户端 发送消息方法。

* * @param message 消息内容 * @throws java.io.IOException */ public void sendMessage(String message) throws IOException { /** * getAsyncRemote 非阻塞 * getBasicRemote 阻塞,并且第二个参数是一次发送消息中的部分消息 */ synchronized (this) { try { 页面刷新 但是找的不是之前的session if (this.session.isOpen()) { // 判断链接是否关闭 // 异步时,tomcat可能有bug,引起TEXT_FULL_WRITING //this.session.getAsyncRemote().sendText(message); this.session.getBasicRemote().sendText(message); System.out.println("-------------------------给客户端发送消息--------------"); } } catch (Exception e) { e.printStackTrace(); } } } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { MyWebSocket.onlineCount++; } public static synchronized void subOnlineCount() { if (onlineCount != 0) { // 防止减到负数 MyWebSocket.onlineCount--; } } private String getPageDesc(String query) { if (StringUtils.isEmpty(query)) { return "未知"; }else { return "地址库文件列表"; } } }

3.下面聊聊遇到的坑儿:

websocket服务器收到客户端的请求后,需要对数据库进行操作,需要获取到service对象(坑一)和shiro用户信息(坑二)

(1)为了获取到service对象,需要定义一个工具类

package com.datang.dmp.modules.passback.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

/**
 * Created by on 2019/7/5.
 *
 */
@Lazy(false)
@Component
public class SpringUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext = null;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if(SpringUtil.applicationContext == null){
            SpringUtil.applicationContext  = applicationContext;
        }
    }

    //获取applicationContext
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    //通过name获取 Bean.
    public static Object getBean(String name){
        return getApplicationContext().getBean(name);

    }

    //通过class获取Bean.
    public static  T getBean(Class clazz){
        return getApplicationContext().getBean(clazz);
    }

    //通过name,以及Clazz返回指定的Bean
    public static  T getBean(String name,Class clazz){
        return getApplicationContext().getBean(name, clazz);
    }


}

(2)获取到shiro里面的当前登录用户信息

package com.datang.dmp.modules.passback.web;

import com.datang.dmp.modules.sys.utils.UserUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;

/**
 *
 * Created by on 2019/7/5.
 */
@Configuration
public class WebSocketConfig  extends ServerEndpointConfig.Configurator{
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
        // 将用户信息存储到socket的配置里
        sec.getUserProperties().put("user", UserUtils.getUser());
        super.modifyHandshake(sec, request, response);
    }
}

 

你可能感兴趣的:(websocket)