受祝福的服务器(Node.js)通过 websocket 到浏览器中的 Xterm.js 客户端

Posted

技术标签:

【中文标题】受祝福的服务器(Node.js)通过 websocket 到浏览器中的 Xterm.js 客户端【英文标题】:Blessed server (Node.js) over websocket to Xterm.js client in Browser 【发布时间】:2018-10-16 20:52:57 【问题描述】:

我有什么:

Node.js 脚本运行 Blessed 和 http/websocket 服务器。 浏览器运行 Xterm.js 和 websocket 客户端。

我想做什么:

通过 websockets 为 xterm 窗口渲染祝福。

服务器代码:

"use strict";

process.title = 'neosim-server';

var blessed = require('neo-blessed');
var contrib = require('blessed-contrib');
var webSocketServer = require('websocket').server;
var http = require('http');

const webSocketsServerPort = 8080;
var clients = [ ];

/**
 * HTTP server
 */
var server = http.createServer(function(request, response) 
    // Not important for us. We're writing WebSocket server,
    // not HTTP server
);
server.listen(webSocketsServerPort, function() 
    console.log((new Date()) + " Server is listening on port "
        + webSocketsServerPort + ".");
);

/**
 * WebSocket server
 */
var wsServer = new webSocketServer(
    // WebSocket server is tied to a HTTP server. WebSocket
    // request is just an enhanced HTTP request. For more info 
    // http://tools.ietf.org/html/rfc6455#page-6
    httpServer: server,
    autoAcceptConnections: false
);

function originIsAllowed(origin) 
    // put logic here to detect whether the specified origin is allowed.

    return true;
  

// This callback function is called every time someone
// tries to connect to the WebSocket server
wsServer.on('request', function(request) 
    if (!originIsAllowed(request.origin)) 
        request.reject();
        console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
        return;
    
    console.log((new Date()) + ' Connection from origin '
        + request.origin + '.');

    // accept connection - you should check 'request.origin' to
    // make sure that client is connecting from your website
    // (http://en.wikipedia.org/wiki/Same_origin_policy)
    var connection = request.accept(null, request.origin); 
    // we need to know client index to remove them on 'close' event
    connection.write = connection.send;
    connection.read = connection.socket.read;
    connection.program = blessed.program(
        tput: true,
        input: connection,
        output: connection
    );
    connection.program.tput = blessed.tput(
        term: 'xterm-256color',
        extended: true,
    );
    connection.screen = blessed.screen(
        program: connection.program,
        tput: connection.program.tput,
        input: connection,
        output: connection,
        //smartCSR: true,
        terminal: 'xterm-256color',
        fullUnicode: true
    );


    var index = clients.push(connection) - 1;

    var userName = false;

    console.log((new Date()) + ' Connection accepted.');
    connection.program.write("test");

    // send back chat history
    /*if (history.length > 0) 
    connection.sendUTF(
        JSON.stringify( type: 'history', data: history ));
    */

    // terminal type message
    connection.on('term', function(terminal) 
        console.log("Term");
        connection.screen.terminal = terminal;
        connection.screen.render();
    );

    connection.on('resize', function(width, height) 
        console.log("Resize");
        connection.columns = width;
        connection.rows = height;
        connection.emit('resize');
    );

    // user sent some message
    connection.on('message', function(message) 
        if (message.type === 'utf8')  // accept only text
            console.log((new Date()) + ' Received Message: ' + message.utf8Data);
        
    );

    // user disconnected
    connection.on('close', function(connection) 
        if (connection.screen && !connection.screen.destroyed) 
            connection.screen.destroy();
        
    );

    connection.screen.key(['C-c', 'q'], function(ch, key) 
        connection.screen.destroy();
    );

    connection.screen.on('destroy', function() 
        if (connection.writable) 
            connection.destroy();
        
    );

    connection.screen.data.main = blessed.box(
        parent: connection.screen,
        left: 'center',
        top: 'center',
        width: '80%',
        height: '90%',
        border: 'line',
        content: 'Welcome to my server. Here is your own private session.'
    );

    connection.screen.render();
);

客户页面:

