Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Electron: socket.io can receive but not emit

I'm creating an Electron application that uses Socket.io to communicate to a server application, but I'm experiencing a weird issue: whereas my Electron app successfully joins and receives messages from my server, it completely fails to emit anything.

Client-side:

const io = require('socket.io-client');
// ...
var socket = io("http://localhost:8081");

socket.on('welcome', () => {
  console.log('welcome received'); // displayed
  socket.emit('test')
});
socket.on('error', (e) => {
  console.log(e); // not displayed
});
socket.on('ok', () => {
  console.log("OK received"); // not displayed
});
socket.on('connect', () => {
  console.log("connected"); // displayed
  socket.emit('test');
});

Server-side:

io.on('connection', (client) => {
  io.emit('welcome');

  client.on("test", () => {
      console.log("received test"); // not displayed
      io.emit("ok");
  })
});
io.listen(8081);

Note that there is also a Web client that connects to the server, and works absolutely as expected.

What am I doing wrong?

like image 972
Deuchnord Avatar asked Jun 06 '17 08:06

Deuchnord


Video Answer


1 Answers

Electron has the main process that can have node integration and renderer process that has nodeIntegration turned off by default for security reasons.

If you try to use your client code in the main process (main.js) it will work and receive/emit normal. But if you try to use the client code on the rendered process, then it won't work. You need to add the client code inside the preload.js and then preload it using the webPreferences.preload option of the BrowserWindow like this:

function createWindow () {
  // Create the browser window.
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  // and load the index.html of the app.
  mainWindow.loadFile('index.html')
  ...
}

Then you can have the client code inside preload.js:

const io = require('socket.io-client');
const socket = io(`http://localhost:${process.env.SOCKET_PORT}`);

socket.on('welcome', () => {
  console.log('on welcome : welcome received renderer'); // displayed
  socket.emit('test')
});
socket.on('error', (e) => {
  console.log(e); // displayed ?
});
socket.on('ok', () => {
  console.log("OK received renderer"); // displayed
});
socket.on('connect', () => {
  console.log("connected renderer"); // displayed
  socket.emit('test');
});

begin the socket, start the electron app and see it working:

Socket server:

socket.io server

App console:

Electron app

Beware of the Content Security Policy (CSP):

Content Security Policy error

To avoid this, you have to add your WS server to your CSP. For localhost use this inside index.html header:

<meta http-equiv="Content-Security-Policy" content="default-src 'self' ws://localhost:*/socket.io/; script-src 'self' ws://localhost:*/socket.io/">
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self' ws://localhost:*/socket.io/; script-src 'self' ws://localhost:*/socket.io/">

It's a localhost server for all ports and of course it's for development only, you have to enter normal WS server in production.

You can clone and check my working example from Github: https://github.com/clytras/electron-sockets

EDIT Last but not least; check and allow incomming/outgoing access for nodejs and/or port in firewal.

Electron version: 7.1.2
Node version: 12.8.1
like image 72
Christos Lytras Avatar answered Oct 18 '22 22:10

Christos Lytras