如何检测从 Websocket 服务器发送的消息

Posted

技术标签:

【中文标题】如何检测从 Websocket 服务器发送的消息【英文标题】:How to detect which message was sent from the Websocket server 【发布时间】:2020-11-09 09:31:22 【问题描述】:

我有一个小型 Web 应用程序正在侦听来自 Websocket 服务器的传入消息。我是这样收到的

const webSocket = new WebSocket("wss://echo.websocket.org");
webSocket.onopen = event => webSocket.send("test");
webSocket.onmessage = event => console.log(event.data);

但发送服务器更复杂。可能会出现多种类型的消息,例如“UserConnected”、“TaskDeleted”、“ChannelMoved”

如何检测发送的消息类型?现在我将代码修改为

const webSocket = new WebSocket("wss://echo.websocket.org");

webSocket.onopen = event => 
  const objectToSend = JSON.stringify(
    message: "test-message",
    data: "test"
  );

  webSocket.send(objectToSend);
;

webSocket.onmessage = event => 
  const objectToRead = JSON.parse(event.data);

  if (objectToRead.message === "test-message") 
    console.log(objectToRead.data);
  
;

所以我必须从服务器发送一个包含“方法名称”/“消息类型”的对象,例如“TaskDeleted”识别在客户端执行的正确方法?那会导致一个很大的 switch case 语句,不是吗?

有没有更好的方法?

【问题讨论】:

你会更容易使用一些帮助库,如 (socket.io) 它会解决你的问题yt tutorial 是的,这是个好主意。但是如果服务器没有socket.io,那需要发送哪种消息格式呢?这样socket.io仍然能够解析消息 【参考方案1】:

您可以通过直接映射方法来避免大的 switch-case 语句:

// List of white-listed methods to avoid any funny business
let allowedMethods = ["test", "taskDeleted"];

function methodHandlers()
  this.test = function(data)
  
     console.log('test was called', data);
  

  this.taskDeleted = function(data)
  
     console.log('taskDeleted was called', data);
  



webSocket.onmessage = event => 
  const objectToRead = JSON.parse(event.data);
  let methodName = objectToRead.message;
  if (allowerMethods.indexOf(methodName)>=0)
  
     let handler = new methodHandlers();
     handler[methodName](data);
  
  else
  
     console.error("Method not allowed: ", methodName)
  
 ;

【讨论】:

非常感谢您的代码。我更喜欢类似于 socket.io 的解决方案。 socket.on(eventName, callback) 但问题是我不知道服务器需要发送的格式以从中解析 eventName【参考方案2】:

根据 Eriks Klotins 的回答,我想提供一个我目前正在使用的解决方案。但是有必要定义一个消息格式。我想过这种格式


    "event": "event name goes here",
    "data": 
        "arg1": "first argument"
    

基于此我将代码修改为

const webSocket = new WebSocket("wss://echo.websocket.org");

webSocket.onopen = event => 
  const objectToSend = JSON.stringify(
    event: "test",
    data: 
      argOne: "argument 1"
    
  );

  webSocket.send(objectToSend);
;

webSocket.onmessage = event => 
  const objectToRead = JSON.parse(event.data);
  const eventMethods = events();
  const eventMethod = eventMethods[objectToRead.event];

  eventMethod(objectToRead.data);
;

function events()
  return 
    test: function( argOne ) 
      console.log(argOne);
      
  ;

【讨论】:

【参考方案3】:

正如您在其中一个 cmets 中要求的那样,为像 socket.io 这样的 websockets 提供一个流畅的接口。

您可以使用简单的 PubSub(发布订阅)设计模式使其流畅,这样您就可以订阅特定的消息类型。 Node 提供了 EventEmitter 类,因此您可以继承 onemit 事件,但是,在此示例中是使用类似 API 的快速模型。

在生产环境中,我建议在 node.js 环境中使用本机 EventEmitter,并在前端使用与浏览器兼容的 npm 包。

查看每件作品的描述。

订阅者保存在一个带有一组回调的简单对象中,如果需要,您可以添加取消订阅。

注意:如果你使用的是 node.js,你可以扩展 EventEmitter

// This uses a similar API to node's EventEmitter, you could get it from a node or a number of browser compatible npm packages.
class EventEmitter 
  //  [event: string]: Set<(data: any) => void> 
  __subscribers = 
  
  // subscribe to specific message types
  on(type, cb) 
    if (!this.__subscribers[type]) 
      this.__subscribers[type] = new Set
    
    this.__subscribers[type].add(cb)
  
  
  // emit a subscribed callback
  emit(type, data) 
    if (typeof this.__subscribers[type] !== 'undefined') 
      const callbacks = [...this.__subscribers[type]]
      callbacks.forEach(cb => cb(data))
    
  


class SocketYO extends EventEmitter 
  
  constructor( host ) 
    super()
    // initialize the socket
    this.webSocket = new WebSocket(host);
    this.webSocket.onopen = () => 
      this.connected = true
      this.emit('connect', this)
    
    this.webSocket.onerror = console.error.bind(console, 'SockyError')
    this.webSocket.onmessage = this.__onmessage
  
  
  // send a json message to the socket
  send(type, data) 
    this.webSocket.send(JSON.stringify(
      type,
      data
    ))
  
  
  on(type, cb) 
    // if the socket is already connected immediately call the callback
    if (type === 'connect' && this.connected) 
      return cb(this)
    
    // proxy EventEmitters `on` method
    return super.on(type, cb)
  
  
  // catch any message from the socket and call the appropriate callback
  __onmessage = e => 
    const  type, data  = JSON.parse(e.data)
    this.emit(type, data)
  


// create your SocketYO instance
const socket = new SocketYO(
  host: 'wss://echo.websocket.org'
)

socket.on('connect', (socket) => 
  // you can only send messages once the socket has been connected
  socket.send('myEvent', 
    message: 'hello'
  )
)

// you can subscribe without the socket being connected
socket.on('myEvent', (data) => 
  console.log('myEvent', data)
)

【讨论】:

以上是关于如何检测从 Websocket 服务器发送的消息的主要内容,如果未能解决你的问题,请参考以下文章

如何从 websocket 端点外部发出 websocket 消息?

websocket实现原理

websocket实现原理

如何在特定时间通过 websocket 发送消息

在 Spring Websocket 上向特定用户发送消息

websocket检测服务器是否断开-来游戏手游解答