1、什么是WebSocket
- 定义:WebSocket是一种区别于HTTP协议的通信协议,可在单个TCP连接上进行全双工通信。
- 主要功能:提供客户端和服务端的双向数据推送
- 应用场景:实时弹幕、股票、在线聊天等
- 特点:
- 握手阶段采用 HTTP 协议。
- 数据格式轻量,性能开销小。客户端与服务端进行数据交换时,服务端到客户端的数据包头只有2到10字节,客户端到服务端需要加上另外4字节的掩码。而HTTP请求每次都需要携带完整头部。
- 更好的二进制支持,可以发送文本,和二进制数据
- 没有同源限制,客户端可以与任意服务器通信
- 协议标识符是ws(如果加密,则是wss),请求的地址就是后端支持websocket的API。
注:webSocket是一种底层协议,日常开发中通常会搭配socket.js 和 stomp.js 等库来实现附加的功能,比如数据加密、身份认证等
以下代码都默认安装了以上两个js库
2、创建
2.1 默认使用浏览器原生 Websocket
import Stomp from 'stomp'
//...
const url = "ws://localhost:61614/stomp"
const client = Stomp.client(url)
//...
2.2 使用浏览器原生 Websocket
import Stomp from 'stomp'
import SocktJs from 'socketjs'
//...
const ws = new SocktJs(url);
const client = Stomp.over(ws);
//...
3、连接
3.1不提供headers
client.connect(login, passcode, connectCallback);
client.connect(login, passcode, connectCallback, errorCallback);
client.connect(login, passcode, connectCallback, errorCallback, host);
3.2提供headers
client.connect(headers, connectCallback);
client.connect(headers, connectCallback, errorCallback);
4、断开连接
client.disconnect(disconnectCallback)
5、断开连接
client.heartbeat.outgoing = 20000; // 客户端每20000ms发送一次心跳
client.heartbeat.incoming = 0; // 客户端将不接受从服务器发送的心跳
6、数据发送
stomp的消息体必须是一个string,传递对象时使用JSON.stringfy()
来解决
client.send(url, headers, body);
//当不需要设置headers时,第二个参数设置为{}
client.send(url, {}, body);
7、数据订阅
const subscription = client.subscribe(url, callback,[headers]);
subscribe()方法返回一个对象,包含一个id属性和一个unsubscribe
方法,使用次方法可以取消当前订阅subscription.unsubscribe()
。
当headers中没有提供id属性的时候,stomp库会默认返回一个unique ID
//使用自定义id
const mysubid = 'myCustomId';
const subscription = client.subscribe(destination, callback, { id: mysubid });
数据过滤
const headers = {ack: 'client', 'selector': "location = 'Europe'"};
client.subscribe("/queue/test", message_callback, headers);
客户端将只会在接受的数据匹配 location = 'Europe'
时才会触发 message_callback
8、消息确认机制
默认情况下,STOMP消息将在被传递到客户端之前由服务器自动确认。客户端可以通过在订阅消息时设置 ack headers
来自行处理消息确认逻辑。在这种情况下,客户端必须使用message.ack()
方法来通知服务器它已确认该消息。
const subscription = client.subscribe("/queue/test",
function(message) {
// do something with the message
...
// and acknowledge it
message.ack();
},
{ack: 'client'}
);
ack()
方法接受一个具备transaction字段的参数:heades
。
const tx = client.begin();
message.ack({ transaction: tx.id, receipt: 'my-receipt' });
tx.commit();
对应的 nack()
方法通知服务器消息没被确认;
客户端使用其begin()
方法启动事务,该方法采用可选事务,即标识事务的唯一字符串。如果未设置事务,则库将自动生成一个事务。
此方法返回一个JavaScript对象,该对象具有一个与事务ID对应的id属性和两个方法:
commit()
//提交事务abort()
// 中断事务
// start the transaction
const= 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()时忘记添加事务头,则该消息将不属于事务,并且将直接发送,而无需等待事务完成。
const = "unique_transaction_identifier";
// start the transaction
const = 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