ホームページ ウェブフロントエンド jsチュートリアル node.jsは複数ユーザー向けのWeb端末操作を実装します

node.jsは複数ユーザー向けのWeb端末操作を実装します

Apr 14, 2018 pm 01:35 PM
javascript node.js web

今回はマルチユーザーのWebターミナル操作を実現するためのnode.jsを紹介します。node.jsを使用してマルチユーザーのWebターミナル操作を実現するための注意事項は何ですか?以下は実際的なケースです。 。

ターミナル (コマンドライン) は、ローカル IDE の共通機能として、git 操作とプロジェクトのファイル操作を非常に強力にサポートします。 WebIDE では、Web 疑似端末が存在しない場合、カプセル化されたコマンド ライン インターフェイスを提供するだけでは開発者を完全に満足させることができません。そのため、より良いユーザー エクスペリエンスを提供するために、Web 疑似端末の開発が急務となっています。議題。

リサーチ

私たちの理解では、ターミナルはコマンド ライン ツールに似ており、シェルを実行できるプロセスです。コマンド ラインに一連のコマンドを入力して Enter キーを押すたびに、端末プロセスは子プロセスをフォークして、入力されたコマンドを実行します。端末プロセスは、システム コール wait4() を通じて子プロセスの終了を監視し、出力します。公開された子プロセスの実行情報を通じて。

Web 側でローカリゼーションと同様のターミナル機能を実装する場合、ネットワークの遅延と信頼性の保証、ローカリゼーションに可能な限り近いシェル ユーザー エクスペリエンス、Web ターミナル UI の幅と高さ、出力情報の適応、セキュリティ アクセスなど、さらに多くの作業が必要になる場合があります。制御や権限管理などWeb ターミナルを具体的に実装する前に、シェルの機能実装、ユーザー エクスペリエンス、およびセキュリティ (Web ターミナルはオンライン サーバーで提供される機能) のどれが最もコアであるかを評価する必要があります。 、そのため、セキュリティが保証されている必要があります。この2つの機能を確保することを前提として初めて、Web疑似端末は正式にスタートすることができます。

まず、これら 2 つの関数の技術的な実装について考えてみましょう (サーバー側のテクノロジは、nodejs を使用します):

ノード ネイティブ モジュールは、対話型入力の実装と出力の実行に使用できる repl モジュールを提供し、タブ補完、カスタマイズされた出力スタイル、その他の機能も提供します。ただし、ノード関連のコマンドのみを実行できるため、何を実現することはできません。システムシェルの目的を実行します。 ノードのネイティブ モジュール child_porcess は、基盤となる libuv をカプセル化する uv_spawn 関数である spawn を提供します。基盤となる実行システムは、fork および execvp を呼び出してシェル コマンドを実行します。ただし、タブのオートコンプリート、履歴コマンドを表示する矢印キーなど、疑似ターミナルの他の機能は提供されません。 したがって、ノードのネイティブモジュールを使用して擬似端末をサーバ側で実装することは不可能であり、擬似端末の原理とノード側の実装方向を引き続き検討する必要がある。

疑似端末

擬似端末は実際の端末ではなく、カーネルが提供する「サービス」です。ターミナル サービスは通常、次の 3 つの層で構成されます:

キャラクターデバイス、中間レベルのライン規律、および最下位レベルのハードウェアドライバーに提供されるトップレベルの入出力インターフェイス

その中で、トップレベルのインターフェイスは (読み取り、書き込み) などのシステム コール関数を通じて実装されることが多く、基礎となるハードウェア ドライバーはカーネルによって提供される擬似端末のマスター/スレーブ デバイス通信を担当します。行規律は比較的抽象的に見えますが、実際、機能的に言​​えば、入力プロセス中の割り込み文字 (ctrl) の処理など、入出力情報の「処理」を担当します。 + c) およびいくつかのバックスペース文字 (バックスペースと削除) などを使用し、出力改行文字 n を rn などに変換します。

