使用websocket踩到的坑儿,,,

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


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

2.使用websocket

(1)前端

<script type="text/javascript">
	var webSocket = null;
	// 创建 webSocket 实例  wss。 http:-ws
	var wsProtocol = "wss";
	var schema = "<%=scheme%>";
	if (schema == "https") {
		wsProtocol = "wss";
	} else {
		wsProtocol = "ws";
	}
	var url = wsProtocol + "://<%=basePath %>/addressFileWebSocket?addressFile";
	if ('WebSocket' in window) {
		webSocket = new WebSocket(url);//启动open方法,开始建立连接
	} else if ('MozWebSocket' in window) {
		webSocket = new MozWebSocket(url);
	} else {
	}
	webSocket.onopen = onOpen;
	webSocket.onmessage = onMessage;
	webSocket.onerror = onError;
	webSocket.onclose = onClose;

	function onOpen(openEvt) {
		console.log("webSocket连接成功...");
	}
	function onMessage(evt) {
		var data = evt.data;
		console.log("webSocket收到消息服务器发送的消息==>" + data);
		if(data != ''){
			var fileId = data.split("-")[0];
			var time = data.split("-")[1];
			window.parent.layer.alert('温馨提示:成功处理!', function (index) {
				window.parent.layer.close(index);
			});
			doSend(time, "close");
			window.location.reload(true);//刷新页面
		}
	}
	function onError() {
		console.log("webSocket连接错误...");
	}
	function onClose(e) {
		console.log("webSocket连接关闭...");
	}
	//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
	window.onbeforeunload = function () {
		webSocket.close();
	};
	// flag=open 请求回调状态,flag=close 请求关闭连接
	function doSend(time, flag) {
		if (webSocket.readyState == webSocket.OPEN) {
			webSocket.send(time + "-" + flag);
			console.log("发送" + time + "成功!");
		} else {
			console.log("连接" + time + "失败!");
		}
	}
	window.close = function (e) {
		webSocket.onclose();
	}

</script>

(2)后台

装信息的并发容器

public class MyWebSocketMap {
    private static ConcurrentMap<String, MyWebSocket> myWebSocketConcurrentHashMap = new ConcurrentHashMap<String, MyWebSocket>();

    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<MyWebSocket> getValues() {
        return myWebSocketConcurrentHashMap.values();
    }

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

    public static List<String> getAllKey() {
        List<String> allKey = new ArrayList<String>();
        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<DataPlanWebSocket> webSocketSet = new CopyOnWriteArraySet<DataPlanWebSocket>();
    ApplicationContext act = SpringUtil.getApplicationContext();
    TDmAddressFileService addressFileService =  act.getBean(TDmAddressFileService.class);
   

    /**
     * <p>打开列表界面:初始化连接成功调用的方法</p>
     *
     * @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);
    }

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

    /**
     * <p>收到客户端消息后调用的方法</p>
     *
     * @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);
            }
        }
        //}
    }

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

    /**
     * <p>服务器给客户端 发送消息方法。</p>
     *
     * @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> T getBean(Class<T> clazz){
        return getApplicationContext().getBean(clazz);
    }

    //通过name,以及Clazz返回指定的Bean
    public static <T> T getBean(String name,Class<T> 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);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值