注意事項:
1.如果出現握手失敗,檢查路徑之外還可以去查看攔截器是否做了什麼處理
WebSocket和sharedWork簡介
WebSocket是持久化連接,用於解決瀏覽器與後臺服務器雙向通訊的問題
sharedWorker共享工作線程允許多個頁面共享使用,每個頁面都是鏈接到該共享工作線程的某個端口號上。頁面通過該端口與共享工作線程進行通信
配置所需jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.framework.version}</version>
</dependency>
這裏所用的版本最好和項目中所用到的spring其他內容統一,避免出現版本不兼容問題。
對版本的要求是:spring版本爲 4.0 版本及以上, Tomcat 7.047 以上版本。
websocket入口
/**
* Component註解告訴SpringMVC該類是一個SpringIOC容器下管理的類
* 其實@Controller, @Service, @Repository是@Component的細化
* @Configuration註解該類,等價於XML中配置beans標籤
*/
@Configuration
@EnableWebSocket
public class MyWebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//一般瀏覽器都會支持
registry.addHandler(WebSocketPushHandler(),"/webSocketServer").addInterceptors(new MyHandShakeInterceptor());
//低版本瀏覽器使用此路徑,等下頁面上會講到
registry.addHandler(WebSocketPushHandler(), "/sockjs/webSocketServer").addInterceptors(new MyHandShakeInterceptor())
.withSockJS();
}
@Bean
public WebSocketHandler WebSocketPushHandler(){
return new MyWebSocketHander();
}
}
實現WebSocketConfigurer接口,重寫registerWebSocketHandlers方法,這是一個核心實現方法,配置websocket入口,允許訪問的域、註冊Handler、SockJs支持和攔截器。
registry.addHandler註冊和路由的功能,當客戶端發起websocket連接,把/path交給對應的handler處理, 而不實現具體的業務邏輯,可以理解爲收集和任務分發中心。
setAllowedOrigins(String[] domains),允許指定的域名或IP(含端口號)建立長連接,如果只允許自家域名訪問,這裏輕鬆設置。 如果不限時使用”*”號,如果指定了域名,則必須要以http或https開頭。
攔截器實現
/**
* websocket握手攔截器
* 攔截握手前,握手後的兩個切面
*/
public class MyHandShakeInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
System.out.println("準備進行握手");
if (request instanceof ServletServerHttpRequest) {
// attributes.put("username",userName);
}
return true;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Exception exception) {
System.out.println("握手成功,進入服務端");
}
}
1.beforeHandshake,在調用handler前處理方法。常用在註冊用戶信息,綁定WebSocketSession,在handler里根據用戶信息獲取WebSocketSession發送消息。
Handler處理類
/**WebSocket處理器
* Created by qiumeng on 2017/8/7 0007.
*/
public class MyWebSocketHander extends TextWebSocketHandler {
private static final List<WebSocketSession> users = new ArrayList<>();
//用戶進入系統監聽
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
System.out.println("成功進入了系統。。。");
users.add(session);
}
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
System.out.println("成功接收到消息");
// 把客戶端的消息解析爲JSON對象
if(message instanceof TextMessage) {
TextMessage msg = new TextMessage("發送的數據"+message.getPayload());
//給所有瀏覽器羣發消息
sendMessagesToUsers(msg);
// this.handleTextMessage(session, (TextMessage)message);
} else if(message instanceof BinaryMessage) {
BinaryMessage msg = new BinaryMessage((byte[]) message.getPayload());
//給所有瀏覽器羣發消息
sendMessagesToUsers(msg);
// this.handleBinaryMessage(session, (BinaryMessage)message);
} else {
if(!(message instanceof PongMessage)) {
throw new IllegalStateException("Unexpected WebSocket message type: " + message);
}
// this.handlePongMessage(session, (PongMessage)message);
}
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {System.out.println("安全退出了系統");}
@Override
public boolean supportsPartialMessages() {
return false;
}
/**
* 給所有的用戶發送消息
*/
public void sendMessagesToUsers(WebSocketMessage<?> message){
for(WebSocketSession user : users){
try {
//isOpen()在線就發送
if(user.isOpen()){
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//進行實例化bean可以在其他controller實例化 調用所發送的信息
@Bean
public MyWebSocketHander myWebSocketHander(){
return myWebSocketHander();
}
}
客戶端連接
這裏只寫出javascript代碼
<script type="text/javascript">
var receive_text = document.getElementById("up");
var myWorker = new SharedWorker('http://localhost:8081/portal/resources/js/worker.js');
console.log("創建sharedwork");
myWorker.onerror = function(e)
{
console.log('myWorker.onerror : ' + e.message);
}
function myOnMessage(e) {
receive_text.innerHTML += "<br/>" + e.data;
receive_text.scrollTop = receive_text.scrollHeight;
}
if(1){
// 4 綁定消息處理函數,僅設置 onmessage
myWorker.port.onmessage = myOnMessage;
}
else{
// 5 另外一種消息綁定方式, addEventListener 和 start 配合使用
myWorker.port.addEventListener('message', myOnMessage);
myWorker.port.start();
}
function send(){
var ret = myWorker.port.postMessage(JSON.stringify($("#message").val()))
}
//頁面發送消息
function sendMessage() {
// var ret = myWorker.port.postMessage(stringToBytes($("#message").val()))
var file=$("#file")[0].files[0];
var reader = new FileReader();
if(""==file){
alert("請選擇文件")
}
// reader.readAsBinaryString(file)
reader.readAsArrayBuffer(file)
reader.onload = function (e) {
if (e.target.readyState == FileReader.DONE) {
var data = new Uint8Array(e.target.result);
//這裏有一個問題,目前沒有解決 就是傳輸二進制數據大小的問題 好像爲100萬字節左右
//在大的話,目前沒有找到解決辦法
var jsondata=JSON.stringify(data.slice(0, 100))
var ret = myWorker.port.postMessage(jsondata);
}
}
}
</script>
下面是創建sharedWorker主線程代碼work.js
var count = 0;
var ports = [];
var webSocket = new WebSocket("ws://localhost:8081/portal/webSocketServer");
var websocketMessage=null;
// 唯一的事件處理函數
onconnect = function(e) {
console.log('onconnect from home');
// 1 網頁建立連接獲取端口
var port = e.ports[0];
ports.push({
port:port,
lastRecv: new Date()
});
// 2 綁定事件處理函數
port.addEventListener('message', function(e) {
var ret = e.data;
//因爲WebSockets只能通過連接發送純文本數據,所以對於複雜的數據結構,在通過連接發送之前,必須進行序列化
//發送消息到後臺
if(""!=ret){
console.log("向websocket後臺發送消息")
webSocket.send(ret);
}
});
// 3 啓動端口
port.start();
}
webSocket.onopen = function(event) {
console.log("websocket創建");
};
webSocket.onmessage = function(event) {
console.log("websocket返回消息")
websocketMessage=event.data
//data的數據格式也是字符串,如果想得到其他格式的數據,必須手工解析這些數據
for(var i=0;i<ports.length;i++){
var p=ports[i];
p.port.postMessage(websocketMessage)
}
};
webSocket.onerror = function(event) {
};