Websocket server listen to another websocket server


#1

Hello friends,

I have a question and I wanted to know if you could help me, it happens that I have been looking for information on how to solve the problem within Adonis and with the tools that he gives me, but I have not.

I need to make several websocket servers that are listening to other addresses and listen to what they deliver to send my data already processed to my clients.

The first two websockets should be found in adonis listening to those respective websockets, at a glance it would be as if the websocket servers were websocket client

I hope someone can help me
regards


#2

Just to be sure, the 2 websockets are pure websocket implementation right ? And the websocket client is the adonis websocket server package ?

if so you should use websocket/ws to be client on both websocket listening.

  1. create a class client handling the opening of the connection, ping pong logic (in the ws spec !), reconnect on close. etc.

  2. define a service provider to register both clients and start them when thte server is l on server start.

  3. Bind an internal adonis ‚ÄėEvent‚Äô to the onMessage of the client. To emit the received message elsewhere in adonis.

  4. Listen to this event signature in a ws controller and broadcast to you client.

The only tricky part is to properly maintain the connection open and reconnect if needed. the rest is quite simple.

EDIT:
Event if you where to connect to another websocket implementation (client) it just matter to have the right client obviously.


#3

Where does the Websocket 1 and Websocket 2 gets messages from?


#4

For example i need that websocket 1 listen to wss://stream.binance.com:9443/ws/trxusdt@ticker and websocket 2 wss://api.bitfinex.com/ws/2, when they recibe the message, data is process and my websocket server generate a broadcast to all my clients


#5

Hi Goopil1,

thank you for answering, I understand what you tell me but my problem is in how to perform a websocket client that listens to the connection of wss: //stream.binance.com: 9443 / ws / trxusdt @ ticker and another that at the same time listen to wss: //api.bitfinex.com/ws/2 but both being servers because I need that when receiving the data it is processed and sent to my clients connected to my websocket.

It does not help me to listen from the clientside, I need that in node I do it.

I do not know if I can explain myself


#6

Hi @DiruzCode,

I understood what you are trying to do. You should know that node (and in our case adonis server) can be a client on another websocket server. In fact the websocket/ws package expose a client and a server.

const WebSocket = require('ws');

const ws = new WebSocket('ws://www.host.com/path');

ws.on('open', function open() {
  ws.send('something');
});

ws.on('message', function incoming(data) {
  console.log(data);
});

this code illustrate how the ws client connect to a ws server and this is perfectly valid on node side.

you could do somethings like this

const WebSocket = require('ws');
const Event = use('Event')

class WsConnection {
    constructor ({url, evName}) {
        this._url = url
        this._ev = evName
        this._client = new WebSocket(url)
        this._client._onOpen.bind(this)
        this._client._onMessage.bind(this)
    }

    _onOpen() {
        console.log('con opened on', this._url)
    }

    _onMessage (data) {
        console.log('data received on', this._url, data)
        Event.emit(this._ev, data)
    }
}

module.exports = WsConnection

#7

thank @Goopil1 for your help.

I managed to lift the websocket and make the connection from another app, I had the typical ‚Äúregeneratorruntime is not defined‚ÄĚ error but the forum helped me.

Currently my client is subscribed to my channel, which is called huobi, but even if he makes the connection to the controller, he does not send me the answer of his connection to the other web socket.

I have not managed to find a solution since there are not many examples of the use of adonis websocket controllers.

Socket.js

const Ws = use('Ws')

Ws.channel('huobi', 'HuobiController')


Ws.channel('huobi', ({ socket }) => {
  console.log('a new subscription for huobi topic')
})

Client

const ws = adonis.Ws('ws://127.0.0.1:3333', {path: 'adonis-ws'});
      ws.connect();

      const huobi = ws.subscribe('huobi');

      huobi.on('ready', () => {
        huobi.emit('message', 'hello')
      })

      huobi.on('error', (error) => {
        console.log('error :',error)
      })

      huobi.on('close', (close) => {
        console.log('close: ', close);
      })

      huobi.on('message', (msg) => {
        console.log(msg);
      })

My WS controller

const WebSocket = require('ws');
const Event = use('Event')