<!doctype html>
  <html lang="en">
    <head>
      <link rel="stylesheet" href="node_modules/xterm/dist/xterm.css" />
      <script src="node_modules/xterm/dist/xterm.js"></script>
      <script src="node_modules/xterm/dist/addons/attach/attach.js"></script>
      <script src="node_modules/xterm/dist/addons/fit/fit.js"></script>
      <script src="node_modules/xterm/dist/addons/winptyCompat/winptyCompat.js"></script>
    </head>
    <body>
      <div id="terminal"></div>
      <script>
        Terminal.applyAddon(attach);
        Terminal.applyAddon(fit);
        Terminal.applyAddon(winptyCompat);
        var term = new Terminal();
        var socket = new WebSocket('ws://localhost:8080', null);

        term.open(document.getElementById('terminal'));
        term.winptyCompatInit();
        term.fit();
        term.focus();
        term.write('Terminal ('+term.cols+'x'+term.rows+')\n\r');
//        term.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ')

//        window.addEventListener('resize', resizeScreen, false)

        function resizeScreen () 
            term.fit();
            socket.emit('resize',  cols: term.cols, rows: term.rows );
        ;

        socket.onopen = function (connection) 
            term.attach(socket, true, false);
            //term.emit('term');
            //term.emit('resize');
        ;

        socket.onmessage = function (message) 
            term.write(message);
        ;
      </script>
    </body>
  </html>

我遇到的问题是调用 screen.render() 时,客户端上没有显示任何内容。创建我的blessed.screen 时,我尝试使用:

connection.screen = blessed.screen(input: connection.socket, output: connection.socket);

但这也不起作用。在我当前的服务器代码中,我试图欺骗祝福使用connection.send而不是connect.socket.write,因为我认为浏览器websockets只接受'onmessage'事件。像这样:

connection.write = connection.send;
connection.read = connection.socket.read;
connection.screen = blessed.screen(input: connection, output: connection);

到目前为止,我尝试过的任何方法都没有奏效。我在这里做错了什么?或者仅仅是祝福不会与浏览器websockets一起使用。另外,我知道连接正常,因为我可以在两端发送/接收消息。

【问题讨论】:

【参考方案1】:

我让它工作了。我将 http 响应添加到您的服务器以提供 html/js 文件 - 只是为了将所有内容集中在一个地方。主要问题是客户端中 websocket 连接的第二个参数为空。我刚刚删除它,然后它正在播放。

还添加了按 CR 时输入的回声,以便您从服务器获得响应。

查看下面的修改来源

服务器代码

process.title = 'neosim-server';

var blessed = require('neo-blessed');
var contrib = require('blessed-contrib');
var webSocketServer = require('websocket').server;
var http = require('http');
var fs = require('fs');

const webSocketsServerPort = 8080;
var clients = [ ];

/**
 * HTTP server
 */
var server = http.createServer(function(request, response) 
    // Not important for us. We're writing WebSocket server,
    // not HTTP server    

    let path = request.url.substring(1);
    if(path === '') 
        path = 'index.html';
    

    if(fs.existsSync(path)) 
        if(path.indexOf('.js') === path.length-3) 
            response.setHeader('Content-Type', 'application/javascript');
        
        response.end(fs.readFileSync(path));
     else 
        response.statusCode = 404;
        response.end('');
    
);

server.listen(webSocketsServerPort, function() 
    console.log((new Date()) + " Server is listening on port "
        + webSocketsServerPort + ".");
);

/**
 * WebSocket server
 */
var wsServer = new webSocketServer(
    // WebSocket server is tied to a HTTP server. WebSocket
    // request is just an enhanced HTTP request. For more info 
    // http://tools.ietf.org/html/rfc6455#page-6
    httpServer: server,
    autoAcceptConnections: false
);

function originIsAllowed(origin) 
    // put logic here to detect whether the specified origin is allowed.

    return true;
  

