WebSocket 简介
WebSocket 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在 TCP 之上,同 HTTP 一样通过 TCP 来传输数据,但是它和 HTTP 最大不同是:
- WebSocket 是一种双向通信协议,在建立连接后,WebSocket 服务器和 Browser/Client Agent 都能主动的向对方发送或接收数据,就像 Socket 一样;
- WebSocket 需要类似 TCP 的客户端和服务器端通过握手连接,连接成功后才能相互通信。
整合示例
本示例为演示WebSocket的广播式模式,即服务端有消息时,会将消息发送给所有连接了当前endpoint的 Browser/Client Agent
1. maven引入依赖包
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.8.RELEASE</spring.version>
</properties>
<dependencies>
<!-- springmvc jar -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- WEBSOCKET的JAR -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
2. 编写WebSocket的配置类
Annotation注解配置方式(boot):WebSocketConfig.java
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
@Configuration
//开启对WebSocket的支持
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{
/**
* 注册一个STOMP协议的节点,并映射到指定的URL
* @param registry
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//注册一个STOMP的endpoint,并指定使用SockJS协议
registry.addEndpoint("/endpointSocket").withSockJS();
}
/**
* 配置消息代理
* @param registry
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//配置一个广播式的消息代理
registry.enableSimpleBroker("/topic");
}
}
XML配置方式:spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
<mvc:annotation-driven/>
<mvc:resources mapping="/js/**" location="/js/"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- /app:客户端向服务端 发送的前缀 -->
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/ws">
<websocket:sockjs/>
</websocket:stomp-endpoint>
<!-- 服务端向客户端 发送的前缀 -->
<websocket:simple-broker prefix="/topic"/>
</websocket:message-broker>
</beans>
3. 请求控制器
import java.util.Map;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* 由客户端触发,并接受服务器发送信息的例子
*/
@Controller
public class MessageController {
@RequestMapping(value = "/websocket", method = { RequestMethod.GET })
public String toHello() {
System.out.println("messageController ----> tohello()");
return "websocket"; //定位到页面
}
@MessageMapping("/greeting")
@SendTo("/topic/greetings")
public String greeting(Map<String, Object> message) throws Exception {
System.out.println("MessageController====================================>客户端连接");
return "服务器返回: Hello,客户端输入信息< " + message.get("name") + ">";
}
}
4. 页面webSocket.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ page session="false"%>
<%
String path = request.getContextPath(); // path = "/travel"
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Spring + WebSocket Hello world例子</title>
<script src="<%=basePath%>js/sockjs-1.1.1.min.js"></script>
<script src="<%=basePath%>js/stomp-2.3.4.min.js"></script>
<script src="<%=basePath%>/js/jquery-1.10.2.js"></script>
<script src="<%=basePath%>/js/jquery-ui-1.10.4.custom.js"></script>
<script src="<%=basePath%>/js/jquery.json.js"></script>
<script>
//创建sockJS协议
var socket = new SockJS("<c:url value='/ws'/>");
var stompClient = Stomp.over(socket);
//连接服务器
//stompClient.connect("guest", "guest", function() {}); 用户名和密码
stompClient.connect({}, function(frame) {
//setConnected(true);
$("#recFromServer").append("<br>" + "成功连接服务器.!");
console.log('Connected: ' + frame);
window.alert('Connected: ' + frame);
//subscribe:订阅一个主题,“/topic”前缀是在:
stompClient.subscribe('/topic/greetings', function(greeting) {
console.log(greeting.body);
console.log("自己的操作!");
var content = JSON.parse(greeting.body).content;
$("#recFromServer").append("<br>" + content);
});
});
function sendMessage() {
//发送信息给服务器
stompClient.send("/app/greeting", {}, JSON.stringify({
'name' : $("#message").val()
}));
}
</script>
</head>
<body>
输入名称:${basePath }
<input id="message" type="text">
<input type="button" onclick="sendMessage()" value="发送到服务器">
<div id="recFromServer"></div>
测试方式: 用两个浏览器打开这个页面,然后一个页面提交信息,它能接收到服务器的数据,同时另一个页面也能接收到服务器发送的数据。
</body>
</html>
菜鸟代码示例
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>My WebSocket</title>
</head>
<body>
<input id="text" type="text" />
<button onclick="send()">Send</button>
<button onclick="closeWebSocket()">Close</button>
<div id="message">
</div>
</body>
<script type="text/javascript">
var websocket = null;
//判断当前浏览器是否支持WebSocket
if('WebSocket' in window){
//如果支持WebSocket,发起WebSocket连接
websocket = new WebSocket("ws://localhost:8088/websocket");
} else{
alert('Not support websocket')
}
//连接发生错误的回调方法
websocket.onerror = function(){
setMessageInnerHTML("error");
};
//连接成功建立的回调方法
websocket.onopen = function(event){
setMessageInnerHTML("open");
}
//接收到消息的回调方法
websocket.onmessage = function(event){
setMessageInnerHTML(event.data);
}
//连接关闭的回调方法
websocket.onclose = function(){
setMessageInnerHTML("close");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function(){
//关闭连接
websocket.close();
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML){
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//关闭连接
function closeWebSocket(){
websocket.close();
}
//发送消息
function send(){
var message = document.getElementById('text').value;
//发送消息
websocket.send(message);
}
</script>
</html>
启动并测试
也可打开多个浏览器窗口连接到WebSocket服务端,在其中一个浏览器窗口中点击获取服务器时间,其他两个也将接收到消息