WebSocket协议提供了一个套接字实现全双工通信的功能。除了其他的功能之外,它能够实现web浏览器和服务器之间的异步通信。全双工意味着服务器可以发送消息给服务器,浏览器也可以发送消息给服务器。
Spring配置WebSocket环境:
目前浏览器对 WebSocket 的支持如下:
- IE:10.0(完整支持);
- FireFox:4.0(部分支持),6.0(完整支持);
- Chrome:4.0(部分支持),13.0(完整支持);
- Opera:11.0(部分支持),12.10(完整支持);
- Safari:5.0(部分支持),12.10(完整支持);
- IOS Safari:4.2(部分支持),6.0(完整支持);
- Android Browser:4.4(完整支持);
目前部分服务端对于 WebSocket 的支持情况如下:
- JavaEE 7.0+
- Tomcat 8.0+
- Jetty 7.0+
- WebSocket 通信的 URL 是以
“ws://”
或“wss://”
开头的,分别对应“http://”
,“https://”
;
Spring 从 4.0 开始全面支持 WebSocket ,该支持包括以下几个方面:
- 发送和接收信息的 API;
- 用于发送信息的模板;
- 支持 SockJS(用于解决浏览器、服务器及代理不支持 WebSocket 的问题);
在 Spring 中使用 WebSocket 需要导入以下依赖包:
org.springframework:spring-web
org.springframework:spring-webmvc
org.springframework:spring-websocket
本例使用的环境是:tomacat8+spring4.0.6
WebSocket样例
第一步:创建JinNangHandler
package com.sanguo;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
public class JinNangHandler extends AbstractWebSocketHandler{
public void handleTextMessage(WebSocketSession session, TextMessage message)throws Exception{
System.out.println("received message is: "+message.getPayload());
Thread.sleep(2000);
session.sendMessage(new TextMessage("火烧赤壁"));
}
public void afterConnectionEstablished(WebSocketSession session) throws Exception{
System.out.println("connection established");
}
public void afterConnectionClosed(WebSocketSession session,CloseStatus status) throws Exception{
System.out.println("connection closed");
}
}
AbstractWebSocketHandler是接口WebSocketHandler的一个抽象实现,WebSocketHandler定义了五个方法,需要子类实现,其中最重要的三个方法是:
afterConnectionEstablished(WebSocketSession session)throws Exception
:链接建立触发handlerMessage(WebSocketSession session,WebSocketMessage<?> message)throws Exception
:主要实现方法,用于从浏览器接受消息,并且发送消息返回给服务器。afterConnectionClosed(WebSocketSession session,CloseStatus status)throws Exception
:链接关闭触发afterConnectionEstablished(WebSocketSession session)throws Exception
:链接建立触发
AbstractWebSocketHandler实现了这五个方法,所以可以直接继承AbstractWebSocketHandler,并选择重载上面三个方法。尽管AbstractWebSocketHandler是一个抽象类但是它并不要求我们必须重载任何特定的方法。相反,他让我们来决定重载哪一个方法。除了重载AbstractWebSocketHandler中所定义的五个方法以外:我们还可以重载AbstractWebSocketHandler中所定义的三个方法:
- handlerBinaryMessage()
- handlePongMessage()
- handleTextMessage()
这三个方法只是handleMessage()方法的具体化,每个方法对应于某一种特定类型的消息。
因为JinNangHandler将会处理文本类型的“”发送信息:黄盖!“”,因此我们应该重载handleTextMessage()方法。当有文本消息抵达的时候,我们会在控制台输出收到的文本信息,在两秒钟的模拟延迟之后,会在同一个链接上返回另外一条文本消息。
JinNangHandler所没有重载的方法都有AbstractWebSocketHandler以空操作的方式进行了实现,这意味着JinNangHandelr也能处理二进制和pong消息,只是对这些消息不进行任何操作而已。
第二步:在java配置中,启用WebSocket并映射消息处理器
package com.sanguo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(jinNangHandler(), "/jinnang");
}
@Bean
public JinNangHandler jinNangHandler() {
return new JinNangHandler();
}
}
registerWebSocketHandlers()方法是注册消息处理器的关键。通过重载该方法,我们得到了一个WebSocketHandlerRegistry对象,通过该对象可以调用addHandler()来注册信息处理器。在本例中,我们注册了JinNangHandler(以bean的方式进行声明)并将其与"/jinnang"路径相关联。
第三步:实现WebInitializer,加载WebSocketConfig配置类,并负责在Web应用启动时启动Spring。
package com.sanguo;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {WebSocketConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
至此服务端代码我们已经写好了
客户端代码
jinnang.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
//创建 websocket
var url = 'ws://localhost:8080/SpringTest/jinnang';
var sock = new WebSocket(url);
//websocket 连接行为
sock.onopen = function(){
console.log('开启 websocket 连接');
sendJinNang();
};
//websocket 接受到信息行为
sock.onmessage = function(e){
console.log('接受信息',e.data);
setTimeout(function(){sendJinNang()},2000);
};
//websocket 关闭行为
sock.onclose = function(){
console.log('关闭 websocket 连接');
};
function sendJinNang() {
console.log('发送信息:黄盖!');
sock.send('form client: 黄盖!');
}
</script>
</head>
<body>
Hello world!
</body>
</html>
脚本所做的第一件事情就是创建WebSocket实例。对于支持WebSocket的浏览器来说,这个类型是原生的。通过创建WebSocket实例,实际上打开了到给定URL的WebSocket。在本例中,URL使用了"ws://"前缀,这表明这是一个基本的WebSocket链接。如果是安全WebSocket的话,协议的前缀将会是"wss://"。
WebSocket创建完毕之后,接下来的代码建立了WebSocket的事件处理功能。注意,WebSocket的onopen,onmessage和onclose事件对应于JinNangHandler的afterConnectionEstablished(),handleTextMessage(),和afterConnectionClosed()方法。