大概思路: 首先用户登陆 获取用户信息存储到httpsession中,然后客户端链接服务端websocket,首先HandshakeInterceptor这个拦截器会拦截请求 调用 beforeHandshake方法进行握手操作,然后吧httpsession 和 websocketsession进行绑定 , 然后执行MySocketHandler 类得afterConnectionEstablished方法 ,创建一个静态map 吧所有连接得客户端存到map里 以便用户退出登陆时清空session。 客户端发来消息会调用 handleMessage
1、pom中添加依赖
org.springframework
spring-websocket
${spring-version}
org.springframework
spring-messaging
${spring-version}
2、spring-mvc.xml 中添加websocket拦截
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 set = new CopyOnWriteArraySet<>();
public static Map sessionid = new HashMap();
private WebSocketSession session;
@Autowired
private RedisTemplate redisTemplate;
@Override
public void afterConnectionEstablished(WebSocketSession session) {
Map 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 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("");
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 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
pageEncoding="utf-8"%>
socketwelcome
sendMsg
close WebSocketconnection
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+ "
");
}
function sendMsg() {
var msg = $("#text").val();
websocket.send("3_0");
}
function closeWebSocket(){
websocket.close();
}
//离线
function closewebsocket(){
alert("用户离线了");
}
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 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 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)如果是远程调用测试时地址必须是同一个