Java による授業向け画面共有システムの設計と実装

  • 大城 信康 谷成 雄
  • 琉球大学 並列信頼研究室

    目的と背景

  • 大学の講義中、スクリーンに映されている画面は後ろの席程見えずらい。
  • その問題を手元のPCにも写せるようにすることで解決しようと考えた。
  • 60人以上での画面共有を行うことを目標とする。
  • VNCを用いての画面共有

  • 画面を共有する方法 -> VNC
  • VNC: Virtual Network Computing
    ネットワークを介してコンピュータを遠隔操作するプログラム
  • VNCのリモートPCの画面を写す機能を利用する。
  • 通常のVNCの問題点

  • VNC Serverの負荷が重い。
  • Server側の通信網1本への通信負荷が高い。
  • 通常のVNCの問題点

  • 1台と48台でVNCをかけた時のスループットとサーバ側のCPU使用率
  • スループット(単位:Byte) CPU使用率
    1台 20M 15%
    48台 0.4M 100%
  • VNCに使われるCPUの使用率が100%になり、スループットが5分の1まで下がっている。
  • VNCの問題点の解決策

    クライアントを木構造で接続させる

    TreeVNCの利点

    通常のVNC TreeVNC
  • クライアントが増えてもかかる負荷一定。
  • 通信網1本に対する負荷が減り、安定した通信ができる(有線)。
  • TreeVNCの利点

    通常のVNC TreeVNC
    通常のVNC TreeVNC
    最大負荷 N * データ量 (クライアントの数に比例) (M+1) * データ量

    クライアントの数をN、木構造の子供の数をMとする

    TreeVNCの設計

  • TreeVNCのクライアントは最初にTop Proxyに接続を行う。
  • データは木の下へと流れていく。
  • tightVNC ViewerのJava版(ver 1.3)を元にTreeVNCの実装を行う。
  • 発表内容

    RFB protocol

  • Remote Frame Buffer Protocol :
    GUI操作によるリモートアクセス用の通信プロトコル。VNCで用いられる。
  • 転送される画面(フレームバッファ)のデータは変更があった部分(差分)だけが矩形単位で送られる。
  • で囲まれている矩形のデータだけが送られてくる。

    VNC のシーケンス図

  • 1~5まではinitial seaquenceとなる。
  • 6以降は繰り返し行われる処理。画面のデータが転送されてくる。
  • RFB Protocol

  • FramebufferUpdateRequest:
  • 画面に差分が発生したらサーバから教えて貰うためのリクエスト
  • バイト数
    型   [値]
    説明
    1
    U8         3
    message-type
    1
    U8
    incremental
    2
    U16
    x-position
    2
    U16
    y-position
    2
    U16
    width
    2
    U16
    height
  • このリクエストはTop Proxyだけが行う。
  • RFB Protocol

  • FramebufferUpdate: 画面の更新データ
  • バイト数
    型   [値]
    説明
    1
    U8          0
    message-type
    1
    U8
    padding
    2
    U16
    number-of-rectangles
  • 以下number-of-rectanglesの数だけ矩形のデータが続く
  • バイト数
    型   
    説明
    2
    U16
    x-position
    2
    U16
    y-position
    2
    U16
    width
    2
    U16
    height
    4
    U32
    encoding-type
    ...
    ...
    PIXEL DATA

    RFB Protocol

  • 例:FramebufferUpdate
  • x-position 336
    y-position 388
    width 724
    height 449
    encoding-type 16(ZRLE)
    ZRLE ...

    データ転送量

    矩形の大きさと描画に必要なデータ量(単位:Byte)

    矩形の大きさ \ エンコード RAW ZRLE
    724 * 449 1.3M 0.8M
    1920 * 64 0.5M 0.15M
    1920 * 1080 8.2M 3.4M


    RAW、ZRLE、ZRLEEエンコードのデータ量の比較

    データ転送量

  • クライアントが60台の時の通常のVNCと、2分木構成にしたTreeVNCの通信網への負荷について考える。
  • 通常のVNC TreeVNC
    最大負荷 N * データ量(クライアントの数に比例) (M+1) * データ量

    クライアントの数をN、木構造の子供の数をMとする

  • N = 60、 M = 1 となる。
  • 724 * 449 の画面分のデータ(0.8M)を送信するとする。
  • データ転送量

  • 通常のVNC TreeVNC
    最大負荷 48M 2.4M

    通常のVNC TreeVNC

    クライアント:60台 TreeVNCは2分木でTreeを構成

    エンコード

  • MacintoshでVNCを行うとZRLEを使うことができる。
  • データ量がRAWデータの約4分の1のデータ量ですむ。
  • TreeVNCではこのZRLEを扱っている。
  • ZRLE

  • ZRLE : Zlib Run-Length Encoding
  • 最初の4バイトにはZlibのデータの長さが、続いてZlibのデータが送られてくる。
  • バイト数
    型 
    説明
    4 U32 length
    length U8 array zlibData
  • Zlibデータ
  • 辞書がなければデータを正しく解凍できない
  • ZRLEの問題

  • 辞書はZlibデータの最初に送られてくる。
  • ZRLEのデータを最初から送ることができれば、辞書も送ることができる。
  • データの途中から送ると辞書は送られず、正しく解凍を行うことができない。
  • ZRLEE

  • そこで、Top ProxyにZRLEのデータを再度圧縮し直すことで辞書を付けてもらうことにした。以下はその部分のソースである。
  • Deflater nDeflater = deflater; // new Deflater();
    LinkedList out = new LinkedList();
    unzip(inflater, inputs, 0 , out, INFLATE_BUFSIZE);
    // dump32(inputs);
    int len2 = zip(nDeflater, out, 0, bufs);
    
  • 一度再圧縮してしまえば後はどこからデータを流しても問題ない。
  • ZRLEE

  • クライアント側は毎回新しいZRLEのストリームを使うようにする。
  • 	    if (rfb.updateRectEncoding==RfbProto.EncodingZRLEE) 
       	       zrleInStream = null;
    	    if (zrleInStream == null)
    	       zrleInStream = new ZlibInStream();
    	  
  • JavaではZlibの辞書の取り出しが実装されていなかった為、このような方法をとることになった。
  • MulticastQueue

  • MulticastQueueの図を入れる。
  • 接続されてきた時点からデータの送信が始まる。データは読み込まれるまでメモリ上に残っている。

    MulticastQueue

  • MulticastQueueからデータを取り出し子供に送っている部分のソース
  • LinkedList bufs = c.poll();
    int inputIndex = 0;
    ByteBuffer header = bufs.get(inputIndex);
    if (header==null) continue;
    writeToClient(os, bufs, inputIndex);
    
  • c.poll(inputIndex) 内部では次の処理を行っている。
  • latch.await();
    return next;
    
  • 次のデータが用意できるまでawait()で待つのである。
  • MulticastQueue

  • データは作られるとMulticastQueueにputされる。
  • put内部ではつぎの処理が行われる。

    Node next = new Node(item);
    tail.set(next);
    tail = next;
    	  

    set()内部ではlatchがcountDown()され、await()で止まっていたスレッドが動き出す。

    MulticastQueue

  • MulticastQueueは次の次のデータへの参照を順序良く行うためのクラスである。
  • MulticastQueueはjava.util.CountDownLatchを用いて実装されたクラスである。
  • クライアントから接続されると、データ転送用のスレッド(sender)が走る。
  • このスレッドは次に流すデータが来るまでは待機して置かなければならない。そして流すべきデータがくるとまた動き始めなければならない。
  • このスレッドの待機・解放を行うのがMulticastQueueとなる。
  • MulticastQueueの問題点

  • Clientがデータを読み込まないとデータが溜まりメモリを圧迫してしまう。
  • MulticastQueueの問題点

  • TimeOut(TO)スレッドを走らせ、一定の時間データを読み込まなければ代わりにこのTOが読み込むようにする。

  • 解決策

    テスト環境の構築

  • CUI版のVNCクライアントを作成
  • 48台あるクラスタでCUI版のクライアントをはしらせてVNCをかけさせる。
  • 最初の1台目と48台めをGUI版のクライアントで接続を行い見比べてみる。
  • TreeVNCの発端

  • 大学のB3でうける授業の1つ、programming4で作り始めたことがきっかけ。
  • programming4は作りたいものを提案して作る授業。
  • 授業が終わっても改良を加えていた。
  • B4になりこの場で発表してみることになった。