Xterm.js: يمكن إرفاق xterm استخدام المقبس. io؟

تم إنشاؤها على ١٦ مارس ٢٠١٩  ·  63تعليقات  ·  مصدر: xtermjs/xterm.js


import * as Terminal from 'xterm';
import * as attach from 'xterm/lib/addons/attach/attach';

Terminal.applyAddon(attach);  // Apply the `attach` addon

var term = new Terminal();
var socket = new WebSocket('wss://docker.example.com/containers/mycontainerid/attach/ws');

term.attach(socket);  // Attach the above socket to `term`

لا أريد استخدام مقبس الويب مباشرة

typquestion

التعليق الأكثر فائدة

@ BengBu-YueZhang ممكن ، لكن سيتعين عليك توفير وظيفة الإرفاق الخاصة بك. تحت الغطاء ، يحتوي الكائن الطرفي على طريقة write ، والتي يجب أن يتم لصقها بأي واجهة برمجة تطبيقات إخراج تستخدمها. بالنسبة للحالة العكسية ، قم ببساطة بإرفاق حدث "البيانات" بمخزن إدخال الواجهة. كلا الاتجاهين يعملان حاليًا على سلاسل JS. لمزيد من المعلومات راجع https://github.com/xtermjs/xterm.js/blob/master/src/addons/attach/attach.ts

ال 63 كومينتر

@ BengBu-YueZhang ممكن ، لكن سيتعين عليك توفير وظيفة الإرفاق الخاصة بك. تحت الغطاء ، يحتوي الكائن الطرفي على طريقة write ، والتي يجب أن يتم لصقها بأي واجهة برمجة تطبيقات إخراج تستخدمها. بالنسبة للحالة العكسية ، قم ببساطة بإرفاق حدث "البيانات" بمخزن إدخال الواجهة. كلا الاتجاهين يعملان حاليًا على سلاسل JS. لمزيد من المعلومات راجع https://github.com/xtermjs/xterm.js/blob/master/src/addons/attach/attach.ts

أعلق هنا لأنني كنت أحاول الحصول على AttachAddon للعمل مع socketio . لقد جعلته يعمل في معظم الأحيان ولكن الناتج دائمًا ما ينقطع عندما أستخدم أوامر مثل vim أو nano .

هذا هو الملف AttachAddon.ts أستخدمه مع socketio:

/**
 * Copyright (c) 2014, 2019 The xterm.js authors. All rights reserved.
 * <strong i="12">@license</strong> MIT
 *
 * Implements the attach method, that attaches the terminal to a SocketIO stream.
 */

import { IDisposable, ITerminalAddon, Terminal } from 'xterm';

interface IAttachOptions {
  bidirectional?: boolean;
  inputUtf8?: boolean;
}

export class AttachAddon implements ITerminalAddon {
  private _socket: SocketIOClient.Socket;
  private _bidirectional: boolean;
  private _utf8: boolean;
  private _disposables: IDisposable[] = [];

  constructor(socket: SocketIOClient.Socket, options?: IAttachOptions) {
    this._socket = socket;
    // always set binary type to arraybuffer, we do not handle blobs
    // this._socket.binaryType = 'arraybuffer';
    this._bidirectional = (options && options.bidirectional === false) ? false : true;
    this._utf8 = !!(options && options.inputUtf8);
  }

  public activate(terminal: Terminal): void {
    if (this._utf8) {
      this._disposables.push(addSocketListener(this._socket, 'message',
        (data: any) => terminal.writeUtf8(new Uint8Array((data || "") as ArrayBuffer))));
    } else {
      this._disposables.push(addSocketListener(this._socket, 'message',
        (data: any) => terminal.write((data || "") as string)));
    }

    if (this._bidirectional) {
      this._disposables.push(terminal.onData(data => this._sendData(data)));
    }

    this._disposables.push(addSocketListener(this._socket, 'close', () => this.dispose()));
    this._disposables.push(addSocketListener(this._socket, 'error', () => this.dispose()));
  }

  public dispose(): void {
    this._disposables.forEach(d => d.dispose());
  }

  private _sendData(data: string) {
    this._socket.send(data);
  };

}

function addSocketListener(socket: SocketIOClient.Socket, type: string, handler: (this: SocketIOClient.Socket, data: string) => any): IDisposable {
  socket.addEventListener(type, handler);
  return {
    dispose: () => {
      if (!handler) {
        // Already disposed
        return;
      }
      socket.removeEventListener(type, handler);
    }
  };
}

وإليك ما يبدو عليه تنفيذ الأمر vim example.txt (لاحظ أن example.txt ليس فارغًا):

