简体   繁体   English

如何在 Electron (Next.js/React) 中实现 websocket 客户端?

[英]How to implement websocket client in Electron (Next.js/React)?

I have a working websocket server.我有一个工作的 websocket 服务器。 I use a websocket as client in web browser/react before, but I'm unable to use Websocket inside electron app since WebSocket depends on browser's compatibility and for some reason, this feature is unavailable in Electron.我以前在网络浏览器/反应中使用 websocket 作为客户端,但我无法在电子应用程序中使用 Websocket,因为 WebSocket 取决于浏览器的兼容性,并且由于某种原因,此功能在 Electron 中不可用。

I use nextron (nextjs/react + electron) boilerplate.我使用 nextron (nextjs/react + electron) 样板。

yarn create nextron-app MY_APP --example with-typescript-material-ui

import React from 'react';
import Head from 'next/head';
import { ThemeProvider } from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';
import { theme } from '../lib/theme';
import type { AppProps } from 'next/app';

export default function (props: AppProps) {
  const { Component, pageProps } = props;

  // where to put ws here ? this placement generates an error
  const ws = new WebSocket("ws://192.168.100.8:8081/")
  console.log("file: _app.tsx:11 ~ ws", ws)

  React.useEffect(() => {
    const jssStyles = document.querySelector('#jss-server-side');
    if (jssStyles) {
      jssStyles.parentElement.removeChild(jssStyles);
    }
  }, []);

  return (
    <React.Fragment>
      <Head>
        <meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
      </Head>
      <ThemeProvider theme={theme}>
        <CssBaseline />
        <Component {...pageProps} />
      </ThemeProvider>
    </React.Fragment>
  );
}

在此处输入图像描述

Apparently it can't be done.显然是做不到的。 Electron blocks any attempt to use websocket inside the client (React). Electron 阻止任何在客户端 (React) 内使用 websocket 的尝试。 The only solution so far I use is to convert websocket messages and events into ipcRenderer ( https://www.electronjs.org/docs/latest/api/ipc-renderer )到目前为止我使用的唯一解决方案是将 websocket 消息和事件转换为ipcRenderer ( https://www.electronjs.org/docs/latest/api/ipc-renderer )

the process is fairly simple: React receives an event and calls the IPCRenderer on the client, The IPCRenderer receive an event in the renderer (node.js) and call upon the ws or the websocket in the node.js这个过程相当简单:React 接收一个事件并在客户端调用 IPCRenderer,IPCRenderer 在渲染器(node.js)中接收一个事件并调用 node.js 中的ws或 websocket

Sending:发送:

React -> IPC -> Node.js -> websocket -> Websocket Server (main server) 

Receiving:接收:

Websocket Server (main server) -> websocket -> Node.js -> IPC -> React

I use the ws module fro node.js from here我从这里使用 node.js 的ws模块

I hope this can help someone or myself in the future我希望这可以帮助将来的人或我自己

If you want to send/broadcast an event to websocket如果你想发送/广播事件到 websocket

// in the client side usually the root component, or in my case _app.tsx:
import electron from 'electron'
const ipcRenderer = electron.ipcRenderer

class _app extends React.Component<any, any> {
  constructor(props: any) {...}
  componentDidMount() {...}

  // this will send a message for a button click
  handleClick(msg: string) {
      // reply will be true if it succeed
      let reply = ipcRenderer.sendSync('some-event', msg)
  }
}

later in the main window app.js:稍后在主窗口 app.js 中:

import { app, ipcMain } from 'electron'
import WebSocket from 'ws'

// connect with ws
let ws = new WebSocket(`ws://${YOUR_WS_SERVER_IP}:${YOUR_WS_PORT}/`)

// find the electron main window, mine's in background.ts
const mainWindow = createWindow('main', {
    width: 1366,
    height: 768,
    minWidth: 1366,
    minHeight: 768
})

// .... some electron code here ....

// when ipcMain receive an event named 'some-event'
ipcMain.on('some-event', (event, msg) => {
  ws.send(msg) // send message using websocket here
  event.returnValue = true // give a return value true 
})

If you want to handle an event received from websocket如果你想处理从 websocket 接收到的事件

import { app, ipcMain } from 'electron'
import WebSocket from 'ws'

// connect with ws
let ws = new WebSocket(`ws://${YOUR_WS_SERVER_IP}:${YOUR_WS_PORT}/`)

// find the electron main window, mine's in background.ts
const mainWindow = createWindow('main', {
  width: 1366,
  height: 768,
  minWidth: 1366,
  minHeight: 768
})

ws.on("message", (message: any) => {
   var str = message.toString()
   console.log("Message received: ", str)
   
   mainWindow.webContents.send('some-event', str)
})

on the react component (App.tsx):在反应组件(App.tsx)上:

// in the client side usually the root component, or in my case _app.tsx:
import electron from 'electron'
const ipcRenderer = electron.ipcRenderer

class _app extends React.Component<any, any> {
  constructor(props: any) {
      super(props)
      // I put this since I use class based component. 
      // a functional ones won't need this
      this.handleIpc = this.handleIpc.bind(this)
  }
  componentDidMount() {
      this.handleIpc() // to make sure the ipcRenderer manipulate the component state AFTER the whole component was loaded first
  }

  handleIpc() {
    var self = this
    ipcRenderer.on("some-event", function (e, data) {
       console.log("Message received: ", data)
    })
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM