Spring:SpringMVC对WebSocket的支持

1 使用WebSocket

通常应用程序之间发送消息会使用如JMS、AMQP等技术,但是如果要实现浏览器与服务器的全双工通信,则以上技术使不适合用的,但在互联网时代,这样的需求是普遍存在的。随着HTML5的但僧,一个新的协议也应运而生,这便是WebSocket协议,它很好的解决了浏览器与服务器全双工通信的问题,而且相对于传统的解决方案能更好的节省服务器资源和带宽并达到实时通信。Java 7已经支持WebSocket协议,相应的Spring 4.0时也对WebSocket通信提供了支持,主要有以下几点:

[1] 发送和接收消息的API。

[2] 用来发送消息的模板。

[3] 支持SockJS,用来解决浏览器、服务器及代理不支持WebSocket的问题。

位于WebSocket一段的应用发送消息,而另一端的应用处理消息。因为它是全双工通信的,所以每一端可以发送又可以处理消息。但是WebSocket最为常见的应用场景是实现服务器和基于浏览器的应用之间的通信。在浏览器中使用JavaScript开启一个到达服务器的链接,服务器通过这个链接发送更新到浏览器中。相对于传统的在Web端轮训服务器及使用Flash中的Socket和XMLSocket,这种方式更加高效和便捷。

Spring提供了一个WebSocketHandler接口,该接口定义了5个WebSocket相关的接口方法。Spring拥有一个WebSocketHandler接口的抽象实现类AbstractWebSocketHandler,继承该类便可以选择性的实现感兴趣的方法。

org.springframework.web.socket.handler.AbstractWebSocketHandler

public abstract class AbstractWebSocketHandler implements WebSocketHandler {
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    }
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        if (message instanceof TextMessage) {
            handleTextMessage(session, (TextMessage) message);
        }
        else if (message instanceof BinaryMessage) {
            handleBinaryMessage(session, (BinaryMessage) message);
        }
        else if (message instanceof PongMessage) {
            handlePongMessage(session, (PongMessage) message);
        }
        else {
            throw new IllegalStateException("Unexpected WebSocket message type: " + message);
        }
    }
    //用来处理文本消息类型的方法
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
    }
    //用来处理二进制消息类型的方法
    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
    }
    //用来处理Pong消息类型的方法
    protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
    }
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
    }
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
    }
    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
}

我们可以通过继承它来完成自己想要实现的业务逻辑

public class MyWebSocketHandler extends AbstractWebSocketHandler {
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("建立链接");
    }
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.out.println("关闭链接");
    }
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.out.println("收到消息:" + message.getPayload());
		//发送消息
		session.sendMessage(new TextMessage("OK"));
    }
}

因为发送的消息是文本类型的消息,所以需要重载handleTextMessage()方法,在接收文本消息后在简单的输出消息。这样一个简单的消息处理类就完成了,需要在applicationContext.xml中添加相关的配置。

<!--声明自定义的handler-->
<bean id="helloHandler" class="com.xxx.MyWebSocketHandler"/>
<!--将helloHandler映射到 对应的地址-->
<websocket:handlers>
	<websocket:mapping handler="helloHandler" path="/hello"/>
</websocket:handlers>

页面部分:

<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
<html>
	<head>
		<title>websocket测试</title>
		<script type="text/javascript">
			var url="ws://" + window.location.host + "<%=request.getContextPath() %>/hello";
			//打开webSocket
			var sock = new WebSocket(url);
			//处理链接开启事件
			sock.onopen = function(){
				console.log("开启链接");
				sayHello();
			}
			//处理消息
			sock.onmessage = function(e){
				console.log("收到消息:",e.data);
				setTimeout(function(){
					sayHello()
				},2000);
			}
			//链接关闭事件
			sock.onclose = function(){
				console.log("关闭链接");
			}
			//发送消息
			sayHello(){
				console.log("发送消息");
				sock.send("hello world");
			}
		</script>
	</head>
	<body>
	socket测试
	</body>
</html>

在上述页面中,使用原生的JavaScript创建了WebSocket实例。通过这个实例,实际上打开了一个给定URL的WebSocket。处理URL使用了ws://前缀,表明这是一个WebSocket链接。

WebSocket创建完毕后,实现了对应的事件绑定方法onopen、onmessage和onclose时间分别对应MyWebSocketHandler类的afterConnectionEstablished()、handleTextMessage()和afterConnectionClosed()方法。在onopen时间中会调用sayHello()方法,发送hello world消息,通过这个消息建立双向通信。服务器端的MyWebSocketHandler作为相应将这个消息发送回来,当客户端收到这个消息后,onmessage又会发送一条消息,如此循环直至关闭链接。

2 WebSocket的限制

需要注意的是,WebSocket是一个比较新的规范,在web浏览器和应用服务器上没有得到全面的支持。FireFox和Chrome早已完整支持WebSocket,但是其他浏览器对WebSocket的支持还存在一定的限制。服务器端Tomcat从8开始全面支持,Jetty从7开始支持。

即便在浏览器和服务器都支持WebSocket的情况下也可能出现问题,比如防火墙通常会限制HTTP以外的流量,有可能还没有配置允许进行WebSocket通信。如果浏览器不支持可以使用SockJS,一个在浏览器上运行的JavaScript库,如果浏览器不支持WebSocket,则该库可以模拟对WebSocket的支持,实现浏览器和web服务器之间低延迟、全双工、跨域的通信通道,其API和WebSocket几乎一样,有兴趣的话大家可以自行探索。

发布了110 篇原创文章 · 获赞 475 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/yongqi_wang/article/details/87884039