Screen Shot 2019-07-26 at 3 03 56 PM

AhanM هل هذا سؤال كيفية إصلاح المشكلات المتبقية؟ بشكل أساسي عليك التأكد من أن تشفير النقل الذي يتم بواسطة socketIO لا يفسد وحدات بايت الإدخال. عادةً ما يستخدم محررو المحطة الطرفية استخدامًا مكثفًا لأحرف التحكم والتسلسلات ، ويبدو أن لقطة الشاشة أعلاه تبدو وكأنها معطلة لسبب ما
أسهل حل هو التبديل إلى النقل الثنائي الوارد (إذا كان socketIO يدعم ذلك) والتعامل مع البايت باستخدام writeUtf8 .

jerch نعم أحاول إصلاح إخراج vim و nano. أنا أستخدم writeUtf8 في طريقة activate عند تعيين الخيار inputUtf8 . أعتقد أن هذه هي نفس الطريقة التي يتبعها AttachAddon لمآخذ الويب العادية. يرجى إعلامي إذا كان هناك أي شيء آخر يتعلق بك من الإصدار الخاص بي من AttachAddon.ts .

كما أنني متأكد تمامًا من أن socket.io يدعم النقل الثنائي.

AhanM وكيف يبدو جزء الخادم؟

الواجهة الخلفية هي خادم قارورة python يستخدم flask-socketio. لدي عامل يقرأ الإخراج الطرفي في سلسلة رسائل خلفية باستخدام مكتبة تسمى

هذا هو الكود الجانبي للخادم الذي أستخدمه لقراءة الإخراج الطرفي:

 def read(self) -> None:
        """ Attempt to read from the channel stdout and send back to socket client """
        logging.info('Worker ' + self.id + ' reading')
        try:
            data = self.channel.recv(BUF_SIZE)
        except Exception as e:
            logging.error('Closing worker {} due to exception: {}'.format(self.id, str(e)))
            raise e

        if not data:
            self.close(reason='Channel closed')
            logging.error('Closing worker {} due to reason: {}'.format(self.id, 'Channel closed'))

            return
        else:
            data = data

        self.sio.emit('message', data)

AhanM الجزء المهم هنا هو الطريقة ، كيف تنقل أجزاء pty إلى xterm.js

jerch إذن هذه حالة فريدة لأنني لا أقوم بتشغيل محطة زائفة محليًا على الخادم ولكني أشغل ssh-ing في جهاز آخر (يحتوي على مورد مهم). هذا هو السبب في أنني أستخدم Paramiko وليس pty

AhanM هل يرسل مكون الباراميكو bytestrings (مصفوفات البايت) أو سلاسل UTFxy المشفرة؟ هذا أمر صعب إلى حد ما ليتم تنفيذه بشكل صحيح مع Python3 ، في Python2 ، يمكن استخدام نوع السلسلة العادية مباشرة كحاوية بايت. هل يعمل جزء الباراميكو بشكل صحيح إذا قمت بإرفاقه بمحطة محلية (إخراجها مباشرة إلى طرف جنوم أو إكسترم)؟