擬似端末はマスター デバイスとスレーブ デバイスの 2 つの部分に分かれており、これらはデフォルトの回線規則を実装する双方向パイプ (ハードウェア ドライバー) を介して接続されます。擬似端末マスターからの入力はすべてスレーブに反映され、その逆も同様です。スレーブデバイスの出力情報もパイプを介してマスターデバイスに送信され、擬似端末のスレーブデバイスでシェルが実行され、端末機能が完成する。

擬似端末のスレーブデバイスは、端末のタブ補完やその他のシェル特殊コマンドを実際にシミュレートできます。したがって、ノードネイティブモジュールがニーズを満たすことができないという前提の下で、最下層に焦点を当て、OSがどのような機能を提供するかを確認する必要があります。 。現在、glibc ライブラリは posix_openpt インターフェイスを提供していますが、そのプロセスは少し面倒です:

posix_openpt を使用して、擬似端末マスター デバイスを開きます。 スレーブ デバイスの権限を設定するには、grantpt を使用します。 対応するスレーブ デバイスのロックを解除し、スレーブ デバイス名 (/dev/pts/123 と同様) を取得するには、unlockpt を使用します。 マスター (スレーブ) デバイスは、書き込みと操作の実行

したがって、より優れたカプセル化を備えた pty ライブラリが登場しました。これは、forkpty 関数だけで上記のすべての機能を実現できます。ノード C++ 拡張モジュールを作成し、pty ライブラリを使用して、疑似ターミナル内のデバイスからコマンド ラインを実行するターミナルを実装します。

疑似端末セキュリティの問題については、記事の最後で説明します。

疑似端末の実装アイデア

擬似端末のマスター/スレーブデバイスの特性に応じて、擬似端末のライフサイクルとそのリソースをマスターデバイスが配置されている親プロセスで管理し、子プロセスでシェルを実行します。スレーブデバイスが配置されている場所 実行プロセス中の情報と結果がメインデバイスに送信され、メインデバイスが配置されているプロセスが stdout を外部に提供します。

ここで pty.js の実装アイデアを学びましょう:

pid_t pid = pty_forkpty(&master, name, NULL, &winp);
 switch (pid) {
 case -1:
  return Nan::ThrowError("forkpty(3) failed.");
 case 0:
  if (strlen(cwd)) chdir(cwd);
  if (uid != -1 && gid != -1) {
  if (setgid(gid) == -1) {
   perror("setgid(2) failed.");
   _exit(1);
  }
  if (setuid(uid) == -1) {
   perror("setuid(2) failed.");
   _exit(1);
  }
  }
  pty_execvpe(argv[0], argv, env);
  perror("execvp(3) failed.");
  _exit(1);
 default:
  if (pty_nonblock(master) == -1) {
  return Nan::ThrowError("Could not set master fd to nonblocking.");
  }
  Local<Object> obj = Nan::New<Object>();
  Nan::Set(obj,
  Nan::New<String>("fd").ToLocalChecked(),
  Nan::New<Number>(master));
  Nan::Set(obj,
  Nan::New<String>("pid").ToLocalChecked(),
  Nan::New<Number>(pid));
  Nan::Set(obj,
  Nan::New<String>("pty").ToLocalChecked(),
  Nan::New<String>(name).ToLocalChecked());
  pty_baton *baton = new pty_baton();
  baton->exit_code = 0;
  baton->signal_code = 0;
  baton->cb.Reset(Local<Function>::Cast(info[8]));
  baton->pid = pid;
  baton->async.data = baton;
  uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid);
  uv_thread_create(&baton->tid, pty_waitpid, static_cast<void*>(baton));
  return info.GetReturnValue().Set(obj);
 }
ログイン後にコピー

