view paper/chapter4.tex @ 26:aec4085dd5db

update
author Taninari YU <you@cr.ie.u-ryukyu.ac.jp>
date Tue, 04 Feb 2014 03:50:01 +0900
parents 2d6118b66367
children 7149e38f717c
line wrap: on
line source

\chapter{画面共有システムTreeVNCの実装}

\section{TightVNCのアップデートへの対応}
TightVNCは現在も開発が続いていて、アップデートされている。このアップデートに対応するために、私が作成しているTreeVNCにも対応させる必要がある。
卒業論文後から私は2種類のアップデートを行った。その2種類のアップデートの対応について説明する。
はじめに行ったアップデートはVersionが1.3.10からVersion2.5.0へ変わる大型アップデートである。
このアップデートでは、パッケージ構成が追加され、元のソースコードがほとんど残っていない状態であった。このような大型なアップデートに対応するには、新しいTightVNCを元にして、作成したTreeVNCの機能を一つずつ移行していく必要がある。
アップデートに加えソースコードの質を高めるためにリファクタリングを行った。
リファクタリングとは、将来の仕様変更に柔軟に対応できるようにソースコードの手直しを行うことである。
次に行ったアップデートはVersion2.5.0からVersion2.7.2へ変わるアップデートである。
このアップデートは、パッケージ構成も変わらず、変更が少量であったので、Mercuriaのmerge機能を用いて手軽にアップデートすることができた。


\section{UIの実装}
\subsection{FramebufferUpdateの概要}
RFBプロトコルでは、FramebufferUpdateによって、矩形状の画像データが送信されてくる。\\
FrameBufferUpdateの概要を(表\ref{tb:framebufferupdate},表\ref{tb:framebufferupdate2})に示す。

\begin{table}[htbp]
\caption{FramebufferUpdate}
\label{tb:framebufferupdate}
\begin{center}
  \begin{tabular}{|c|c|c|} \hline
    バイト数& 型 & 説明  \\ \hline
    1 & U8 & message-type \\ \hline
    1 & U8 & padding\\ \hline
    2 & U16 & number-of-rectangles \\ \hline
  \end{tabular}
\end{center}
\end{table}

この後にnumber-of-rectanglesの数だけ矩形のピクセルデータが続く。ピクセルデータを(表\ref{tb:framebufferupdate2})に示す。

\begin{table}[htbp]
\caption{FramebufferUpdate}
\label{tb:framebufferupdate2}
\begin{center}
  \begin{tabular}{|c|c|c|} \hline
    バイト数& 型 & 説明  \\ \hline
    2 & U16 & x-position \\ \hline
    2 & U16 & y-position \\ \hline
    2 & U16 & width  \\ \hline
    2 & U16 & height \\ \hline
    4 & S32 & encoding-type \\ \hline
  \end{tabular}
\end{center}
\end{table}

ここまでがheaderとして送信されるデータである。矩形の画像なのでx-position、y-position、width、heightの4つの値で画像の位置と大きさを決めることができる。\\
headerに続いて、実際の画像データが送信されてくる。\\
画像データはZRLEエンコーディングで送信される。最初の4バイトはデータの長さを表現して、次にその大きさ分のzlibDataが送信される(表\ref{tb:ZRLE})。

\begin{table}[htbp]
\caption{ZRLEデータ}
\label{tb:ZRLE}
\begin{center}
  \begin{tabular}{|c|c|c|} \hline
    バイト数& 型 & 説明  \\ \hline
    4 & U32 & length \\ \hline
    length & U8 array & zlibData \\ \hline
  \end{tabular}
\end{center}
\end{table}

\subsection{マルチディスプレイへの対応}
送られてきたzlibDataは展開されると左から右、上から下へ並んだ、64*64ピクセルのタイル群画像データとなる。
ここで、画像データがどのように送られてくるのかを調べてみたところ、2つディスプレイがあるとすると、両ディスプレイにまたがった画像更新が来ることがないことがわかった。\\
図\ref{fig:rawdata}の黒い部分が画像データだとすると、図\ref{fig:rawdata}のようなFramebufferUpdateは送られてくることはない。

\begin{figure}[!htbp]
\begin{center}
\includegraphics[width=110mm]{./images/sendscreenimage.pdf}
\end{center}
\caption{画面更新時に来る可能性のないUpdateRectangle}
\label{fig:sendscreenimage}
\end{figure}
VNCでは、複数のディスプレイが存在する場合、すべてのディスプレイの情報が送られてくる。しかし、発表などに使用するディスプレイは一つである場合が多い。\\
必要な画面が一つのディスプレイなのに、すべてのディスプレイのデータを送ると無駄なデータを送信していることになる。そこで、ディスプレイを指定して、その画像だけ送信する機能を追加することで、無駄なデータ送信を省くことができる。



以上のことを踏まえ、FramebufferUpdateで送信されてきたheaderを確認し、x-positionを確認することで、どの画面の画像データを送信するかを選択することができる。\\
例えば、図\ref{fig:rawdata}では、左側の画面を送信したいときは、x-positionが1920より小さい場合送信し、右側を送信したい場合は1920以上のデータを送信するようにフィルタリングすることで実現できる。

\newpage 

\subsection{表示画面の切り替え}
ゼミなど発表者が多数いる状況でVNCを使用すると、発表者が切り替わるごとにサーバを立ち上げなおさなければならない。\\
画面の切替手順を図\ref{fig:changevncserver}に示す。

\begin{figure}[!htbp]
\begin{center}
\includegraphics[width=150mm]{./images/changevncserver.pdf}
\end{center}
\caption{画面切り替えの流れ}
\label{fig:changevncserver}
\end{figure}

初めに、Root Nodeに対して画面を切り替える命令(1:changeVNCServer("10.3"))を出す。命令を受け取ったRoot Nodeは引数で受けっとた
IPのコンピュータに対して、接続要求を出す(2:requestVNC())。要求を受け取ったコンピュータはが認証を承諾するとRoot Nodeに対して、接続要求を承諾したことを通知する(3:acceptConnection())。Root Nodeは元から通信していたネットワーク接続を閉じる命令を出す(4:CloseConnection())。最後に繋がっているNodeに新しい画面に切り替わったことを通知する(5:newServer())。

newServer()の内部処理ををListing\ref{src:changescreen}に示す。これは、Root Nodeが子供に対して、画面の切り替えが起こったことを知らせるソースコードである。\\
clientListは、現在接続されているクライアント情報が入っている。クライアントにそれぞれTCP接続を行い、サーバが変わったので接続し直させる命令を送信する。

\begin{lstlisting}[language=java,frame=lrbt,label=src:changescreen,caption=画面が切り替わったことを知らせるプログラム,numbers=left]
  for (String client : clientList) {
      Socket echoSocket = new Socket(client, 10001);
      DataOutputStream os = new DataOutputStream(echoSocket.getOutputStream());
      os.writeBytes("reconnection\n");
      os.close();
  }
\end{lstlisting}


\section{Authentication}
Root Nodeがサーバに対してVNC接続を行う際、ハンドシェイクが必要となる。
ハンドシェイクの手順として、始めにRoot Nodeがサーバに接続を行うと、
サーバがサポートする最新のプロトコルバージョンが送られてくる。
Root Nodeはサーバから送られてきたプロトコルバージョン以下の使用できるバージョンを
サーバに対し送る。現時点で公開されているプロトコルバージョンは3.3、3.7、3.8だけである。
今回TreeVNCは3.855というバージョンを用意して3.855が来るとTreeVNCを使用するようにした。

プロトコルバージョンが決定すると、サーバ及びNodeは、
その接続で使用されるセキュリティに合意しなければならない。
バージョン3.7以降ではサーバは自身のサポートするセキュリティタイプの一覧を提示する。
Nodeのサポートする有効なセキュリティタイプを少なくとも一つサーバが提示した場合、Nodeはその接続上で使用されるセキュリティタイプを表す単一バイトを送り返す。

登録されているセキュリティタイプの一例として(表 \ref{tb:authtype})のようなものがある。

