WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。使web更具交互性的,包括Java applet、XMLHttpRequest、Adobe Flash、ActiveXObject、各种Comet技术、服务器发送的事件等等。
客户端和服务器之间通过多个HTTP连接来实现,造成HTTP轮询的滥用,客户端向服务器不断发送HTTP请求来进行询问,导致效率低下。
结合WebSocket API ,WebSocket协议提供了一个用来替代HTTP轮询实现网页到远程主机的双向通信的方法:使用单个TCP连接双向通信,允许服务器主动发送信息给客户端。
在实现websocket连线过程中,需要通过浏览器发出websocket连线请求,然后服务器发出回应,这个过程通常称为“握手” 。在 WebSocket API,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
一些浏览器中缺少对WebSocket的支持。第一个支持WebSocket的ie是ie10。此外,一些限制性代理可能会以某种方式配置,要么阻止尝试进行HTTP升级,要么在一段时间后断开连接,因为它已经打开了太长时间。
因此,回退选项是必要的,而Spring框架提供了基于SockJS协议的透明的回退选项。
REST是一种广泛接受、理解和支持的用于构建web应用程序的体系结构。它是一种体系结构,它依赖于拥有许多url(名词)、少数HTTP方法(动词)和其他原则,如使用超媒体(链接)、保持无状态等等。
相比之下,WebSocket应用程序可能只使用一个URL作为初始HTTP握手。之后所有的消息都在相同的TCP连接上共享和流动。这指向一个完全不同的、异步的、事件驱动的消息体系结构。与传统消息应用程序(如JMS、AMQP)更接近的一种。
Spring Framework 4包含一个新的Spring消息模块,其中包含来自Spring Integration项目的关键抽象,比如消息、MessageChannel、MessageHandler,以及其他可以作为消息传递体系结构的基础。该模块还包括一组用于将消息映射到方法的注释,类似于Spring MVC基于注释的编程模型。
WebSocket最适合的是在web应用程序中,客户端和服务器需要频繁地交换事件和低延迟。主要候选者包括但不限于在金融、游戏、合作等方面的应用。这样的应用程序对时间延迟非常敏感,并且需要在高频率的情况下交换各种各样的消息。
它是低延迟和高频率的消息的组合,可以使WebSocket协议的使用成为关键。即使在这样的应用程序中,仍然可以选择是否所有的客户机-服务器通信都应该通过WebSocket消息来完成,而不是使用HTTP和REST。答案会因应用而异;然而,很可能某些功能可能会在WebSocket和REST API中暴露出来,从而为客户提供其他的选择。此外,REST API调用可能需要向通过WebSocket连接的感兴趣的客户端广播消息。
Spring框架允许@Controller和@RestController类同时拥有HTTP请求处理和WebSocket消息处理方法。此外,Spring MVC请求处理方法或任何应用程序方法,都可以轻松地向所有感兴趣的WebSocket客户端或特定的用户广播一条消息。
以Spring Boot为例:
* 只专注于客户端的API,因为每个服务器端语言有自己的API。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
dependency>
有两种写法
创建和配置一个WebSocketHandler
1.创建一个WebSocket服务器和实现WebSocketHandler一样简单(继承TextWebSocketHandler或BinaryWebSocketHandler)。
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.TextMessage;
public class MyHandler extends TextWebSocketHandler {
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
// ...
}
}
2.有专门的WebSocket Java-config或XML命名空间支持将上面的WebSocket处理程序映射到一个特定的URL。
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(myHandler(), "/myHandler");
}
@Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}
与之等价的XML配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
http://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers>
<websocket:mapping path="/myHandler" handler="myHandler"/>
websocket:handlers>
<bean id="myHandler" class="org.springframework.samples.MyHandler"/>
beans>
定制WebSocket握手
定制初始HTTP WebSocket握手请求的最简单方法是通过一个handshake截取程序,它公开了“前”和“后”握手方法。这样的拦截器可用于阻止握手或对WebSocketSession提供任何属性。例如,有一个内置的拦截器用于将HTTP会话属性传递给WebSocket会话:
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyHandler(), "/myHandler")
.addInterceptors(new HttpSessionHandshakeInterceptor());
}
}
与之等价的XML配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
http://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers>
<websocket:mapping path="/myHandler" handler="myHandler"/>
<websocket:handshake-interceptors>
<bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
websocket:handshake-interceptors>
websocket:handlers>
<bean id="myHandler" class="org.springframework.samples.MyHandler"/>
beans>
配置WebSocket引擎
1.每个底层WebSocket引擎都暴露了控制运行时特征的配置属性,比如消息缓冲区大小、闲置超时等。
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxBinaryMessageBufferSize(8192);
return container;
}
}
与之等价的XML配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
http://www.springframework.org/schema/websocket/spring-websocket.xsd">
<bean class="org.springframework...ServletServerContainerFactoryBean">
<property name="maxTextMessageBufferSize" value="8192"/>
<property name="maxBinaryMessageBufferSize" value="8192"/>
bean>
beans>
2.对于Jetty,您需要提供预配置的Jetty WebSocketServerFactory,并通过WebSocket Java配置将其插入到Spring的DefaultHandshakeHandler中。
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(echoWebSocketHandler(),
"/echo").setHandshakeHandler(handshakeHandler());
}
@Bean
public DefaultHandshakeHandler handshakeHandler() {
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
policy.setInputBufferSize(8192);
policy.setIdleTimeout(600000);
return new DefaultHandshakeHandler(
new JettyRequestUpgradeStrategy(new WebSocketServerFactory(policy)));
}
}
与之等价的XML配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
http://www.springframework.org/schema/websocket/spring-websocket.xsd">
<bean class="org.springframework...ServletServerContainerFactoryBean">
<property name="maxTextMessageBufferSize" value="8192"/>
<property name="maxBinaryMessageBufferSize" value="8192"/>
bean>
beans>
打开一个连接,为连接创建事件监听器,断开连接,消息时间,发送消息返回到服务器,关闭连接。
// 创建一个Socket实例
var socket = new WebSocket('ws://localhost:8080');
// 打开Socket
socket.onopen = function(event) {
// 发送一个初始化消息
socket.send('I am the client and I\'m listening!');
// 监听消息
socket.onmessage = function(event) {
console.log('Client received a message',event);
};
// 监听Socket的关闭
socket.onclose = function(event) {
console.log('Client notified socket has closed',event);
};
// 关闭Socket....
//socket.close()
};
项目链接a
使用@OnOpen,@OnClose,@OnMessage,@OnError等注解,编写相应的函数处理js的相关请求。
项目链接b