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