\begin{table}[htbp]
\caption{AuthType}
\label{tb:authtype}
\begin{center}
\begin{tabular} {|l|l|}
  \hline
  {\bf 値}&名称\\
  \hline
  {\bf 0}&Invalid\\
  \hline
  {\bf 2}&None\\
  \hline
  {\bf 5}&RA2\\
  \hline
  {\bf 18}&TLS\\
  \hline
  {\bf 21}&MD5 ハッシュ認証\\
  \hline
\end{tabular}
\end{center}
\end{table}

MAC OS X SnowLeopardで起動しているVNCサーバに接続するときには
MAC専用の認証の値35がありこれでパスワード認証を行うことができていた。

しかしMAC OS X Lionでパスワード認証を行おうとすると、
MAC OS X Lionにしてパスワード認証ができなくなったので、
別の認証方法で認証を行うことにした。

調べてみるとMAC OS Xが返してくる認証番号は[30, 31, 32, 2, 35]がある。
32はサーバに対して画面要求の認証を求めるタイプの認証であることがわかった。
この認証を用いるとサーバに対してRoot Nodeが接続する際にサーバ側に確認画面が出るようになる。
サーバ側がこれを容認すると認証が成立する。

\newpage



\section{接続先自動検索システムの実装}
Listing\ref{src:gethost}はBroadcastを使用して、データを送信するプログラムである。
起動しているRoot NodeはBroadcastパケットが流れてくるのを待っている(Listing\ref{src:gethost})。
Javaでは、Broadcastパケットを作成す際は、java.net.DatagramPacketを使用する。
\begin{lstlisting}[language=java,frame=lrbt,label=src:gethost,caption=Broadcastを用いてサーバを探すプログラム,numbers=left]
  public GetHostClient(String _str) {
    str = _str;
  }

  public void sendData() {
    buf = str.getBytes();
    DatagramPacket sendPacket = new DatagramPacket(buf, str.length(), mAddr, PORT);
    try {
      soc.send(sendPacket);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
\end{lstlisting}

Listing\ref{src:gethost}のif文の中でstrと受け取った値(recvPacket.getData())を比較しているが、このstrを任意に決めることで、strの情報を知らない人には一覧情報が提供されなくなる。
一覧情報が提供されない場合はIPアドレスを直接指定しなければ接続することができないので、IPアドレスとstrを知らない人は接続することができないので、プライベートな画面共有を行うこともできるように実装している。
Broadcast Packetを受け取ると、受け取ったIPアドレスに対し、TCPコネクションを張り、現在起動しているVNC Serverの一覧を送る(replayBroadcast())。
\begin{lstlisting}[language=java,frame=lrbt,label=src:getbroadcast,caption=Broadcastを受け取るプログラム,numbers=left]
byte[] buf = new byte[BufSize];
byte[] resorve = new byte[BufSize];
try {
  InetAddress mAddr = InetAddress.getByName(McastAddr);
  MulticastSocket soc = new MulticastSocket(Port);
  DatagramPacket recvPacket = new DatagramPacket(buf, BufSize);
  soc.joinGroup(mAddr);
  while (!stopFlag) {
    soc.receive(recvPacket);
    address = getAddress(recvPacket.getSocketAddress());
    inputStream = new ByteArrayInputStream(recvPacket.getData());
    inputStream.read(resorve);
    if(str.equals(castString(resorve)))
      replyBroadcast();
    if(stopFlag) break;
  } catch (IOException e) {
    e.printStackTrace();
  }
}
\end{lstlisting}


Listing\ref{src:gethost}は、Root Nodeから受け取ったVNCServer一覧を表示する部分である。ここで使用されているtextは、javaのGUIコンポーネントであるJFrameを継承したクラスのインスタンスである。
\begin{lstlisting}[language=java,frame=lrbt,label=src:getaddr,caption=起動サーバ一覧を表示するプログラム,numbers=left]
  Socket socket = server.accept();  
  is = new BufferedReader(new InputStreamReader(
  socket.getInputStream()));
  proxyAddr = is.readLine();
  if(proxyAddr!=null)
  text.checkBox(proxyAddr);
  text.setButton();
  text.visible();
\end{lstlisting}




%各64*64のタイル群の先頭には、1byteのsubencordingタイプが設定されている。