See more: Spring WebSocket reference
整个例子属于WiseMenuFrameWork的一部分,可以将整个项目Clone下来,如果朋友们有需求,我可以整理一个独立的demo出来。
WebSocket前端准备
前端我们需要用到两个js文件:
sockjs.js和stomp.js
- SockJS:
SockJS 是一个浏览器上运行的 JavaScript 库,如果浏览器不支持 WebSocket,该库可以模拟对 WebSocket 的支持,实现浏览器和 Web 服务器之间低延迟、全双工、跨域的通讯通道。
-
Stomp
Stomp 提供了客户端和代理之间进行广泛消息传输的框架。Stomp 是一个非常简单而且易用的通讯协议实现,尽管代理端的编写可能非常复杂,但是编写一个 Stomp 客户端却是很简单的事情,另外你可以使用 Telnet 来与你的 Stomp 代理进行交互。
发送公告功能
html代码
<div>
<div>
<button id="connect" onclick="connect();">Connect</button>
<button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button>
</div>
<div id="conversationDiv">
<p>
<label>notice content?</label>
</p>
<p>
<textarea id="name" rows="5"></textarea>
</p>
<button id="sendName" onclick="sendName();">Send</button>
<p id="response"></p>
</div>
</div>
javascript代码
<script src="/js/sockjs-0.3.4.min.js"></script>
<script src="/js/stomp.min.js"></script>
<script>
var stompClient = null;
function setConnected(connected) {
document.getElementById('connect').disabled = connected;
document.getElementById('disconnect').disabled = !connected;
document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
document.getElementById('response').innerHTML = '';
}
// 开启socket连接
function connect() {
var socket = new SockJS('/socket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
});
}
// 断开socket连接
function disconnect() {
if (stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
// 向‘/app/change-notice’服务端发送消息
function sendName() {
var value = document.getElementById('name').value;
stompClient.send("/app/change-notice", {}, value);
}
connect();
</script>
相关说明:
关于JavaScript实现WebSocket的功能很简单,基本分以下几步:
开启Socket
-
var socket = new SockJS('/socket');
先构建一个SockJS对象 -
stompClient = Stomp.over(socket);
用Stomp将SockJS进行协议封装 -
stompClient.connect()
与服务端进行连接,同时有一个回调函数,处理连接成功后的操作信息。
发送消息
stompClient.send("/app/change-notice", {}, value);
页面预览:
Paste_Image.png修改公告功能
当我们发送公告后,将上图的公告信息在不刷新页面的情况下,使用WebSocket将其改变。发送公告的前端代码已经完成,现在我们来写另一个客户端,用来接收第一个页面发送的公告,展示在上图红框中。
功能比较简单,所以下面只给出部分js代码:
var noticeSocket = function () {
var s = new SockJS('/socket');
var stompClient = Stomp.over(s);
stompClient.connect({}, function () {
console.log('notice socket connected!');
stompClient.subscribe('/topic/notice', function (data) {
$('.message span.content').html(data.body);
});
});
};
相关说明:
这里与发送公告代码不同的是,在stompClient
建立连接成功之后,我们要监听服务端发送过来的信息,接收到之后,改变页面上公告的内容,所以用到了stompClient.subscribe()
这个subscribe()
方法功能就是定义一个订阅地址,用来接收服务端的信息(在服务端代码中,我们在@SendTo
中指定了这个订阅地址“/topic/notice”),当收到消息后,在回调函数中处理业务逻辑。
至此,所有的功能开发完毕!
效果演示
首先我们发布一条公告:
然后我们切到另一个页面,发现内容已变:
网友评论
推荐下,分库分表中间件 Sharding-JDBC 源码解析 17 篇:http://t.cn/R0UfGFT
夏
可以讲一下它的原理吗?求解
这个问题楼主有解决吗?
github上他们说貌似是sockjs本身有问题。要禁止使用默认cdn依赖库就能解决问题?不知道楼主怎么解决的。
function connect() {
// ws://localhost:8080/websocket/start/add
var url = 'ws://localhost:8080/websocket/start/add';
var socket = new SockJS(url);
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
// 订阅Socket消息
stompClient.subscribe('/topic/showResult', function(calResult){
showResult(JSON.parse(calResult.body).result);
});
});
}这个时
又出现错误为:
XMLHttpRequest cannot load ws://localhost:8080/websocket/start/add/info. Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.
这是为什么呢?
function connect() {
// ws://localhost:8080/websocket/start/add
var url = '/add';
var socket = new SockJS(url);
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
// 订阅Socket消息
stompClient.subscribe('/topic/showResult', function(calResult){
showResult(JSON.parse(calResult.body).result);
});
});
}
但是后来得到的响应是GET http://localhost:8080/add/info 404 (Not Found),请教下这个是为什么呢?需要配置服务端的请求地址么?服务端的地址不是ws://协议吗?非常感谢回复!
我加了个/*的路径映射就没这个问题了。
org.springframework.messaging.converter.MessageConversionException: Could not read JSON: Invalid UTF-8 start byte 0xae
at [Source: [B@684aaa68; line: 1, column: 3]; nested exception is com.fasterxml.jackson.core.JsonParseException: Invalid UTF-8 start byte 0xae
at [Source: [B@684aaa68; line: 1, column: 3]
function connect() {
// ws://localhost:8080/websocket/start/add
var url = 'ws://localhost:8080/websocket/start/add';
var socket = new SockJS(url);
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
// 订阅Socket消息
stompClient.subscribe('/topic/showResult', function(calResult){
showResult(JSON.parse(calResult.body).result);
});
});
}这个时
又出现错误为:
XMLHttpRequest cannot load ws://localhost:8080/websocket/start/add/info. Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.
这是为什么呢?
function connect() {
// ws://localhost:8080/websocket/start/add
var url = '/add';
var socket = new SockJS(url);
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
// 订阅Socket消息
stompClient.subscribe('/topic/showResult', function(calResult){
showResult(JSON.parse(calResult.body).result);
});
});
}
但是后来得到的响应是GET http://localhost:8080/add/info 404 (Not Found),请教下这个是为什么呢?需要配置服务端的请求地址么?服务端的地址不是ws://协议吗?非常感谢回复!
指定了一个页面,用来实现WebSocket客户端发送公告功能,使用的是@RequestMapping,所以接收的是http请求,进行页面跳转。
我要是不指定页面该怎么去更改。。。测试下功能就行。我按照你的方法写好后加载不出html页面,希望您能帮我解决下
Exception in thread "main" java.lang.IllegalStateException: Tomcat connector in failed state
at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.start(TomcatEmbeddedServletContainer.java:157)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.startEmbeddedServletContainer(EmbeddedWebApplicationContext.java:288)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh(EmbeddedWebApplicationContext.java:141)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:483)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:686)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:957)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:946)
at org.springframework.boot.SpringApplication$run.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
at com.freedom.orion.OrionApplication.main(OrionApplication.groovy:21)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)