Electron: IPC vs. WebSocket

For local Node.js applications, which of these two data exchange modes should you choose?

We'll compare their respective features and performance...

Let's clarify that if WebSocket is available for all Node-based systems, then even a simple server script, similar to that described in the JavaScript section of this site, IPC, when it works only with Electron.

On the server side, WebSocket commands are based on the ws module. Other options with different syntaxes are possible. On the interface side, a standard WebSocket object is used.

  WebSocket IRAQI OIL COMPANY
 
SERVER
Import const WebSocketServer = require («ws «) .Server; const {ipcMain} = require ('electron') . ipcMain
Create a communication object w = new WebSocketServer (port) -
Waiting for link to open w.on ("compound," (w) => {}) -
Data transfer to interface w.send (data) event.sender.send ("channel," data)
Synchronous data transmission - event.returnValue = data
Retrieving data from an interface w.on ("message," (m) => {}) ipcMain.on ("channel," (e, o) => {})
Closing of communication channel w.on ("close," () => {}) -
     
 
INTERFACE SIZE
Import - const ipcRenderer = require ('electron') .ipcRenderer
Creating an object const w = new WebSocket (port); -
Sending data to the server w.send (data) ipcRender.send ("channel," data)
Synchronous data transmission - ipcRender.sendSyc ("channel," data)
Retrieving data from the server w.onmessage = function (event) {}) ipcRender.on ("channel," (event) => {})
Closing of communication channel w.close () -

You can see the differences between the two protocols:

In terms of capabilities, IPC outperforms synchronous communication. By not using system ports, it also avoids the risk of collision when the port is already in use by another application.

IPC Demo

We will build a basic application with a window and a backend that communicates with the interface in synchronous and asynchronous modes.

The interface sends a "hello server" message to the backend, which responds via a "hello interface."
In asynchronous mode, the listener on the interface waits for a message to be received, on the "message" channel.
In synchronous mode, the server response is the return value of the function that sends the message to the server .

Server ID

const path = require("path")
const { app, BrowserWindow, ipcMain } = require('electron')
const print = console.log

let win
function createWindow() {
    win = new BrowserWindow({
        width: 960, height: 600, 
        title:"IPC vs. WebSocket",
        webPreferences : { nodeIntegration:true }
    })
 
    win.setMenu(null)
    const fpath = path.join(__dirname, 'ipc.html')
    win.loadURL(fpath)
    win.on('closed', () => { win = null })
}

// IPC

ipcMain.on('message', (event, data) => {
  print("Received: " + data) 
  event.sender.send('message', 'Hello interface!')
})

ipcMain.on('message-sync', (event, data) => {
  print("Received: " + data) 
  event.returnValue = 'Hello interface (synchronous)!'
})

// App

app.on('ready', createWindow)
app.on('window-all-closed', () => { 
    app.quit()
    process.exit(1)
})
app.on('quit', function () { 
    print("Done.")
})

In the demo, the event.sender.send command responds on the same "message" channel that is used when receiving, but it is also possible to send data on different multiple channels (as opposed to synchronous mode).

Browser side code

<!DOCTYPE html>
<html>
<body>
<h1>IPC Demo</h1>
<fieldset id="storage"></fieldset>
<fieldset id="storageSync"></fieldset>
<script>
const {ipcRenderer} = require('electron')
ipcRenderer.on('message', (event, data) => {
document.getElementById("storage").innerHTML = data
})
ipcRenderer.send('message', 'Hello server!')
var answer = ipcRenderer.sendSync('message-sync', 'Hello server sync!')
document.getElementById("storageSync").innerHTML = answer
</script>
</body>
</html>

To run the program, enter "electron ipc.js" in the script directory.

WebSocket Demo

As before, the interface sends a "Hello server!" Message to the backend, which instead sends a "Hello interface!" To the browser.

Server ID

The backend imports the ws module that is included in the archive.

const path = require("path")
const { app, BrowserWindow  } = require('electron')
const WebSocket = require("ws")

const wss = new WebSocket.Server( { port: 1040 } )

let win
function main() {
    win = new BrowserWindow({
        width: 960, height: 600, 
        title:"WebSocket Demo"
    })
    win.setMenu(null)

    const fpath = path.join(__dirname, 'websocket.html')
    win.loadURL(fpath)
    win.on('closed', () => { win = null })
    
    wss.on('connection', function (w) {  
        w.on( 'message' , function (data)  {
             console.log(data)
        })  
        w.on('close', function() { 
             console.log("Closed") 
        })    
        w.send("Hello interface!")
    })    
}

app.on('ready', main)
app.on('window-all-closed', () => { 
    app.quit()
    process.exit(1)
})
app.on('quit', function () { 
    console.log("Done.")
})

Browser side code

The interface uses a standard browser WebSocket object.

<!DOCTYPE html>
<html>
<body>
<h1>WebSocket Demo</h1>
<fieldset id="storage"></fieldset>
<script>
const socket = new WebSocket("ws://localhost:1040");
socket.onmessage = function(event) {
var data = event.data
document.getElementById("storage").innerHTML = data
}
socket.onopen = function() {
socket.send('Hello server!')
}
</script>
</body>
</html>

The code is a little simpler, because only the asynchronous mode is tested and this time you do not need to enable the Electron module.

Type "electron websocket.js" to run the script.

Comparative speeds

My original intention was to continue the comparison with new scripts exchanging a series of data to compare the speed of the two protocols. But when you performed the two previous scripts, you can see that it is useless. While data is instantly displayed using IPC, noticeable time is observed using WebSocket.
And this is normal, IPC is internal to Electron, while WebSocket passes through the computer's network system, with all its necessary restrictions and controls.

Thus, since the decision has been made to use Electron, CPI should also become the preferred method of communication unless a two-way system is required, such as server notifications.

Download Scripts