大概思路: 首先用户登陆 获取用户信息存储到httpsession中,然后客户端链接服务端websocket,首先HandshakeInterceptor这个拦截器会拦截请求 调用 beforeHandshake方法进行握手操作,然后吧httpsession 和 websocketsession进行绑定 , 然后执行MySocketHandler 类得afterConnectionEstablished方法 ,创建一个静态map 吧所有连接得客户端存到map里 以便用户退出登陆时清空session。 客户端发来消息会调用 handleMessage
1、pom中添加依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>${spring-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>${spring-version}</version> </dependency>
2、spring-mvc.xml 中添加websocket拦截
<websocket:handlers allowed-origins="*"> <websocket:mapping path="/ws" handler="myHandler"/> <websocket:handshake-interceptors> <bean class="com.cloudunicomm.interceptor.HandshakeInterceptor"/> </websocket:handshake-interceptors> </websocket:handlers> <bean id="myHandler" class="com.cloudunicomm.interceptor.MySocketHandler"/>
3、添加MySocketHandler类
package com.cloudunicomm.interceptor; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.ListOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; import com.alibaba.fastjson.JSONObject; import com.cloudunicomm.vo.User; public class MySocketHandler extends TextWebSocketHandler { private static final Logger logger = LoggerFactory.getLogger(MySocketHandler.class); // 线上人数 private static int count; private static CopyOnWriteArraySet<WebSocketSession> set = new CopyOnWriteArraySet<>(); public static Map<String,WebSocketSession> sessionid = new HashMap<String,WebSocketSession>(); private WebSocketSession session; @Autowired private RedisTemplate<String,String> redisTemplate; @Override public void afterConnectionEstablished(WebSocketSession session) { Map<String, Object> map = session.getAttributes(); String userid = map.get("userid").toString(); sessionid.put(userid, session); this.session = session; try{ set.add(this.session); }catch(Exception e) { e.printStackTrace(); } MySocketHandler.addOnlineCount(); System.out.println("目前连接人数:" + getOnlineCount()); } public void afterConnectionClosed(WebSocketSession session,CloseStatus closeStatus) { this.session = session; String userid = session.getAttributes().get("userid").toString(); redisTemplate.delete("SEAT_"+userid); session.getAttributes().remove("userid"); set.remove(this.session); subOnlineCount(); System.out.println("目前连接人数:" + getOnlineCount()); } public void handleMessage(WebSocketSession session,WebSocketMessage<?>message){ System.out.println("来自客户端消息: "+message.getPayload()+ "_"+ session.getId() ); //发送给所有人 /* for(WebSocketSession ssion : set) { try { ssion.sendMessage(message); }catch(IOException e) { e.printStackTrace(); } } */ //解析message 修改用户状态 try{ //id:state, //username:sip:$tU@123.57.144.26:9060 String[] state = message.getPayload().toString().split("_"); ListOperations<String,String> value = redisTemplate.opsForList(); String val = value.rightPop("SEAT_"+state[0]).toString(); User user = (User)JSONObject.toJavaObject(JSONObject.parseObject(val), User.class); user.setState(Integer.parseInt(state[1])); //实际是解析出来得 现在先写死 user.setNext_hop("123.57.144.26:9060/udp"); user.setTo("<sip:$tU@123.57.144.26:9060>"); String struser = JSONObject.toJSONString(user); value.leftPush("SEAT_"+user.getId().toString(), struser); }catch(Exception e){ logger.error(e.getMessage(),e); } } public static int getOnlineCount() { return count; } public static void addOnlineCount() { count++; } public static void subOnlineCount() { count--; } /** * 给指定连接推消息 * @param session * @param message */ public String pushMsg(String sessionid, String message){ for(WebSocketSession ssion : set) { try { if(sessionid.equals(ssion.getId())){ ssion.sendMessage(new TextMessage(message)); return "机器:" + sessionid+ "推送成功"; } }catch(IOException e) { e.printStackTrace(); } } return "推送失败"; } /** * 给全部连接 * @param message * @return */ public String pushMsg(String message) { int i = 0; for(WebSocketSession ssion : set) { try { ssion.sendMessage(new TextMessage(message)); i++; }catch(IOException e) { e.printStackTrace(); } } return "共有" + i + "得到推送"; } }
4、添加 HandshakeInterceptor 类
package com.cloudunicomm.interceptor; import java.util.Map; import javax.servlet.http.HttpSession; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor { /* * 握手前处理动作 */ @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler, Map<String,Object> map)throws Exception { System.out.println("握手前"); if(request instanceof ServletServerHttpRequest){ ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request; HttpSession httpSession = servletRequest.getServletRequest().getSession(true); if(null != httpSession){ // String userid = httpSession.getAttribute("userid").toString(); String userid = httpSession.getAttribute("userid").toString(); map.put("userid",userid); } } return super.beforeHandshake(request, response, handler, map); } @Override public void afterHandshake(ServerHttpRequest request,ServerHttpResponse response,WebSocketHandler wsHandler,Exception ex) { super.afterHandshake(request, response, wsHandler, ex); } }
5、添加TestController
package com.cloudunicomm.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/im") public class TestController { /*@Bean public MySorketHandle mySorketHandle() { return new MySorketHandle(); } */ @RequestMapping("/page") public String page(HttpServletRequest request, HttpServletResponse response) { return "IMpage"; } /*@ResponseBody @RequestMapping("/push") public String push(@RequestParam(required = false) String sessionId, HttpServletResponse response){ String msg= ""; if (StringUtils.isEmpty(sessionId)) { msg =mySorketHandle().pushMsg("服务器推送信息了"); System.out.println(msg); }else{ msg =mySorketHandle().pushMsg(sessionId, "服务器推送信息了"); System.out.println(msg); } return msg; } */ }
6、添加 jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta charset="UTF-8"> <title>socket</title> <script type="text/javascript" src="http://cdn.static.runoob.com/libs/jquery/2.1.1/jquery.min.js"></script> </head> <body> welcome<br /> <input id="text" type="text"/> <button onclick="sendMsg()">sendMsg</button> <hr/> <button onclick="closeWebSocket()">close WebSocketconnection</button> <hr/> <div id="message"></div> </body> <script type="text/javascript"> var websocket = null; //判断浏览器是否支持websocket if('WebSocket' in window) { websocket = new WebSocket("ws://localhost:8081/preSend/websocket"); }else{ $("#message").html("该浏览器不支持实时通信功能"); } window.onbeforeunload = function () { alert("x帆帆帆帆"); websocket.close(); } websocket.onopen= function() { console.log("websocket连接成功"); } websocket.onclose= function() { closewebsocket(); console.log("websocket连接关闭"); } websocket.onmessage= function(event) { console.log("接收消息"); console.log(event); printMsg(event.data); } //打印消息 function printMsg(msg) { $("#message").append(msg+ "<br/>"); } function sendMsg() { var msg = $("#text").val(); websocket.send("3_0"); } function closeWebSocket(){ websocket.close(); } //离线 function closewebsocket(){ alert("用户离线了"); } </script> </html>
7、登陆controller
package com.cloudunicomm.controller; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.ListOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import com.alibaba.fastjson.JSONObject; import com.cloudunicomm.service.UserService; import com.cloudunicomm.utils.ResultMessage; import com.cloudunicomm.vo.User; @Controller public class LoginController { @Autowired private UserService userService; @Autowired private RedisTemplate<String,String> redisTemplate; @CrossOrigin(origins = "*",maxAge = 3000) @RequestMapping("login") @ResponseBody public ResultMessage login(HttpServletRequest request,HttpServletResponse response, @RequestParam(name="username",required=false)String username, @RequestParam(name="password",required=false)String password){ if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){ return ResultMessage.getFail().setMessage("用户名密码不能为空!"); } User user = userService.AuthUserNmaAndPassword(username,password); if(user == null){ return ResultMessage.getFail().setMessage("用户名密码错误!"); } String str = redisTemplate.opsForList().rightPopAndLeftPush("SEAT_"+user.getId(), "SEAT_"+user.getId()); if(null != str){ return ResultMessage.getFail().setMessage("请勿重复登陆!"); } //登陆成功 添加 用户到redis中 islogin修改为在线 state修改为闲 user.setIslogin(0); user.setState(0); ListOperations<String,String> redislist = redisTemplate.opsForList(); String struser = JSONObject.toJSONString(user); //从左向右存压栈 redislist.leftPush("SEAT_"+user.getId().toString(), struser); request.getSession().setAttribute("userid", user.getId()); return ResultMessage.getSuccess().setData(user.getId()); } }
8、注意事项
1)项目中又拦截器要注释掉 否则websocket会链接失败抛出 'Upgrade' header is missing 异常
2)如果是远程调用测试时地址必须是同一个