大概思路: 首先用戶登陸 獲取用戶信息存儲到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)如果是遠程調用測試時地址必須是同一個