class HuobiController {
  constructor ({socket}) {
        this.socket = socket
        this._url = 'wss://stream.binance.com:9443/ws/trxusdt@ticker'
        this._ev = 'message'
        this._client = new WebSocket(url)
        this._client._onOpen.bind(this)
        this._client._onMessage.bind(this)
    }

  _onOpen() {
    console.log('con opened on', this._url)
  }

  _onMessage (data) {
    console.log('data received on', this._url, data)
    Event.emit(this._ev, data)

  }

}

module.exports = HuobiController

Server.js

const cluster = require('cluster')

if (cluster.isMaster) {
  for (let i=0; i < 4; i ++) {
    cluster.fork()
  }
  require('@adonisjs/websocket/clusterPubSub')()
  return
}


const { Ignitor } = require('@adonisjs/ignitor')

new Ignitor(require('@adonisjs/fold'))
  .appRoot(__dirname)
  .wsServer()
  .fireHttpServer()
  .catch(console.error)

My Console

$ node server.js

2018-08-28T03:42:54.627Z - info: serving app on http://127.0.0.1:3333
2018-08-28T03:42:54.628Z - info: serving app on http://127.0.0.1:3333
2018-08-28T03:42:54.630Z - info: serving app on http://127.0.0.1:3333
2018-08-28T03:42:54.643Z - info: serving app on http://127.0.0.1:3333
a new subscription for huobi topic

If you can help me, I would greatly appreciate it.

regards


#8

You are mixing things here, the class i proposed to you was to extract the ws client from the controller and do them in a service provider. You are properly emitting the event but you do not listend to it

// config/wsClient.js

module.exports = {$
    event: 'my-event',
    uris: [
        'ws-remote-address-a',
        'ws-remote-address-a'
    ],

    // anything else relevant
}

// app/Providers/wsClient/Connection.js
const WebSocket = require('ws');
const Event = use('Event')

class Connection {
    constructor ({ uri, event }) {
        this._url = url
        this._ev = evName$
        this._reconnectInterval = 2500
    }

    _open() {
        this._client = new WebSocket(this._url)

        this._client.on('open', this._onOpen.bind(this))
        this._client.on('message', this._onMessage.bind(this))
        this._client.on('close', this._onClose.bind(this))
        this._client.on('error', this._onError.bind(this))
    }

    _onOpen() {
        console.log('con opened on', this._url)
    }

    _onMessage (data) {
        console.log('data received on', this._url, data)
        Event.emit(this._ev, data)
    }

    _onClose(e) {
		switch (e.code){
		case 1000:	// CLOSE_NORMAL
			console.log("WebSocket: closed")
			break
		default:	// Abnormal closure
			this._reconnect(e)
			break
		}
		this._onclose(e)
    }

    _onError(e) {
        switch (e.code) {
            case 'ECONNREFUSED':
                this._reconnect(e)
                break
            default:
                this.onerror(e)
                break
            }
    }

    _reconnect(e) {
        this._client.removeAllListeners()
        setTimeout(this._open.bind(this), this._reconnectInterval)
    }
}

module.exports = WsConnection

// app/Providers/WsClient/ServiceProvider.js

const Config = use('Config')
const Connection = require('./Connection')

module.exports = class WsClientService {
    boot() {
        const config = Config.get('wsClient')
        const connections = new Set()

        config.uris.forEach(client => {
            connections.add(new Connection({ uri, event: config.event }))
        })
    }
}

// start/app.js

const providers = [
  '@adonisjs/framework/providers/AppProvider',
  '@adonisjs/framework/providers/ViewProvider',
  '@adonisjs/lucid/providers/LucidProvider',
  '@adonisjs/bodyparser/providers/BodyParserProvider',
  '@adonisjs/cors/providers/CorsProvider',
  '@adonisjs/shield/providers/ShieldProvider',
  '@adonisjs/session/providers/SessionProvider',
  '@adonisjs/auth/providers/AuthProvider',
  `${use('Helpers').rootPath()}/app/Providers/WsClient/ServiceProvider` <- register the service provider globally
]

// routing

const Event = use('Event')
const eventName = use('Config').get('wsConfig.event')
const Ws = use('Ws')

Ws.channel('huobi', ({ socket }) => {
  Event.on(eventName, data => {
      socket.broadcastToAll('message', data)
  })
})

need some polish but should work has expected