واجهت نفس مشكلة AhanM واكتشفت أن Socket.io يقوم بإرفاق أحرف فارغة بكل ضغطة مفتاح. تمكنت من إصلاح ذلك باستخدام str.replace ('0'، ') في نهاية كل سلسلة.

اكتشفت هذا عند الضغط على "g" (وأي حرف آخر ، على التوالي) وكنت أقوم بإنشاء هذا.

abs2str: "ز \ u0000"

أدى هذا إلى إصلاح مشكلة التعطيل باستخدام nano.

jzombie هل يمكنك str.replace في ملحق الملحق؟

jzombie : رائع ، هذا محرج ، إذا كان هذا هو الحال ، فسأسمي هذا خطأ في SocketIO. هل أنت متأكد من أنك تستخدم نقل السلسلة وليس ثنائي؟ لا أعرف أي شيء عن العناصر الداخلية لـ SocketIO ، لا يزال النظام الثنائي يحافظ على الإنهاء الفارغ كجزء من الرسالة.

حصلت على xterm للعمل مع Socket.io للعمل باستخدام كتابة xterm (وليس استخدام writeUtf8) واستخدام هذه الأساليب على كل من العميل والخادم.

يجب أن يرسل Socket.io حرفًا فارغًا مع السلسلة ، لذلك عند التحويل إلى سلسلة ، أضفت str.replace ('0'، '') إلى نهايتها.

لم يتم اختباره بدقة ولكن يمكنني الآن الكتابة أفقيًا بتقنية النانو واستخدام مفاتيح التشغيل السريع كما ينبغي.

  function str2ab(str) {
    const buf = new ArrayBuffer(str.length * 2); // 2 bytes for each char
    const bufView = new Uint8Array(buf);
    for (let i = 0, strLen = str.length; i < strLen; i++) {
      bufView[i] = str.charCodeAt(i);
    }

    return buf;
  }

  function ab2str(buf) {
    let str = String.fromCharCode.apply(null, new Uint8Array(buf)).replace('\0', '');

    return str;
  }

jzombie في أي اتجاه (pty -> xterm.js مقابل xterm.js -> pty) هل ينطبق ذلك؟

لاحظ أيضًا أن String.fromCharCode.apply(null, new Uint8Array(buf)) سيؤدي إلى ظهور خطأ في قطع البيانات الأطول بسبب الدخول في حد العودية داخليًا.

لقد فعلت ذلك من كلا الطرفين. الضغط على المفاتيح وتشغيلها من خلال str2ab وتشغيل ab2str على الخادم. ثم قم بتشغيل str2ab من الخادم ، و ab2str على العميل. فقط باستخدام xterm.write () ، وليس writeUtf8 () هنا.

نعم ، ليس لدينا حاليًا طريقة لتقديم البايت الخام من xterm.js إلى الواجهة الخلفية. # 2362 يحل هذا ، بحيث يمكن تشغيله باستخدام socketIO مباشرة في الوضع الثنائي (يجب أن يختفي \ 0 مع هذا).

لست متأكدًا من سبب وجود العديد من المشكلات لإعداد هذا ، لقد اخترقت بشكل أساسي socketIO في العرض التوضيحي الخاص بنا مع بضعة أسطر من التعليمات البرمجية وهو يعمل دون أي مشاكل. هنا هو الفرق للسيد الحالي:

diff --git a/demo/client.ts b/demo/client.ts
index 040292e4..f9a909bd 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -37,6 +37,7 @@ export interface IWindowWithTerminal extends Window {
   WebglAddon?: typeof WebglAddon;
 }
 declare let window: IWindowWithTerminal;
+declare let io: any;

 let term;
 let fitAddon: FitAddon;
@@ -157,10 +158,15 @@ function createTerminal(): void {
       res.text().then((processId) => {
         pid = processId;
         socketURL += processId;
-        socket = new WebSocket(socketURL);
-        socket.onopen = runRealTerminal;
-        socket.onclose = runFakeTerminal;
-        socket.onerror = runFakeTerminal;
+        // socket = new WebSocket(socketURL);
+        // socket.onopen = runRealTerminal;
+        // socket.onclose = runFakeTerminal;
+        // socket.onerror = runFakeTerminal;
+
+        const ioSocket = io('localhost:3001');
+        ioSocket.emit('attach', processId);
+        ioSocket.on('data', data => term.write(data));
+        term.onData(data => ioSocket.emit('data', data));
       });
     });
   }, 0);
