springMVC+websockt实现
2018-11-21 本文已影响0人
请让我想一想
场景还原:即时通讯,类似微信qq这种效果
代码实现:
环境:
- Tomcat 7+
- Maven+SpringMVC+Mybatis
代码实现:
流程梳理:前端请求后端,建立socket连接,成功后保持连接状态,
前端代码:
$(document).ready(function () {
// 指定websocket路径
var websocket;
if ('WebSocket' in window) {
// Spring-websocket 自己项目名称
websocket = new WebSocket("ws://localhost:8080/Spring-websocket/ws?uid=" +${sessionScope.uid});
} else if ('MozWebSocket' in window) {
websocket = new MozWebSocket("ws://localhost:8080/Spring-websocket/ws" +${sessionScope.uid});
} else {
websocket = new SockJS("http://localhost:8080/Spring-websocket/ws/sockjs" +${sessionScope.uid});
}
// 上面执行完成,就已经与后端建立好了socket连接
// 后端有消息通知会触发这个事件
websocket.onmessage = function (event) {
var data = JSON.parse(event.data);
if (data.from > 0 || data.from == -1) {//用户或者群消息
// 接收服务端的实时消息并添加到HTML页面中
$("#log-container").append("<div class='bg-info'><label class='text-danger'>" + data.fromName + " " + data.date + "</label><div class='text-success'>" + data.text + "</div></div><br>");
// 滚动条滚动到最低部
scrollToBottom();
} else if (data.from == 0) {//上线消息
if (data.text != "${sessionScope.username}") {
$("#users").append('<a href="#" onclick="talk(this)" class="list-group-item">' + data.text + '</a>');
alert(data.text + "上线了");
}
} else if (data.from == -2) {//下线消息
if (data.text != "${sessionScope.username}") {
$("#users > a").remove(":contains('" + data.text + "')");
alert(data.text + "下线了");
}
}
};
$("#send").click(function () {
$.post("getuid", {"username": $("body").data("to")}, function (d) {
var v = $("#myinfo").val();
if (v == "") {
return;
} else {
var data = {};
data["from"] = "${sessionScope.uid}";
data["fromName"] = "${sessionScope.username}";
data["to"] = d.uid;
data["text"] = v;
// 发送socket信息,首先会被socket核心类拦截,在里面会根据参数获取指定通知的用户,发送通知
websocket.send(JSON.stringify(data));
$("#log-container").append("<div class='bg-success'><label class='text-info'>我 " + new Date() + "</label><div class='text-info'>" + v + "</div></div><br>");
scrollToBottom();
$("#myinfo").val("");
}
});
});
});
后端代码:3个核心类
/**
* Socket建立连接(握手)和断开
*/
public class HandShake implements HandshakeInterceptor {
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
System.out.println("Websocket:用户[ID:" + ((ServletServerHttpRequest) request).getServletRequest().getSession(false).getAttribute("uid") + "]已经建立连接");
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession(false);
// 标记用户
Long uid = (Long) session.getAttribute("uid");
if(uid!=null){
attributes.put("uid", uid);
}else{
return false;
}
}
return true;
}
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
System.out.println("after hand");
}
}
/**
* Socket处理器
*/
@Component
public class MyWebSocketHandler implements WebSocketHandler {
//用于保存HttpSession与WebSocketSession的映射关系
public static final Map<Long, WebSocketSession> userSocketSessionMap;
@Autowired
LoginService loginservice;
static {
userSocketSessionMap = new ConcurrentHashMap<Long, WebSocketSession>();
}
/**
* 建立连接后,把登录用户的id写入WebSocketSession
*/
public void afterConnectionEstablished(WebSocketSession session)
throws Exception {
Long uid = (Long) session.getAttributes().get("uid");
String username=loginservice.getnamebyid(uid);
if (userSocketSessionMap.get(uid) == null) {
userSocketSessionMap.put(uid, session);
Message msg = new Message();
msg.setFrom(0L);//0表示上线消息
msg.setText(username);
this.broadcast(new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg)));
}
}
/**
* 消息处理,在客户端通过Websocket API发送的消息会经过这里,然后进行相应的处理
*/
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
if(message.getPayloadLength()==0)
return;
Message msg=new Gson().fromJson(message.getPayload().toString(),Message.class);
msg.setDate(new Date());
sendMessageToUser(msg.getTo(), new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg)));
}
/**
* 消息传输错误处理
*/
public void handleTransportError(WebSocketSession session,
Throwable exception) throws Exception {
if (session.isOpen()) {
session.close();
}
Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap.entrySet().iterator();
// 移除当前抛出异常用户的Socket会话
while (it.hasNext()) {
Entry<Long, WebSocketSession> entry = it.next();
if (entry.getValue().getId().equals(session.getId())) {
userSocketSessionMap.remove(entry.getKey());
System.out.println("Socket会话已经移除:用户ID" + entry.getKey());
String username=loginservice.getnamebyid(entry.getKey());
Message msg = new Message();
msg.setFrom(-2L);
msg.setText(username);
this.broadcast(new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg)));
break;
}
}
}
/**
* 关闭连接后
*/
public void afterConnectionClosed(WebSocketSession session,CloseStatus closeStatus) throws Exception {
System.out.println("Websocket:" + session.getId() + "已经关闭");
Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap.entrySet().iterator();
// 移除当前用户的Socket会话
while (it.hasNext()) {
Entry<Long, WebSocketSession> entry = it.next();
if (entry.getValue().getId().equals(session.getId())) {
userSocketSessionMap.remove(entry.getKey());
System.out.println("Socket会话已经移除:用户ID" + entry.getKey());
String username=loginservice.getnamebyid(entry.getKey());
Message msg = new Message();
msg.setFrom(-2L);//下线消息,用-2表示
msg.setText(username);
this.broadcast(new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg)));
break;
}
}
}
public boolean supportsPartialMessages() {
return false;
}
/**
* 给所有在线用户发送消息
* @param message
* @throws IOException
*/
public void broadcast(final TextMessage message) throws IOException {
Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap.entrySet().iterator();
//多线程群发
while (it.hasNext()) {
final Entry<Long, WebSocketSession> entry = it.next();
if (entry.getValue().isOpen()) {
// entry.getValue().sendMessage(message);
new Thread(new Runnable() {
public void run() {
try {
if (entry.getValue().isOpen()) {
entry.getValue().sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
/**
* 给某个用户发送消息
*
* @param userName
* @param message
* @throws IOException
*/
public void sendMessageToUser(Long uid, TextMessage message) throws IOException {
WebSocketSession session = userSocketSessionMap.get(uid);
if (session != null && session.isOpen()) {
session.sendMessage(message);
}
}
}
/**
* WebScoket配置处理器
*/
@Component
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
@Resource
MyWebSocketHandler handler;
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(handler, "/ws").addInterceptors(new HandShake());
registry.addHandler(handler, "/ws/sockjs").addInterceptors(new HandShake()).withSockJS();
}
}
spring-mvc.xml配置
// 配置websocket 3个核心配置类的包,其他配置按自己项目定义即可
<context:component-scan base-package="controller,service.impl,websocket" />
结束:这只是部分socket代码抽取,参考了码云项目
源码可直接跳转:https://gitee.com/shenzhanwang/Spring-websocket.git
感谢这个作者的无私奉献.