// This callback function is called every time someone
// tries to connect to the WebSocket server
wsServer.on('request', function(request) 
    if (!originIsAllowed(request.origin)) 
        request.reject();
        console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
        return;
    
    console.log((new Date()) + ' Connection from origin '
        + request.origin + '.');

    // accept connection - you should check 'request.origin' to
    // make sure that client is connecting from your website
    // (http://en.wikipedia.org/wiki/Same_origin_policy)
    var connection = request.accept(null, request.origin); 
    // we need to know client index to remove them on 'close' event
    connection.write = connection.send;
    connection.read = connection.socket.read;
    connection.program = blessed.program(
        tput: true,
        input: connection,
        output: connection
    );
    connection.program.tput = blessed.tput(
        term: 'xterm-256color',
        extended: true,
    );
    connection.screen = blessed.screen(
        program: connection.program,
        tput: connection.program.tput,
        input: connection,
        output: connection,
        //smartCSR: true,
        terminal: 'xterm-256color',
        fullUnicode: true
    );


    var index = clients.push(connection) - 1;

    var userName = false;

    console.log((new Date()) + ' Connection accepted.');
    connection.program.write("test");

    // send back chat history
    /*if (history.length > 0) 
    connection.sendUTF(
        JSON.stringify( type: 'history', data: history ));
    */

    // terminal type message
    connection.on('term', function(terminal) 
        console.log("Term");
        connection.screen.terminal = terminal;
        connection.screen.render();
    );

    connection.on('resize', function(width, height) 
        console.log("Resize");
        connection.columns = width;
        connection.rows = height;
        connection.emit('resize');
    );

    let buf = '';
    // user sent some message
    connection.on('message', function(message) 
        if (message.type === 'utf8')  // accept only text
            console.log((new Date()) + ' Received Message: ' + message.utf8Data);
            buf += message.utf8Data;
            if(message.utf8Data === '\r') 
                console.log('You wrote:', buf);
                connection.sendUTF(buf +'\r\n');
                buf = '';
            

        
    );

    // user disconnected
    connection.on('close', function(connection) 
        if (connection.screen && !connection.screen.destroyed) 
            connection.screen.destroy();
        
    );

    connection.screen.key(['C-c', 'q'], function(ch, key) 
        connection.screen.destroy();
    );

    connection.screen.on('destroy', function() 
        if (connection.writable) 
            connection.destroy();
        
    );

    connection.screen.data.main = blessed.box(
        parent: connection.screen,
        left: 'center',
        top: 'center',
        width: '80%',
        height: '90%',
        border: 'line',
        content: 'Welcome to my server. Here is your own private session.'
    );

    connection.screen.render();
);

客户端代码:

<!doctype html>
  <html lang="en">
    <head>
      <link rel="stylesheet" href="node_modules/xterm/dist/xterm.css" />
      <script src="node_modules/xterm/dist/xterm.js"></script>
      <script src="node_modules/xterm/dist/addons/attach/attach.js"></script>
      <script src="node_modules/xterm/dist/addons/fit/fit.js"></script>
      <script src="node_modules/xterm/dist/addons/winptyCompat/winptyCompat.js"></script>
    </head>
    <body>
      <div id="terminal"></div>
      <script>
        Terminal.applyAddon(attach);
        Terminal.applyAddon(fit);
        Terminal.applyAddon(winptyCompat);
        var term = new Terminal();
        var socket = new WebSocket('ws://localhost:8080');

        term.open(document.getElementById('terminal'));
        term.winptyCompatInit();
        term.fit();
        term.focus();
        term.write('Terminal ('+term.cols+'x'+term.rows+')\n\r');
//        term.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ')

//        window.addEventListener('resize', resizeScreen, false)

        function resizeScreen () 
            term.fit();
            socket.emit('resize',  cols: term.cols, rows: term.rows );
        ;

        socket.onopen = function (connection) 
          console.log('connection opened');
            term.attach(socket, true, false);
            //term.emit('term');
            //term.emit('resize');
        ;

        socket.onmessage = function (message) 
            term.write(message);
        ;
      </script>
    </body>
  </html>

【讨论】:

您将如何销毁您创建的外壳并重新生成一个新外壳?

以上是关于受祝福的服务器(Node.js)通过 websocket 到浏览器中的 Xterm.js 客户端的主要内容,如果未能解决你的问题,请参考以下文章

IPC::Shareable 是不是适用于受祝福的对象?

httpOnly cookie 中的 JWT - AuthGuard 和受保护的路由(Node.js、Angular)

在 node.js 中生成受密码保护的 ZIP 文件

Vue.js 如何使用 Socket.IO?

Node.js API 受 Keycloak 保护,访问类型为“仅承载”

如何在 node.js 中设置受保护的路由?与我拥有的 userRoutes.js 相同