@@ -172,7 +178,7 @@ function runRealTerminal(): void {
    * To run it with UTF8 binary transport, swap comment on
    * the lines below. (Must also be switched in server.js)
    */
-  term.loadAddon(new AttachAddon(socket));
+  // term.loadAddon(new AttachAddon(socket));
   // term.loadAddon(new AttachAddon(socket, {inputUtf8: true}));

   term._initialized = true;
diff --git a/demo/index.html b/demo/index.html
index ef8f3891..1de9a6e7 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -7,6 +7,7 @@
     <link rel="stylesheet" href="/style.css" />
     <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-promise/4.1.1/es6-promise.auto.min.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/1.0.0/fetch.min.js"></script>
+    <script src="socket.io.js"></script>
   </head>
   <body>
     <h1 style="color: #2D2E2C">xterm.js: A terminal for the <em style="color: #5DA5D5">web</em></h1>
diff --git a/demo/server.js b/demo/server.js
index 8955431b..aa806505 100644
--- a/demo/server.js
+++ b/demo/server.js
@@ -13,6 +13,8 @@ const USE_BINARY_UTF8 = false;
 function startServer() {
   var app = express();
   expressWs(app);
+  var http = require('http').createServer(app);
+  var io = require('socket.io')(http);

   var terminals = {},
       logs = {};
@@ -36,6 +38,10 @@ function startServer() {
     res.sendFile(__dirname + '/dist/client-bundle.js');
   });

+  app.get('/socket.io.js', function(req, res){
+    res.sendFile(__dirname + '/socket.io.js');
+  });
+
   app.post('/terminals', function (req, res) {
     const env = Object.assign({}, process.env);
     env['COLORTERM'] = 'truecolor';
@@ -71,6 +77,20 @@ function startServer() {
     res.end();
   });

+  io.on('connection', function(socket) {
+    // console.log(socket);
+    console.log('a user connected');
+    socket.on('disconnect', function(){
+      console.log('user disconnected');
+    });
+    socket.on('attach', pid => {
+      console.log('attaching to terminal', pid);
+      var term = terminals[parseInt(pid)];
+      term.on('data', data => socket.emit('data', data));
+      socket.on('data', data => term.write(data));
+    });
+  });
+
   app.ws('/terminals/:pid', function (ws, req) {
     var term = terminals[parseInt(req.params.pid)];
     console.log('Connected to terminal ' + term.pid);
@@ -135,6 +155,10 @@ function startServer() {

   console.log('App listening to http://127.0.0.1:' + port);
   app.listen(port, host);
+
+  http.listen(3001, function(){
+    console.log('listening on *:3001');
+  });
 }

لاحظ أن هذا لم يتم دمجه أو تنظيفه بشكل جيد ، وكان عليه أيضًا استخدام منفذ آخر لتشغيله بسرعة. ولكن يجب أن يُظهر أساسيات كافية لإنجاز شيء مشابه للملحق الإضافي.

كما أنني لا أرى أي "\ 0" في نهاية الرسالة؟

jerch لأنك لا تستخدم AttachAddon

هذا لأنك لا تستخدم AttachAddon

Ofc أنا لا أستخدم الملحق الملحق ، لأنه مشفر على WebSocket. يجب تغييره إلى دلالات socketIO.

لكن كيف يجيب ذلك على سؤالي؟ استخدام كل من الملحق القياسي للإرفاق ومقبس التوصيل في وقت واحد لا معنى له.

jerch إذا قرأت فسأشرح كيف قمت بتعديل الوحدة النمطية AttachAddon.ts للعمل مع socketIO. لكنني أعتقد أنك تستخدم نهجًا أبسط من خلال تجنب استخدام الملحق الملحق تمامًا.

AhanM آه آسف نسيت رسالتك أعلاه ، وكانت إجابتي موجهة أكثر إلىjzombie.

تبدو التغييرات التي قمت بإجرائها لإرفاق الملحق جيدة بشكل أساسي ، فهناك بعض الأشياء التي لا أعرف ما إذا كانت ستنجح:

  • socket.addEventListener: لست متأكدًا مما إذا كان هذا يعمل مع socketIO ، ولكن نظرًا لأنك تحصل على شيء ما ،
  • لست متأكدًا من سبب اضطرارك للقيام بهذه (data || "") - هل هناك سبب محدد؟ لاحظ أن هذا قد لا يفعل ما كنت تتوقعه مقابل new Uint8Array(data || '') لأنه ليس من المفترض أن يعمل مع بيانات السلسلة.
  • لقد اكتشفت حدث "البيانات" المحدد بنفسي فقط ، ولست متأكدًا مما إذا كانت الأحداث الأخرى ستنجح خارج الصندوق.
  • utf8 الثنائي مقابل السلسلة فقط - لقد اختبرت نقل السلسلة فقط ، ولست متأكدًا مما إذا كانت العناصر الثنائية utf8 تعمل.

بشكل عام ، قد يكون من الأسهل التمسك بنقل السلسلة في الوقت الحالي ، مع # 2362 سيكون أسهل بكثير مع النظام الثنائي (مثل raw utf8). لا تتردد في اختبار العلاقات العامة إذا كنت تريد العبث بالنقل الثنائي.

تحرير: هل ترى أيضًا تلك القمامة الإنهاء الفارغة jzombie المذكورة؟

jerch لقد عثرت للتو على هذا الرمز في أماكن أخرى وواصلت تجميع الأشياء معًا حتى

ثم قمت بتعديله وفقًا لذلك عندما اكتشفت المشكلة.

ها هو الموضوع الأصلي: https://stackoverflow.com/questions/6965107/converting-between-strings-and-arraybuffers/11058858#11058858

على الرغم من أنه تمت كتابته في الأصل لمهمة مختلفة في متناول اليد ، فقد حصلت عليها للعمل في مثيل Docker يعمل على xterm في واجهة المستخدم الرسومية.

ومع ذلك ، قال أحدهم إنه لا يعاني من هذه المشاكل مع جهاز Mac عند تشغيل vi فيه.

بطريقة ما أثرت المشاكل علي فقط في إصدار Linux.

ولم أشاهد \ 0000 مطلقًا حتى قمت بإجراء صدى بنفسي على الخادم ، وأظهر ضغطات المفاتيح القادمة من العميل.

أ \ 0000 ، ب \ 0000 ، ج \ 0000

jzombie آه طيب. الأشياء Uint16Array التي وجدتها هي تحويل سلسلة JS إلى نقاط كود UTF16. لكن هذا لن ينجح هنا ، لأن مآخذ الويب (نعم لا يزال socketIO يستخدم مآخذ ويب تحتها) لا تستخدم / تعرف UTF16 على الإطلاق ، فهي تستخدم UTF8 لبيانات السلسلة ، والبايت الخام لـ Buffer / Uint8Arrays.

يجب أن يتفق الطرفان أيضًا - جزء الخادم وجزء العميل (مرفق ملحق) على ما إذا كانا يرسلان بيانات سلسلة أو بيانات ثنائية (خام utf8 بايت). لذلك مع sebsockets ، لديك خياران أساسيان:

  • نقل سلسلة
// browser send
websocket.send('some string')
// server receive
wsSocket.on('message', data => ...) // data will be a JS string type automatically

// server send
wsSocket.send('some string data')
// browser receive
websocket.onmessage = (data) => ... // data will be a JS string type automatically
  • النقل الثنائي
// browser send
websocket.send(Uint8Array | ArrayBuffer)
// server receive
wsSocket.on('message', data => ...) // data will be a Buffer type

// server send
wsSocket.send(Buffer.from(...))
// browser receive
websocket.binaryType = 'arraybuffer';
websocket.onmessage = (data) => ... // data will be an ArrayBuffer type

مع socketIO ، بقدر ما حصلت عليه ، أصبح الأمر أسهل ، لأنه سيتعامل مع مفاتيح النوع تلقائيًا (لا داعي للعبث بها مسبقًا). لذا تأكد فقط من أنك عندما ترسل نوعًا ثنائيًا ، فأنت مستعد أيضًا لتلقي ثنائي.

أنا أرسله بالفعل ككائن ، مع بيانات أخرى أيضًا.

ولم أشاهد \ 0000 مطلقًا حتى قمت بإجراء صدى بنفسي على الخادم ، وأظهر ضغطات المفاتيح القادمة من العميل. ... أ \ 0000 ، ب \ 0000 ، ج \ 0000

نعم هذا ليس إنهاءًا فارغًا كما اعتقدت ، وحدات بايت الحشو UTF16. ترميز UTF16 هو ترميز 2 بايت وليس الشيء الصحيح هنا.

أنا أرسله بالفعل ككائن ، مع بيانات أخرى أيضًا.

فلماذا التحويل مع str2ab على الإطلاق؟

لأنه لم يكن لدي أدنى فكرة عما كنت أفعله ، وهذا هو الشيء الوحيد الذي جعله يعمل في تلك المرحلة.

استخدم الخيوط فقط؟

إذا قمت فقط بإرسال البيانات كما هي ، فلا يمكنني القيام بأشياء مثل إدخال كلمة مرور أو استخدام nano ، على الأقل إذا كان يعمل بنظام Linux تحته.

كان نانو يكتب عموديًا ، على سبيل المثال ، وكانت روابط المفاتيح معطلة.

إذا قمت فقط بإرسال البيانات كما هي ، فلا يمكنني القيام بأشياء مثل إدخال كلمة مرور أو استخدام nano ، على الأقل إذا كان يعمل بنظام Linux تحته.

هذا يعمل مع التصحيح الخاص بي أعلاه. إذا كنت تواجه مشكلة ، فهناك ارتباط كبير ببعض إعدادات جانب الخادم ، مثل الترميز الخاطئ وما إلى ذلك. كيف يبدو جانب الخادم؟

إنها فوضى ، لكن ها أنت ذا: https://github.com/zenOSmosis/js-shell

تم العثور عليها هنا: https://github.com/zenOSmosis/js-shell/blob/4ad075bed243ec6ea8a83eab7635f684b19a04be/backend/src/api/socket.io/routes/socketChannel/createXTermSocketChannel.js#L28 -L38

لذلك إذا لم تفعل أشياء ab2str / str2ab على كلا الطرفين ، فإنها لا تعمل؟ (فقط ضع الخيط في كلا الطرفين ، ثم أعاد قراءة السلاسل).

تحرير: قم أيضًا بتصحيح نوع ومحتوى أجزاء البيانات هناك. هل تقوم بتغيير المقابس (على كلا الطرفين) ببعض الأعلام الأخرى؟ مثل الثنائي وكذا؟

إذا كانت السلاسل في كلا الطرفين لا تعمل من أجلك ، يمكنك معرفة ما إذا كان يعمل النظام الثنائي ، ولكن ليس مع مصفوفات UTF16. استخدم TextEncoder لإنشاء بيانات UTF-8 جيدة التنسيق على جانب المتصفح مثل هذا:

const te = new TextEncoder();
const byteChunk = te.encoder('string data');
...
socket.emit('data', byteChunk);

يجب أن يرسل هذا بيانات السلسلة كـ utf8 bytes ، والتي يمكنك إطعامها مباشرةً إلى pty:

    socketChannel.on('data', (data) => {
      ptyProcess.write(data);
    });

لكن ofc هذا يعني أن المضيف الذي يقوم بتشغيل pty (والتطبيق التابع) لديه مجموعة محلية UTF-8. الترميزات الأخرى غير مدعومة حاليًا ، مرة أخرى # 2362 سيصلح هذا.

هذه نصيحة جيدة. سأقوم بتجربتها لاحقًا.

لذلك حاولت استخدام برنامج w / TextEncoder ولاحظت أنه كان ينشئ كائنًا ، والذي لم يكن قابلاً للنقل بشكل مباشر إلى pty.

ومع ذلك ، فقد استخدمت ArrayBuffer مع طول سلسلة من سلسلة الإدخال (وليس * 2) ، وقد أدى ذلك إلى حل المشكلة مع الحرف الفارغ.

مرة أخرى ، كما قلت ، ليس لدي أدنى فكرة عما أفعله ، لكن هذا يعمل إذا كنت تستخدم هذه الوظائف على كلا الجانبين ، باستخدام str2ab على البث ، و ab2str على الاستلام.

التمرير مباشرة إلى xterm.write وليس xterm.writeUtf8.

  str2ab(str) {
    const buf = new ArrayBuffer(str.length);
    const bufView = new Uint8Array(buf);
    for (let i = 0, strLen = str.length; i < strLen; i++) {
      bufView[i] = str.charCodeAt(i);
    }
    return buf;
  }

  ab2str(buf) {
    const  str = String.fromCharCode.apply(null, new Uint8Array(buf));
    return str;
  }

jzombie يعمل اختراق المصفوفة المكتوبة UTF-16 عن طريق الصدفة ، لا تستخدم ذلك. "المحول" الجديد الخاص بك هو محول وضع ثنائي ، وسوف يزيل البايت العالي لنقطة كود UTF16 ، وبالتالي لا يعمل أيضًا مع أحرف unicode (يعمل فقط مع ASCII لأن UTF16 يشارك ASCII مع معظم ترميزات 8 بت الأخرى).

أعطيتك مثالاً عن كيفية القيام بذلك باستخدام TextEncoder. يقوم بإنشاء مثيل مشفر ، يحدث تحويل البيانات الحقيقية مع instance.encode(data) ، يجب عليك إصدار القيمة المرجعة لتلك الطريقة. ما نوع الكائن الذي تم إنشاؤه هناك؟

أقترح عليك التوقف تمامًا هنا لأن اتباع نهجك الحالي سيؤدي إلى حدوث أخطاء لاحقًا. أعد كل شيء إلى الأوتار:

  • يجب أن ترسل pty.on ("البيانات") سلاسل
  • يجب أن يرسل socket.emit ("البيانات") سلاسل على كلا الطرفين
  • يجب أن يرسل socket.on ("البيانات") في المتصفح سلاسل

بعد ذلك ، تتبع محتوى القطعة في جزء الخادم وفي المستعرض وانظر أين تختلف. الفكرة الأساسية: ما تم وضعه على اليسار يجب أن يخرج بدون تغيير يمينًا والعكس صحيح. عد بهذه المعلومات ، فقد أتمكن من مساعدتك.

هل هناك فرق بين أنها Uint8Array وليست Unit16Array؟

نعم ، Uint8Array عبارة عن بايت واحد لكل موضع ، وبالتالي يمكن تخزين 0-255 فقط. Uint16Array هو 16 بت ، وبالتالي 2 بايت على مستوى الذاكرة.

بمعنى ، هل Uint8Array هو UTF-16 ، كما تقول؟

لا ، يعتبر UTF-16 مفهومًا يمثل نقاط تشفير Unicode على هيئة 2 بايت متتالي في الذاكرة. المصفوفات المكتوبة هي مجرد عروض على الذاكرة بمستويات وصول مختلفة ، ويمكن لـ Uint8Array الوصول إلى بايت واحد مقروء كـ byte ، ويمكن لـ Uint16Array فقط الوصول إلى كل بايت ثانٍ وقراءتها كـ short :

mem:               |     byte    |    byte   |
utf16:             |        codepoint        |
Uint8Array[idx]:   |      0      |     1     |
Uint16Array[idx]:  |             0           |

كما ترى ، يمكن لكل من المصفوفتين المكتوبتين قراءة UTF16 ، ولكن مع Uint16Array ، يكون الوصول أكثر ملاءمة كنقطة رمز مباشرة لتعيين الوصول إلى فهرس. باستخدام Uint8Array ، سيتعين عليك الحصول على موقعين و SHIFT + إضافتهما لاستعادة نقطة الشفرة الفعلية مرة أخرى.

أعتقد أنك بحاجة إلى فهم أفضل لتمثيل البيانات / مستوى البايت لإنجاز ذلك.

هذا ما يحدث في عملي على الأقل.

لاحظ أن العميل يرسل Uint8Array مع TextEncoder.

const encoder = new TextEncoder();
console.log(encoder.encode('a')); // Uint8Array [97]

على الخادم ، أحصل على هذا:

{ '0': 97 }

ومع ذلك ، من المحتمل أن يكون هذا بسبب جوانب أخرى من تنفيذي ، وقد لا يحصل الآخرون على نفس النتائج.

أوه لول ، هذا { '0': 97 } هو تسلسل JSON لمصفوفة مكتوبة. يبدو أن SocketIO هل هذا؟ إنه يعني في الأساس نفس الشيء - data[0] == 97 ولكن من السيئ جدًا التعامل معه بهذه الطريقة. لديك عدة خيارات للتعامل مع ذلك:

  • تحقق مما إذا كان socketIO يدعم النقل المباشر للصفيف المكتوب على أنه ثنائي (ربما بعض العلم؟) للتخلص من التحويل المطلوب
  • قم بتحويله مرة أخرى إلى Buffer / Uint8 صف نفسك عن طريق التكرار على جميع المؤشرات وكتابتها في Uint8Array جديد (لا يُعاد إضافة perf إلى أسفل)
  • التمسك بالخيوط (الخيار الأسهل هنا ، انظر أعلاه)

مرة أخرى ، عندما أحاول فقط إرسال البيانات ، حرفياً (بالالتصاق بالسلاسل) ، فإن ذلك لا يعمل بالنسبة لي.

هذا على الأرجح بسبب تنفيذي.

هل يمكنك الحصول على البيانات ونشرها هنا مع ضبط كل شيء على السلاسل؟

ستفعل في ثانية ، لكن من فضلك ضع في اعتبارك هذه التفاصيل السخيفة:

أنا أحتوي على كل شيء في كائن قبل الإرسال ، لأكون قادرًا إلى حد ما على تشغيل xterms متعددة عبر نفس الاتصال ، دون تغيير أسماء الأحداث في جميع الحالات.

يتم إنشاء كل كائن مثل {evtName، evtData} ، ويتم إرساله بمعرف حدث مشترك فريد لكل مثيل xterm.

هذا الجانب يعمل ، على الرغم من أن التنفيذ هو اختراق.

حسنًا ، قد يؤدي تضمينه في كائن آخر إلى تحويل JSON. لتمييز المحطات الطرفية ، يمكنك تسجيل المستمع تحت عناوين URL منفصلة تحتوي على بعض المعرفات بدلاً من ذلك. بهذه الطريقة يمكنك إبقاء الأحداث نظيفة من البيانات الأخرى.
يبدو أن هذه مشكلة في socketIO تقوم بأشياء غريبة على البيانات الثنائية داخل الكائنات. يجب أن تعمل السلاسل الثابتة هنا بدون مشاكل حتى جزء الخادم. قد يكون ترميز النظام الأساسي مشكلة بالنسبة إلى pty إذا لم يكن هذا هو UTF8.

"يبدو" أنه يعمل عند مجرد تمرير السلاسل عبر تطبيقي ، وتجاوز ab2str ، و TextEncoder ، وما إلى ذلك.

ومع ذلك ، أحصل على:

خطأ في النطاق غير معلوم: تم تجاوز الحد الأقصى لحجم مكدس الاستدعاءات
في HTMLDivElement.hasOwnProperty ()
في hasBinary (index.js: 50)

أعتقد أن هذه مكتبة داخلية لـ Socket.io إذا لم أكن مخطئًا. أنا لا أستخدمه شخصيًا.

السطر 50 هو "if (Object.prototype.hasOwnProperty.call (obj، key) && hasBinary (obj [key])) {'....

يتم استخدام هذا داخليًا في مكان ما ، ولكن ليس من قبلي بشكل مباشر:

/* global Blob File */

/*
 * Module requirements.
 */
var isArray = require('isarray');

var toString = Object.prototype.toString;
var withNativeBlob = typeof Blob === 'function' || typeof Blob !== 'undefined' && toString.call(Blob) === '[object BlobConstructor]';
var withNativeFile = typeof File === 'function' || typeof File !== 'undefined' && toString.call(File) === '[object FileConstructor]';
/**
 * Module exports.
 */

module.exports = hasBinary;
/**
 * Checks for binary data.
 *
 * Supports Buffer, ArrayBuffer, Blob and File.
 *
 * <strong i="7">@param</strong> {Object} anything
 * <strong i="8">@api</strong> public
 */

function hasBinary(obj) {
  if (!obj || typeof obj !== 'object') {
    return false;
  }

  if (isArray(obj)) {
    for (var i = 0, l = obj.length; i < l; i++) {
      if (hasBinary(obj[i])) {
        return true;
      }
    }

    return false;
  }

  if (typeof Buffer === 'function' && Buffer.isBuffer && Buffer.isBuffer(obj) || typeof ArrayBuffer === 'function' && obj instanceof ArrayBuffer || withNativeBlob && obj instanceof Blob || withNativeFile && obj instanceof File) {
    return true;
  } // see: https://github.com/Automattic/has-binary/pull/4


  if (obj.toJSON && typeof obj.toJSON === 'function' && arguments.length === 1) {
    return hasBinary(obj.toJSON(), true);
  }

  for (var key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key) && hasBinary(obj[key])) {
      return true;
    }
  }

  return false;
}

أنا أقوم بتشغيل واجهة برمجة تطبيقات (API) بالكامل على اتصال المقبس الفردي هذا ، وأردت تجربة تشغيل اتصالات ثنائية الاتجاه أعلى اتصال موجود ، كـ "نفق" (ليس حقًا ، ولكن نوعًا ما).

مجرد تجربة أكثر أو أقل من أي شيء عملي.

مع تحويل str2ab ، لم أواجه أي مشاكل في القيام بذلك.

  • يدعم Buffer و ArrayBuffer و Blob و File.

ربما لا ترسل Uint8Array مباشرة ، لكن صفيفها؟ سيكون ذلك data.buffer .

آه ، نقطة جيدة. ممكن ان يكون.

لذا نعم ، يبدو أن هذا يعمل ، حتى في هذا الكائن الاختراق.

لكن احترس ، في nodejs ، لا يتم محاذاة arraybuffer أحيانًا مع Buffer في الأعلى ، وبالتالي من الأفضل التمسك بـ Buffer نفسه على الجانب pty.

نشكرك على كل مساعدتك في جعل الكود الخاص بي أكثر كفاءة.

يبدو وكأنه يعمل الآن؟

إنها تعمل بالتأكيد.

حلو ، حظًا سعيدًا في مشروعك: smile_cat:

شكر!

تنبعث node-pty كائنًا عند الضغط على مفتاح المسافة ، كما لاحظت للتو ، بدلاً من سلسلة للأحرف الأبجدية الرقمية.

العودة إلى العمل مع أساليب ab2str في الوقت الحالي ووضع هذا في الخلفية حتى أتمكن من معالجة بعض المشكلات الأخرى. لقد تمكنت حرفيًا من العمل مع عدة قذائف طرفية ، في برامج تفاعلية متعددة ، في نفس الوقت مع النهج السابق.

لا يزال هذا مفيدًا جدًا.

على محمل الجد - طريقك str2ab معطل تمامًا ، لا تستخدم ذلك. فقط لأنك تقدمت معها لا يعني أنها الشيء الصحيح للقيام بذلك.
تعتمد مخرجات node-pty على إعداد الترميز ، يتم تعيينها افتراضيًا على "UTF-8" وتعطي سلاسل في pty.on ("البيانات") ، للحصول على وحدات بايت خام تُعرف أيضًا باسم Buffer ، قمت بتعيين {encoding: null} في ctor.

لفهم أعمق لسلاسل JS ، قد يكون هذا مفيدًا: https://mathiasbynens.be/notes/javascript-encoding

شكرا مرة أخرىjerch. أنا أشير إلى هذا باعتباره مشكلة حتى أتمكن من العودة إليها.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات