WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——可以通俗的解释为服务器主动发送信息给客户端。
websocket允许通过JavaScript建立与远程服务器的连接,从而实现客户端与服务器间双向的通信。
在websocket中有两个方法:
1、send() 向远程服务器发送数据
2、close() 关闭该websocket连接
websocket同时还定义了几个监听函数
1、onopen 当网络连接建立时触发该事件
2、onerror 当网络发生错误时触发该事件
3、onclose 当websocket被关闭时触发该事件
4、onmessage 当websocket接收到服务器发来的消息时触发的事件,也是通信中最重要的一个监听事件。
websocket还定义了一个readyState属性,这个属性可以返回websocket所处的状态:
1、CONNECTING(0) websocket正尝试与服务器建立连接
2、OPEN(1) websocket与服务器已经建立连接
3、CLOSING(2) websocket正在关闭与服务器的连接
4、CLOSED(3) websocket已经关闭了与服务器的连接
websocket的url开头是ws,如果需要ssl加密可以使用wss,当我们调用websocket的构造方法构建一个websocket对象(new WebSocket(url))之后,就可以进行即时通信了。
Spring官方文档:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html
spring4.0以上支持websocket,要求servlet-api必须是3.0+
WebSocket:
org.springframework
spring-websocket
4.0.2.RELEASE
org.springframework
spring-messaging
4.0.2.RELEASE
javax.servlet
javax.servlet-api
3.1.0
web.xml的namespace也要确保是3.0+
// 使用 来选择性地启用或禁用Web片段(和SCI扫描)
如果要支持异步的servlet3.x,可以在web.xml下的servlet和filter里面加上
首先是配置文件类(注解),给服务器添加websocket服务
@Configuration
@EnableWebSocket
public class MyWebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
@Autowired
MyWebSocketHandler handler;
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
//前台 可以使用websocket环境
webSocketHandlerRegistry.addHandler(handler, "/chats/connect/socket").addInterceptors(new MyHandShakeInterceptor());
//前台 不可以使用websocket环境,则使用sockjs进行模拟连接
webSocketHandlerRegistry.addHandler(handler, "/chats/connect/socket/sockjs").addInterceptors(new MyHandShakeInterceptor()).withSockJS();
}
}
然后是创建握手拦截器
public class MyHandShakeInterceptor implements HandshakeInterceptor {
private static final Logger logger = LogManager.getLogger(MyWebSocketHandler.class);
public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map map) throws Exception {
if (serverHttpRequest instanceof ServletServerHttpRequest) {
HttpServletRequest servletRequest = ((ServletServerHttpRequest) serverHttpRequest).getServletRequest();
//当前的登录者
Long userId = (Long) servletRequest.getSession().getAttribute("userId");
if(userId!=null){
map.put("uid", userId);//为服务器创建WebSocketSession做准备
logger.info("用户id:"+userId+" 加入");
}else{
logger.error("没有获取session中的当前登陆者信息");
}
}
return true;
}
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
logger.info("握手后");
}
}
最后是创建websocket的处理类
@Component
public class MyWebSocketHandler implements WebSocketHandler {
@Autowired
private ChatService chatService;
@Autowired
private ChatMessageService chatMessageService;
private static final Logger logger = LogManager.getLogger(MyWebSocketHandler.class);
// 保存所有的用户session
private final static Map sessions = new HashMap<>();
//握手实现连接后
@Override
public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
logger.info("连接成功。。。。。。");
Long userId = (Long) webSocketSession.getAttributes().get("uid");
if(sessions.get(userId) == null)
sessions.put(userId,webSocketSession);
logger.info("session: "+sessions);
}
//发送信息前的处理
@Override
public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage> webSocketMessage) throws Exception {
logger.info("用户"+webSocketSession.getAttributes().get("uid")+":发送消息:" + webSocketMessage.getPayload().toString());
String message = webSocketMessage.getPayload().toString();
String[] buf = message.split(":");
if(buf.length == 2){ //切换聊天,用来标记发送方的信息已读
Long chatId = Long.valueOf(buf[1]);
chatService.setRead(chatId,(Long) webSocketSession.getAttributes().get("uid"));
}
if(buf.length == 3){ //发送消息
Long chatId = Long.valueOf(buf[1]);
String content = buf[2];
Chat chat = chatService.getChat(chatId);
Long userFromId = (Long) webSocketSession.getAttributes().get("uid"); //当前登录者为发送者
Long userToId = null;
if(userFromId == chat.getUserId()){
userToId = chat.getOppositeUser().getId();
}else{
userToId = chat.getUserId();
}
//将信息保存至数据库
try {
ChatMessage chatMessage = chatMessageService.addChatMessage(userFromId, chat.getId(), content);
String sendMsg = "{\"chatId\":"+chatId+",\"message\":{\"uuid\":\""+chatMessage.getUuid()+"\",\"content\":\""+chatMessage.getContent()+
"\",\"createdAt\":"+chatMessage.getCreatedAt().getTime()+",\"user\":{\"id\":"+chatMessage.getUser().getId()+",\"slug\":\""+chatMessage.getUser().getSlug()+
"\",\"nickname\":\""+chatMessage.getUser().getNickname()+"\",\"avatar\":\""+chatMessage.getUser().getAvatar().getPath()+"\"}}}";
logger.info("接收的消息格式:"+sendMsg);
//发送Socket信息
sendMessageToUser(userToId, new TextMessage(sendMsg,true),webSocketSession);
}catch (Exception e){
e.printStackTrace();
}
}
//将JSON格式的消息通过Gson转换成Map
//通过getPayload().toString()获取消息具体内容
//Map msg = new Gson().fromJson(webSocketMessage.getPayload().toString(),new TypeToken
登录页面
login.html
登录页面
登录
用户名
密码
验证码
chat.html
Title
chat.js
$(function() {
var websocket;
// 首先判断是否支持WebSocket
if('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:8080/chats/connect/socket");
} else if('MozWebSocket' in window) {
websocket = new MozWebSocket("ws://localhost:8080/chats/connect/socket");
} else {
websocket = new SockJS("http://localhost:8080/chats/connect/socket/sockjs");
}
// 打开时
websocket.onopen = function(event) {
console.log("连接成功");
console.log(event);
};
// 处理消息时
websocket.onmessage = function(event) {
$("#msg").append(event.data);
console.log("处理消息");
console.log(event);
};
websocket.onerror = function(event) {
console.log("连接失败");
console.log(event);
};
websocket.onclose = function(event) {
console.log("socket连接断开");
console.log(event);
};
// 点击了发送消息按钮的响应事件
$("#TXBTN").click(function(){
// 获取消息内容
var text = $("#tx").val();
// 判断
if(text == null || text == ""){
alert(" content can not empty!!");
return false;
}
//var msg = {
// content: text
//};
// 发送消息
//websocket.send(JSON.stringify(msg));
websocket.send("message:"+1+":"+text);
});
});
参考文章:
Spring MVC 自学杂记(四) -- Spring+SpringMVC+WebSocket
SSM框架+WebSocket实现网页聊天(Spring+SpringMVC+MyBatis+WebSocket)