まず、pty_forkpty (sunOS と互換性のある forkpty の posix 実装) を通じて、 Unix など) マスター/スレーブデバイスを作成し、子プロセスに権限を設定 (setuid、setgid) した後、システムコール pty_execvpe (execvpe のカプセル化) を実行すると、マスターデバイスの入力情報が取得されます。ここで実行されます (子プロセスによって実行されるファイル。sh の場合、stdin がリッスンされます); 親プロセスは、メインデバイスの fd (双方向データ送信用の net.Socket オブジェクトを作成できる) などの関連オブジェクトをノード層に公開し、同時に libuv のメッセージ キューを登録します。子プロセスの終了時にトリガーされる async -> async メッセージ、pty_after_waitpid 関数を実行します。 最後に、親プロセスは、uv_thread_create を呼び出して前の子プロセスの終了メッセージをリッスンすることによって子プロセスを作成します (システム コール wait4 を実行して、特定の pid をリッスンするプロセスをブロックします。終了情報は 3 番目のパラメーターに格納されます) )、pty_waitpid 関数 wait4 関数はカプセル化されており、関数の最後で uv_async_send(&baton->async) が実行されてメッセージがトリガーされます。

最下層で pty モデルを実装した後、いくつかの stdio 操作をノード層で実行する必要があります。親プロセス内でシステムコールを実行することで擬似端末本体デバイスが作成され、本体デバイスのファイルディスクリプタがfdを通じてノード層に公開されるため、擬似端末の入出力も読み込まれ、 fd に従って書き込み、PIPE、FILE などの対応するファイル タイプを作成して完了します。実際、OS レベルでは、擬似端末マスター デバイスは双方向通信を行う PIPE とみなされます。 net.Socket(fd) を介してノード層にソケットを作成し、データ フローの双方向 IO を実装します。これにより、擬似端末のスレーブ デバイスもマスター デバイスと同じ入力を持ち、対応するコマンドがサブデバイスで実行されます。サブプロセスの出力もPIPEを介してメインデバイスに反映され、ノード層のSocketオブジェクトのデータイベントをトリガーします。

ここでの親プロセス、マスターデバイス、子プロセス、スレーブデバイスの入出力の説明が少しわかりにくいので、ここで説明します。親プロセスとメインデバイスの関係は、親プロセスがシステムコール(PIPEとみなすことができる)によりメインデバイスを作成し、メインデバイスのfdを取得する。親プロセスは fd の接続を作成します ソケットは子プロセス(スレーブデバイス)への入出力を実装します。 子プロセスが forkpty を渡す 作成後、login_tty 操作が実行されて子プロセスの stdin、stderr、stderr がリセットされ、すべてがスレーブ デバイス (PIPE のもう一方の端) の fd にコピーされます。したがって、子プロセスの入力と出力はすべてスレーブデバイスの fd に関連付けられ、子プロセスの出力データは PIPE を経由し、親プロセスのコマンドは PIPE から読み込まれます。詳細については、リファレンス forkpty 実装を参照してください

また、ptyライブラリは疑似端末のサイズ設定を提供しており、疑似端末の出力情報のレイアウト情報をパラメータで調整できるため、Web上でコマンドラインの幅と高さを調整する機能も提供します側では、pty レイヤーに擬似端末を設定するだけです。文字単位で測定されるウィンドウ サイズを設定するだけです。

Web端末のセキュリティを保証

glibc が提供する pty ライブラリに基づいて擬似端末バックエンドを実装する場合、セキュリティの保証はありません。 Web ターミナルを介してサーバー上のディレクトリを直接操作したいと考えていますが、擬似ターミナルのバックグラウンドを介して root 権限を直接取得できますが、これはサーバーのセキュリティに直接影響するため、実装する必要があります。ユーザーが同時にオンラインであり、各ユーザーのアクセス権を設定でき、特定のディレクトリにアクセスでき、オプションで bash コマンドを設定でき、ユーザーは互いに隔離され、ユーザーは現在の状態を認識しない「システム」です。この環境はシンプルで簡単に導入できます。 最も適切なテクノロジーの選択は docker です。カーネル レベルの分離として、ハードウェア リソースを最大限に活用でき、ホストの

関連ファイル

をマッピングするのに非常に便利です。しかし、docker は万能ではありません。プログラムが docker コンテナーで実行される場合、各ユーザーへのコンテナーの割り当てはさらに複雑になり、運用保守担当者の制御下になくなります。これがいわゆる DooD (港湾労働者 Docker の外) -- ボリューム経由 「/usr/local/bin/docker」などのバイナリ ファイルの場合は、ホストの docker コマンドを使用して兄弟イメージを開いてビルド サービスを実行します。 docker-in-docker モードの使用には、業界で、特に

ファイル システム

レベルでよく議論される多くの欠点があり、それについては参考資料を参照してください。したがって、Docker テクノロジーは、コンテナー内で既に実行されているサービスのユーザー アクセスのセキュリティ問題を解決するのには適していません。

次に、単一マシン上でのソリューションを検討する必要があります。現時点では、作者は 2 つの選択肢しか考えていません:

コマンド ACL は、ホワイトリスト制限付き bash chroot コマンドによって実装され、ユーザーごとにシステム ユーザーを作成し、ユーザーのアクセス範囲を制限します

まず第一に、コマンド ホワイトリスト方式は廃止されるべきです。第一に、Linux の異なるリリースの bash が同じであるという保証はありません。第二に、タブ コマンド補完機能のせいで、すべてのコマンドを効果的に使い果たすことができません。および 擬似端末によって提供される特殊文字 delete が存在する場合、現在入力されているコマンドと効果的に一致することはできません。したがって、ホワイトリスト方式には抜け穴が多すぎるため、放棄する必要があります。

/bin/bash -r によってトリガーされる制限付き bash は、ユーザーが明示的に「cd ディレクトリ」にアクセスできないように制限できますが、多くの欠点があります:

完全に信頼できないソフトウェアの実行を許可するには不十分です。シェル スクリプトであることが判明したコマンドが実行されると、rbash はスクリプトを実行するためにシェル内に作成された制限をオフにします。ユーザーが bash を実行するか、rbash からダッシュを実行すると、無制限のシェルが取得されます。制限された bash から抜け出す方法はたくさんあります シェルは予測が容易ではありません。

結局、解決策は chroot しかないようです。 chroot はユーザーのルート ディレクトリを変更し、指定されたルート ディレクトリでコマンドを実行します。指定されたルート ディレクトリから飛び出すことはできないため、元のシステムのすべてのディレクトリに同時にアクセスすることはできません。chroot は元のシステムから分離されたシステム ディレクトリ構造を作成するため、元のシステムのさまざまなコマンドは使用できません。 「新しいシステム」は新しくて空であるため、最後に、複数のユーザーが使用する場合に分離され、透過的になるため、私たちのニーズを完全に満たします。

そのため、最終的に Web 端末のセキュリティ ソリューションとして chroot を選択しました。ただし、chroot を使用するには、新しいユーザーの作成だけでなくコマンドの初期化など、多くの追加の処理が必要になります。 「新しいシステム」は空であり、「ls、pmd」などの実行可能なバイナリ ファイルがないことも上で述べました。そのため、「新しいシステム」を初期化する必要があります。ただし、多くのバイナリ ファイルは多くのライブラリに静的にリンクされているだけでなく、実行時にダイナミック リンク ライブラリ (dll) に依存するため、各コマンドが依存する多くの dll を見つける必要もあり、非常に面倒です。ユーザーがこの退屈なプロセスから解放されるようにするために、jailkit が登場しました。

ジェイルキット、とても便利です

Jailkit は、その名前が示すように、ユーザーを刑務所に入れるために使用されます。 jailkit は、内部で chroot を使用してユーザーのルート ディレクトリを作成し、バイナリ ファイルとそのすべての DLL を初期化してコピーするための一連の手順を提供します。これらの機能は、設定ファイル を通じて操作できます。したがって、実際の開発では、jailkit を初期化シェル スクリプトとともに使用して、ファイル システムの分離を実現します。

ここでの初期化シェルとは、前処理スクリプトを指します。chroot は各ユーザーのルート ディレクトリを設定する必要があるため、コマンド ライン権限を持つ各ユーザーに対して対応するユーザーがシェルに作成され、基本ユーザーはjailkit 設定ファイルを通じてコピーされます。バイナリ ファイルとその DLL (基本的なシェル命令、git、vim、ruby など)。最後に、特定のコマンドに対して追加の処理が実行され、アクセス許可がリセットされます。

「新しいシステム」と元のシステムの間のファイル マッピングを処理するプロセスでは、依然としていくつかのスキルが必要です。以前、作者は chroot で設定したユーザーのルート ディレクトリ以外のディレクトリをソフト リンクの形式でマッピングしていましたが、jail 内のソフト リンクにアクセスすると、やはりエラーが報告され、ファイルが見つかりませんでした。 chroot の特性として、ルート ディレクトリの外部にあるファイル システムにアクセスする権限はありません。ハード リンクを通じてマッピングが確立されている場合、chroot によって設定されたユーザー ルート ディレクトリ内のハード リンク ファイルを変更することは可能ですが、削除を伴う操作は行われません。作成などが正しく実行できず、元のシステムのディレクトリにマッピングされ、ハード リンクがそのディレクトリに接続できないため、ハード リンクは最終的にマウントを通じて要件を満たしません。 --bind 実装 (mount --bind /home/ttt/abc など) /usr/local/abc マウントされたディレクトリ(/usr/local/abc)のディレクトリ情報(ブロック)をシールドし、マウントされたディレクトリとマウントされたディレクトリとのマッピング関係をメモリ上に維持します/usr/ local/へのアクセス。 abc は、メモリ マッピング テーブルを通じて /home/ttt/abc のブロックをクエリし、ディレクトリ マッピングを実現するための操作を実行します。

最後に、「新しいシステム」を初期化した後、擬似ターミナルを介してjail関連のコマンドを実行する必要があります:

sudo jk_chrootlaunch -j /usr/local/jailuser/${creator} -u ${creator} -x /bin/bashr

bash プログラムを開いた後、PIPE 経由でメイン デバイスが受信した Web ターミナル入力 (WebSocket 経由) と通信できます。

この記事の事例を読んだ後は、この方法を習得したと思います。さらに興味深い情報については、php 中国語 Web サイトの他の関連記事に注目してください。

推奨読書:

angularjsでechartチャートを実装する方法

jsでインターレースカラー変更を実現する方法

以上がnode.jsは複数ユーザー向けのWeb端末操作を実装しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法 WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法 Dec 17, 2023 pm 02:54 PM

WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法 はじめに: 技術の継続的な発展により、音声認識技術は人工知能の分野の重要な部分になりました。 WebSocket と JavaScript をベースとしたオンライン音声認識システムは、低遅延、リアルタイム、クロスプラットフォームという特徴があり、広く使用されるソリューションとなっています。この記事では、WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法を紹介します。

WebSocket と JavaScript: リアルタイム監視システムを実装するための主要テクノロジー WebSocket と JavaScript: リアルタイム監視システムを実装するための主要テクノロジー Dec 17, 2023 pm 05:30 PM

WebSocketとJavaScript:リアルタイム監視システムを実現するためのキーテクノロジー はじめに: インターネット技術の急速な発展に伴い、リアルタイム監視システムは様々な分野で広く利用されています。リアルタイム監視を実現するための重要なテクノロジーの 1 つは、WebSocket と JavaScript の組み合わせです。この記事では、リアルタイム監視システムにおける WebSocket と JavaScript のアプリケーションを紹介し、コード例を示し、その実装原理を詳しく説明します。 1.WebSocketテクノロジー

JavaScript と WebSocket を使用してリアルタイムのオンライン注文システムを実装する方法 JavaScript と WebSocket を使用してリアルタイムのオンライン注文システムを実装する方法 Dec 17, 2023 pm 12:09 PM

JavaScript と WebSocket を使用してリアルタイム オンライン注文システムを実装する方法の紹介: インターネットの普及とテクノロジーの進歩に伴い、ますます多くのレストランがオンライン注文サービスを提供し始めています。リアルタイムのオンライン注文システムを実装するには、JavaScript と WebSocket テクノロジを使用できます。 WebSocket は、TCP プロトコルをベースとした全二重通信プロトコルで、クライアントとサーバー間のリアルタイム双方向通信を実現します。リアルタイムオンラインオーダーシステムにおいて、ユーザーが料理を選択して注文するとき

WebSocketとJavaScriptを使ったオンライン予約システムの実装方法 WebSocketとJavaScriptを使ったオンライン予約システムの実装方法 Dec 17, 2023 am 09:39 AM

WebSocket と JavaScript を使用してオンライン予約システムを実装する方法 今日のデジタル時代では、ますます多くの企業やサービスがオンライン予約機能を提供する必要があります。効率的かつリアルタイムのオンライン予約システムを実装することが重要です。この記事では、WebSocket と JavaScript を使用してオンライン予約システムを実装する方法と、具体的なコード例を紹介します。 1. WebSocket とは何ですか? WebSocket は、単一の TCP 接続における全二重方式です。

JavaScript と WebSocket: 効率的なリアルタイム天気予報システムの構築 JavaScript と WebSocket: 効率的なリアルタイム天気予報システムの構築 Dec 17, 2023 pm 05:13 PM

JavaScript と WebSocket: 効率的なリアルタイム天気予報システムの構築 はじめに: 今日、天気予報の精度は日常生活と意思決定にとって非常に重要です。テクノロジーの発展に伴い、リアルタイムで気象データを取得することで、より正確で信頼性の高い天気予報を提供できるようになりました。この記事では、JavaScript と WebSocket テクノロジを使用して効率的なリアルタイム天気予報システムを構築する方法を学びます。この記事では、具体的なコード例を通じて実装プロセスを説明します。私たちは

コックピット Web UI から管理アクセスを有効にする方法 コックピット Web UI から管理アクセスを有効にする方法 Mar 20, 2024 pm 06:56 PM

Cockpit は、Linux サーバー用の Web ベースのグラフィカル インターフェイスです。これは主に、初心者/熟練ユーザーにとって Linux サーバーの管理を容易にすることを目的としています。この記事では、Cockpit アクセス モードと、CockpitWebUI から Cockpit への管理アクセスを切り替える方法について説明します。コンテンツ トピック: コックピット エントリ モード 現在のコックピット アクセス モードの確認 CockpitWebUI からコックピットへの管理アクセスを有効にする CockpitWebUI からコックピットへの管理アクセスを無効にする まとめ コックピット エントリ モード コックピットには 2 つのアクセス モードがあります。 制限付きアクセス: これは、コックピット アクセス モードのデフォルトです。このアクセス モードでは、コックピットから Web ユーザーにアクセスできません。

簡単な JavaScript チュートリアル: HTTP ステータス コードを取得する方法 簡単な JavaScript チュートリアル: HTTP ステータス コードを取得する方法 Jan 05, 2024 pm 06:08 PM

JavaScript チュートリアル: HTTP ステータス コードを取得する方法、特定のコード例が必要です 序文: Web 開発では、サーバーとのデータ対話が頻繁に発生します。サーバーと通信するとき、多くの場合、返された HTTP ステータス コードを取得して操作が成功したかどうかを判断し、さまざまなステータス コードに基づいて対応する処理を実行する必要があります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法を説明し、いくつかの実用的なコード例を示します。 XMLHttpRequestの使用

ウェブってどういう意味ですか ウェブってどういう意味ですか Jan 09, 2024 pm 04:50 PM

Web は、ワールド ワイド ウェブとしても知られるグローバル ワイド エリア ネットワークであり、インターネットのアプリケーション形式です。 Web はハイパーテキストとハイパーメディアに基づいた情報システムであり、ユーザーはハイパーリンクを通じて異なる Web ページ間を移動することにより、情報を閲覧したり入手したりすることができます。 Web の基礎はインターネットであり、統一および標準化されたプロトコルと言語を使用して、異なるコンピューター間でのデータ交換と情報共有を可能にします。

See all articles