第一步:利用HandshakeRequest获取session(web端为session,App端为token)
//使用WebListener,监听前端请求,解决直接利用HandshakeRequest获取session为空的问题
@javax.servlet.annotation.WebListener()
public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator implements ServletRequestListener {
private static HttpSession session;
private static HttpServletRequest req;
/***
* 本函数在连接转为WebSocket时调用,可从请求中获取所需要的用户信息
*/
@SuppressWarnings("unchecked")
@Override
public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
RedisService redisService = Global.getIoc().get(RedisService.class);
String token = null;
//首先如果有SessionID的情况,先取出SessionID
if(null != session && null != session.getId()){
//根据SessionID去redis里查询token
token = redisService.get(session.getId());
}
if(req.getHeader("token") != null){
//将header中的token取出
token = req.getHeader("token");
}
if(request.getParameterMap().get("token") != null){
token = request.getParameterMap().get("token").get(0);
}
//判断token是否为空,若是则为非法请求
if(null != token && token.length() > 0){
System.out.println("token : " + token);
//利用token从redis中获取用户信息
String userInfoJson = redisService.get(token);
//该用户的信息在redis中不存在
if(null == userInfoJson || userInfoJson.length() <= 0){
config.getUserProperties().put("deviceId", "3");
return;
}
//将用户信息解析成一个Map
Map<String, Object> userInfo = (Map<String, Object>) JSON.parse(userInfoJson);
//如果该Map为空,则属于登录验证失败
if(userInfo.isEmpty()){
config.getUserProperties().put("deviceId", "2");
return;
}
//利用ioc获取一个dao
Dao dao = Global.getIoc().get(Dao.class);
//判断该用户是否绑定设备
if(dao.count(Device.class, Cnd.where("uid", "=" , userInfo.get("uId"))) == 0){
config.getUserProperties().put("deviceId", "1");
return;
}
//将设备id存储在config中
Device device = dao.query(Device.class, Cnd.where("uid", "=" , userInfo.get("uId"))).get(0);
config.getUserProperties().put("deviceId", device.getTID());
//判断该deviceid是否已经建立了连接
}else{
config.getUserProperties().put("deviceId", "4");
return;
}
}
@Override
public void requestDestroyed(ServletRequestEvent arg0) {
}
@Override
public void requestInitialized(ServletRequestEvent arg0) {
req = (HttpServletRequest) (arg0.getServletRequest());
if(req.getServletPath().endsWith("/w/location/trace")){
session = req.getSession(false);
}
}
}
第二步:从远程服务器接收数据,通过redis做数据处理进行返回,websocket发送给前端
/**
* 这里采用的是Tomcat8的注解模式,不需要在web.xml里面配置
*/
@ServerEndpoint(value = "/w/location/trace", configurator = GetHttpSessionConfigurator.class)
public final class WsLocationModule {
private static final Log log = Logs.get();
private static final Map<String, Session> sessionMap = new ConcurrentHashMap<String, Session>();
private final RedisService redisService = Global.getRedisService();
/**
* 连接建立成功调用的方法
*
* @param session
* 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
* @throws EncodeException
*/
@OnOpen
public void onOpen(Session session, EndpointConfig conf) throws EncodeException {
// 从config中取出设备ID
String deviceId = (String) conf.getUserProperties().get("deviceId");
// 对在GetHttpSessionConfigurator中对用户的验证进行具体的处理
if (deviceId.equals("1")) {
sendMessage(session, JSON.toJSONString(Result.doError("该用户未绑定设备")));
} else if (deviceId.equals("2")) {
sendMessage(session, Json.toJson(Result.doError("获取用户数据失败")));
} else if (deviceId.equals("3")) {
sendMessage(session, Json.toJson(Result.doError("用户登录过期")));
} else if (deviceId.equals("4")) {
sendMessage(session, Json.toJson(Result.doError("请先登录")));
} else if (deviceId.length() > 0) {
System.out.println("设备id:" + deviceId);
// 将session放置在sessionMap中保存,以deviceId作为键
sessionMap.put(deviceId, session);
if (null != redisService && null != redisService.get("last_location_" + deviceId)) {
// 从redis中取出该终端最近的位置消息
String lastLocation = redisService.get("last_location_" + deviceId);
DeviceMessage deviceMessage = Json.fromJson(DeviceMessage.class, lastLocation);
// 判断位置信息是否存在
if (null != deviceMessage) {
// 取出经纬度,封装成一个map,并发送给前端,当有redis中该终端的数据有更新时,在RedisPubSub中推送给前端
/*Map<String, Object> coordinate = new TreeMap<>();
coordinate.put("long", deviceMessage.getLongitude());
coordinate.put("lat", deviceMessage.getLatitude());*/
deviceMessage.setType(Type.Send);
sendMessage(session, JSON.toJSONString(Result.doSuccess("连接成功", deviceMessage)));
}
}
} else {
sendMessage(session, "连接失败");
}
}
@OnMessage
public void process(Session session, String message) {
// 目前预期不会受到任何传入的信息。
// throw new NoSuchElementException();
}
@OnClose
public void end(Session session) {
System.out.println("断开设备" + session.getUserProperties().get("deviceId"));
sessionMap.remove(session.getUserProperties().get("deviceId"), session);
if (session.isOpen()) {
try {
session.close();
} catch (IOException e) {
log.error(e);
e.printStackTrace();
}
}
System.out.println("session " + session.getId() + " close.");
}
@OnError
public void error(Session session, java.lang.Throwable throwable) {
// FIXME 记录错误并关闭会话。
if (throwable.getMessage() != null) {
sessionMap.put(throwable.getMessage(), session);
try {
session.close();
} catch (IOException e) {
log.error(e);
e.printStackTrace();
}
}
System.err.println("session " + session.getId() + " error:" + throwable);
}
public static boolean sendMessage(String user, String message) {
// TODO 从map中取得相关的session
Session session = sessionMap.get(user);
// TODO 通过session将message传出去
if (session == null) {
System.out.println("session不存在");
return false;
}
System.out.println("session存在");
sendMessage(session, message);
return true;
}
public static void sendMessage(Session session, String message) {
// 处于效率考虑,将该方法切换到异步实现。
session.getAsyncRemote().sendText(message, new SendHandler() {
@Override
public void onResult(SendResult result) {
if (!result.isOK()) { // 或考虑result.getException()!=null
log.debug("发送消息失败:" + message);
sessionMap.remove(session);
try {
session.close();
} catch (IOException e) {
// 无理由关闭
}
}
}
});
}