WebSocke是 HTML5 提供的一種在單個 TCP 鏈接上進行全雙工通信的協議。javascript
WebSocket協議是基於TCP的一種新的網絡協議,是一個應用層協議,是TCP/IP協議的子集。html
它實現了瀏覽器與服務器全雙工(full-duplex)通訊,客戶端和服務器均可以向對方主動發送和接收數據。在 WebSocket API 中,瀏覽器和服務器只須要完成一次握手,二者之間就直接能夠建立持久性的鏈接,並進行雙向數據傳輸。java
在JS中建立WebSocket後,會有一個HTTP請求從瀏覽器發向服務器。在取得服務器響應後,創建的鏈接會使用HTTP升級將HTTP協議轉換爲WebSocket協議。也就是說,使用標準的HTTP協議沒法實現WebSocket,只有支持那些協議的專門瀏覽器才能正常工做。因爲WebScoket使用了自定義協議,因此URL與HTTP協議略有不一樣。未加密的鏈接爲ws://,而不是http://。加密的鏈接爲wss://,而不是https://。jquery
實時Web應用的解決方案,實現Web的實時通訊。web
說的再直白點, html的消息推送 。segmentfault
假如你有一個頁面,數據不按期更改,一般的作法就是 Ajax輪詢,輪詢是指在特定的時間間隔(如每一秒),由瀏覽器對服務器發起HTTP請求,而後由服務器返回最新的數據給客戶端的瀏覽器。跨域
因爲HTTP協議是惰性的,只有客戶端發起請求,服務器纔會返回數據。這種傳統的模式帶來很明顯的缺點,即瀏覽器須要不斷的向服務器發出請求,然而HTTP請求可能包含較長的頭部,其中真正有效的數據可能只是很小的一部分,顯然這樣會浪費不少的帶寬等資源。。而WebSocket屬於服務端推送技術,能更好的節省服務器資源和帶寬,而且可以更實時地進行通信。數組
當有了web socket,數據變更時 讓服務器通知客戶端,啓不是很美妙? 瀏覽器
(1) 默認端口是80和443(ssl)。服務器
(2) 協議標識符是ws和wss(ssl)。
(3) 請求報文示例
General -------------------------------------------- Request URL:ws://localhost:8080/j2ee-websocket/websocket/1 Request Method:GET Status Code:101 Switching Protocols --------------------------------------------- Response Headers --------------------------------------------- Connection:upgrade Date:Tue, 05 Dec 2017 01:22:45 GMT Sec-WebSocket-Accept:cRxT/XcOpnsleDb1KdydWXOw+us= Sec-WebSocket-Extensions:permessage-deflate;client_max_window_bits=15 Server:Apache-Coyote/1.1 Upgrade:websocket
瀏覽器經過 JavaScript 向服務器發出創建 WebSocket 鏈接的請求,鏈接創建之後,客戶端和服務器端就能夠經過 TCP 鏈接直接交換數據。
當你獲取 Web Socket 鏈接後,你能夠經過 send() 方法來向服務器發送數據,並經過 onmessage 事件來接收服務器返回的數據。
客戶端JavaScript代碼:
<script type="text/javascript"> var websocket = null; var principal = '1'; var socketURL = 'ws://' + window.location.host + '/j2ee-websocket/websocket/' + principal; //判斷當前瀏覽器是否支持WebSocket,若支持建立一個websocket對象。 if('WebSocket' in window){ websocket = new WebSocket(socketURL); } else{ alert('Not support websocket'); } //鏈接發生錯誤的回調方法 websocket.onerror = function(event){ alert("error"); }; //鏈接成功創建的回調方法 websocket.onopen = function(){ alert("open"); } //接收到消息的回調方法 websocket.onmessage = function(event){ alert('recive : ' + event.data); } //鏈接關閉的回調方法 websocket.onclose = function(event){ alert("close"); } //發送消息 function send(message){ websocket.send(message); } </script>
WebSocket 對象的屬性:
var Socket = new WebSocket(socketURL)
Socket.readyState
只讀屬性readyState表示鏈接狀態,能夠是如下值:
0 - 表示鏈接還沒有創建。
1 - 表示鏈接已創建,能夠進行通訊。
2 - 表示鏈接正在進行關閉。
3 - 表示鏈接已經關閉或者鏈接不能打開。
websocket事件:
事件 事件處理程序 描述 open Socket.onopen 鏈接創建時觸發 message Socket.onmessage 客戶端接收服務端數據時觸發 error Socket.onerror 通訊發生錯誤時觸發 close Socket.onclose 鏈接關閉時觸發
websocket對象的方法
方法 描述 Socket.send() 使用鏈接發送數據 Socket.close() 關閉鏈接
推薦:google/jquery-websocket代碼 (http://code.google.com/p/jquery-websocket)
google/jquery-websocket增長了消息的類型,將消息拆分爲{"type":"","message":""}。
這樣更靈活,能夠根據業務類型,定義type,如:通知,公告,廣播,發文等...
<script type="text/javascript"> var principal = '1'; var socketURL = 'ws://' + window.location.host + '/j2ee-websocket/websocket/' + principal; websocket = $.websocket(socketURL, { open : function() { // when the socket opens alert("open"); }, close : function() { // when the socket closes alert("close"); }, //收到服務端推送的消息處理 events : { 'radio' : function(event) { console.info($.parseJSON(event.data)); }, 'notice' : function(event) { console.info($.parseJSON(event.data)); }, //... more custom type of message } }); //發送消息 function send() { websocket.send('radio', " hello,this msg from client request"); } </script>
AJAX輪詢和Websockets:
SockJS是一個瀏覽器JavaScript庫,它提供了一個連貫的、跨瀏覽器的Javascript API,在瀏覽器和web服務器之間創建一個低延遲、全雙工、跨域通訊通道。SockJs的一大好處是提供了瀏覽器兼容性,優先使用原生的WebSocket,在不支持websocket的瀏覽器中,會自動降爲輪詢的方式。
<script src="//cdn.jsdelivr.net/sockjs/1.0.0/sockjs.min.js"></script> var sock = new SockJS('/coordination'); sock.onopen = function() { console.log('open'); }; sock.onmessage = function(e) { console.log('message', e.data); }; sock.onclose = function() { console.log('close'); }; sock.send('test'); sock.close();
如下內容轉載自(https://segmentfault.com/a/1190000006617344?utm_source=tag-newest)
STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,簡單(流)文本定向消息協議,它提供了一個可互操做的鏈接格式,容許STOMP客戶端與任意STOMP消息代理(Broker)進行交互。
stomp幀(Frame)格式以下:
屬性。 類型 描述 command String name of the frame("CONNECT",'SEND',etc.) headers JavaScript object body String
command和headers屬性始終會被定義,當這個frame沒有頭部時,headers能夠爲空。若這個frame沒有body, body的值能夠爲null。
建立stomp客戶端:
一、在web瀏覽器中使用普通的Web Socket
stomp javascript客戶端會使用ws://的URL與stomp服務端進行交互。爲了建立一個stomp客戶端js對象,須要使用Stomp.client(url), 而這個URL鏈接着服務端的WebSocket的代理:
var url = "ws://localhost:61614/stomp"; var client = Stomp.client(url);
Stomp.client(url, protocols)也能夠用來覆蓋默認的subprotocols, 第二個參數能夠是一個字符串或一個字符串數組指定的多個subprotocols。
二、在web瀏覽器中使用定製的WebSocket
瀏覽器提供了不一樣的websocket協議,一些老的瀏覽器不支持WebSocket的腳本或者使用別的名字。默認下,stomp.js會使用瀏覽器原生的WebSocket class去建立WebSocket。利用Stomp.over(ws)這個方法可使用其餘類型的WebSockets.
<script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script> <script> // use SockJS implementation instead of the browser's native implementation var ws = new SockJS(url); var client = Stomp.over(ws); [...] </script>
鏈接服務器:
一旦Stomp客戶端創建了,必須調用它的connect()方法去鏈接,從而Stomp服務端進行驗證。這個方法須要兩個參數,用戶的登陸和密碼憑證。這種狀況下,客戶端會使用Websocket打開鏈接,併發送一個CONNECT frame。這個鏈接是異步進行的,你不能保證當這個方法返回時是有效鏈接的,爲了知道鏈接的結果,須要一個回調函數。
var connect_callback = function(){ // called back after the client is connected and authenticated to the STOMP server }
若是鏈接失敗connect()方法接受一個可選的參數(error_callback),當客戶端不能鏈接上服務端時,這個回調函數error_callback會被調用,該函數的參數爲對應的錯誤對象。
var error_callback = function(error) { // display the error's message header: alert(error.headers.message); };
在大多數狀況下,connect()
方法可接受不一樣數量的參數來提供簡單的API:
client.connect(login, passcode, connectCallback);
client.connect(login, passcode, connectCallback, errorCallback);
client.connect(login, passcode, connectCallback, errorCallback, host);
若是你須要附加一個headers頭部,connect方法還能夠接受其餘兩種形式的參數:
client.connect(headers, connectCallback);
client.connect(headers, connectCallback, errorCallback);
header是map形式,須要自行在headers添加login, passcode(甚至host):
var headers = { login: 'my login', passcode: 'my passcode', // additional header 'client-id': 'my-client-id' } client.connect(headers, connectCallback);
斷開鏈接時,調用disconnect方法,這個方法也是異步的,當斷開成功後會接受一個額外的回調函數的參數:
client.disconnect(function(){ alert('See you next time!'); });
當客戶端與服務器斷開鏈接,就不會再發送或接收消息了。
客戶端和服務器鏈接過程當中會每隔固定時間發送heart-beats, heat-beating也就是頻率,incoming時接收頻率,outgoing是發送頻率。經過改變incoming和outgoing能夠更改客戶端的heart-beating(默認爲10000ms
client.heartbeat.outgoing = 20000; // client will send heartbeats every 20000ms client.heartbeat.incoming = 0; // client does not want to receive heartbeats from the server
heart-beating 是利用window.setInterval()去規律地發送heat-beats或者檢查服務端的heart-beats.
發送消息
當客戶端與服務端鏈接成功後,能夠調用send()來發送STOMP消息。這個方法必須有一個參數,用來描述對應的STOMP的目的地。另兩個可選的參數:headers(object類型包含額外的信息頭部)和body(一個String類型的參數)
client.send("queue/test", {priority:9}, "Hello, STOMP");
若是你想發送一個有body的信息,也必須傳遞headers參數。若是沒有headers須要傳遞,那麼就傳遞{}便可,以下所示:
client.send(destination, {}, body);
訂閱(Subscribe)和接收(receive)消息
爲了在瀏覽器中接收消息,STOMP客戶端必須先訂閱一個目的地destination。你可使用subscribe()
去訂閱。這個方法有2個必需的參數:目的地(destination
),回調函數(callback
);還有一個可選的參數headers
。其中destination
是String類型,對應目的地,回調函數是伴隨着一個參數的function
類型。
var subscription = client.subscribe("/queue/test/", callback);
subscribe()
方法返回一個object
,這個object
包含一個id
屬性,對應這個這個客戶端的訂閱ID。而unsubscribe()
能夠用來取消客戶端對這個目的地destination
的訂閱。
默認狀況下,若是沒有在headers
額外添加,這個庫會默認構建一個獨一無二的ID
。在傳遞headers
這個參數時,可使用你本身的ID
:
var mysubid = '...'; var subscription = client.subscribe(destination, callback, { id: mysubid });
這個客戶端會向服務端發送一個STOMP訂閱幀(SUBSCRIBE frame
)並註冊回調事件。每次服務端向客戶端發送消息時,客戶端都會輪流調用回調函數,參數爲對應消息的STOMP幀對象(Frame object
)。以下所示:
callback = function(message) { // called when the client receives a STOMP message from the server if (message.body) { alert("got message with body " + message.body) } else { alert("got empty message"); } });
subscribe()
方法,接受一個可選的headers
參數用來標識附加的頭部。
var headers = {ack: 'client', 'selector': "location = 'Europe'"}; client.subscribe("/queue/test", message_callback, headers);
這個客戶端指定了它會確認接收的信息,只接收符合這個selector : location = 'Europe'
的消息。
若是想讓客戶端訂閱多個目的地,你能夠在接收全部信息的時候調用相同的回調函數:
onmessage = function(message) { // called every time the client receives a message } var sub1 = client.subscribe("queue/test", onmessage); var sub2 = client.subscribe("queue/another", onmessage)
若是要停止接收消息,客戶端能夠在subscribe()
返回的object
對象調用unsubscribe()
來結束接收。
var subscription = client.subscribe(...); ... subscription.unsubscribe();
支持JSON
STOMP消息的body
必須爲字符串。若是你須要發送/接收JSON
對象,你可使用JSON.stringify()
和JSON.parse()
去轉換JSON對象。
var quote = {symbol: 'APPL', value: 195.46}; client.send("/topic/stocks", {}, JSON.stringify(quote)); client.subcribe("/topic/stocks", function(message) { var quote = JSON.parse(message.body); alert(quote.symbol + " is at " + quote.value); };
默認狀況,在消息發送給客戶端以前,服務端會自動確認(acknowledged
)。
客戶端能夠選擇經過訂閱一個目的地時設置一個ack header
爲client
或client-individual
來處理消息確認。
在下面這個例子,客戶端必須調用message.ack()
來通知服務端它已經接收了消息。
var subscription = client.subscribe("/queue/test", function(message) { // do something with the message ... // and acknowledge it message.ack(); }, {ack: 'client'} );
ack()
接受headers
參數用來附加確認消息。例如,將消息做爲事務(transaction)的一部分,當要求接收消息時其實代理(broker)已經將ACK STOMP frame
處理了。
var tx = client.begin(); message.ack({ transaction: tx.id, receipt: 'my-receipt' }); tx.commit();
nack()
也能夠用來通知STOMP 1.1.brokers(代理):客戶端不能消費這個消息。與ack()
方法的參數相同。
能夠在將消息的發送和確認接收放在一個事務中。
客戶端調用自身的begin()
方法就能夠開始啓動事務了,begin()
有一個可選的參數transaction
,一個惟一的可標識事務的字符串。若是沒有傳遞這個參數,那麼庫會自動構建一個。
這個方法會返回一個object。這個對象有一個id
屬性對應這個事務的ID,還有兩個方法:commit()
提交事務abort()
停止事務
在一個事務中,客戶端能夠在發送/接受消息時指定transaction id來設置transaction。
// start the transaction var tx = client.begin(); // send the message in a transaction client.send("/queue/test", {transaction: tx.id}, "message in a transaction"); // commit the transaction to effectively send the message tx.commit();
若是你在調用send()
方法發送消息的時候忘記添加transction header,那麼這不會稱爲事務的一部分,這個消息會直接發送,不會等到事務完成後才發送。
var txid = "unique_transaction_identifier"; // start the transaction var tx = client.begin(); // oops! send the message outside the transaction client.send("/queue/test", {}, "I thought I was in a transaction!"); tx.abort(); // Too late! the message has been sent