Akira's Commentary

VNCプロトコル

VNCプロトコルは

から構成されています。以下の各節では、それぞれのフェーズ、 処理におけるプロトコルの詳細をみていくこととしましょう。

VNCのドキュメントではVNCプロトコルとなっていますが、 プロトコルドキュメント側では旧名のRFB (Remote Framae Buffer)のままになっています。

セッションの初期化

VNCセッションはTCPを使用し、ビューワ(クライアント)からの コネクションで始まります。

プロトコルバージョン

VNCプロトコルではコネクションが確立したなら 最初にプロトコルバージョンメッセージを交換します。 プロトコルバージョンメッセージは12バイトのASCII文字列で

'RFB xxx.yyy\n'

の形式です。ここでxxx、yyyはそれぞれプロトコルの メージャー、マイナーバージョンを示します。

コネクションが確立したなら最初にサーバが 自分のサポートするプロトコルバージョンを通知します。 ビューワはこのメッセージによって接続先が 確かにVNCサーバであること、 およびそのバージョンを確認します。 通知されたバージョンが利用可能であればビューワは、 自分の側のプロトコルバージョンをサーバに返します。

このようなバージョン番号の交換は、サーバ、クライアントの双方で ある程度のバックワードコンパチビリティを持てるようにするためのものです。 現在のところは、 マイナーバージョンについてのみコードが実装されています。

認証

プロトコルバージョンの交換後、 サーバはクライアントに認証方式を通知します。

バイト数意味
4CARD32認証方式
0接続失敗
1認証無し
2VNC認証

後続のデータは指示された認証方式によって異なります。

接続失敗
これは何らかの理由でサーバが 接続を受け入れることができなかったことを意味します。 サーバはこの後に失敗理由を送出して コネクションを切断します。
バイト数意味
4CARD32理由の長さ
理由の長さCARD8 array理由の文字列
認証無し
これは認証無しでクライアントを受け付けることを意味します。 後続のデータはありません。 クライアントはすぐにクライアント初期化メッセージを送出します。
VNC認証
VNC認証を使用することを指示します。 これはDES暗号を使うものです。 この場合後続データとして 16バイトのチャレンジがサーバから送られてきます。
バイト数意味
16CARD8チャレンジ
クライアントはユーザから入力されたパスワードでこのチャレンジを DESで暗号化してサーバに送り返します。
バイト数意味
16CARD8応答
サーバはこの応答とあらかじめ登録されているパスワードとをチェックして、 最終的にコネクションを受け付けるかどうかを判断し、その結果を返してきます。
バイト数意味
4CARD32応答
0OK
1失敗
2多すぎる
多すぎる、というのは、サーバが既にクライアントと接続していて、 他の接続を受け付けない場合です。 結果がOKであればクライアントは初期化を行ないます。

クライアント初期化

認証されたならクライアントは次の初期化メッセージを送ります。

バイト数意味
1CARD8共有フラグ

共有フラグは、クライアントがリモートデスクトップを 他のクライアントと共有するかどうかを指示するものです。 このフラグをセットすれば、 同じデスクトップを複数のビューワで 同時にアクセスすることができます。

しかし、最新のサーバには、この指定を無視して、常に共有、 常に占有を指示するオプションが追加されています。

サーバ初期化

クライアント初期化メッセージを受け取ると サーバはサーバ初期化メッセージを返します。 これはリモートデスクトップのサイズ、 ピクセル形式、名前を通知するものです。

バイト数意味
2CARD16画面の幅
2CARD16画面の高さ
16PIXEL_FORMATサーバ側のピクセル形式
4CARD32名前の長さ
名前の長さCARD8 array名前の文字列

名前はリモートデスクトップを識別するためのもので、 Xvncサーバの場合にはユーザ名、 WinVNCの場合にはマシン名を元に設定されます。 クライアント側での使い方は特に規定されていません。 多くの場合ビューワのタイトルとして表示されています。

ピクセル形式は以下のデータで指示されます。

バイト数意味
1CARD8ピクセルビット数(転送上)
1CARD8ピクセルビット数(実データ)
1CARD8ビッグエンディアンフラグ
1CARD8true-color フラグ
2CARD16R 最大値
2CARD16G 最大値
2CARD16B 最大値
1CARD8R シフト
1CARD8G シフト
1CARD8B シフト
3パディング

転送上のビット数は、8、16、32 のいずれかでなければなりません。 実データのビット数は、転送上されたピクセルデータの内、 どれだけが実際のデータになるのかを示します。

true-colorの場合には、後続の6データでRGBの表現形式を指示します。 これはピクセルデータをエンディアンフラグの指示によって 整数値とした場合、RGBそれぞれの最大値と、 フィールド位置を指示するものです。

例えば下記のBGR233形式(1)を使う場合には
Figure for BGR233
となります。

プロトコル上は(true-colorフラグをオフにすることによって) pseudo-colorを使うようにすることもできます。 その場合は、後続のデータは無視され、 (後述の)カラーマップの設定、変更によって カラーマップを設定します。 しかし、現時点ではpseudo-colorが ちゃんと使えるサーバは存在していないようです。

ピクセル形式の指示

サーバの初期化メッセージで通知されたサーバ側のピクセル形式に対して、 クライアントは以降自分で受け取りたいピクセル形式を通知します。

バイト数意味
1CARD80メッセージタイプ
3パディング
16PIXEL_FORMATピクセル形式

プロトコルの定義上は、これは初期化用のメッセージではなく、 クライアントが自由に要求できるメッセージとされています。 しかし、画面データはこのメッセージで指示するピクセルビット数に 強く依存した形式で転送されてきます。 しかし、画面データには それがどのようなビット数のものかを特定する情報がありません。 このためセッションの途中でビット数を変更すると、 それ以降のデータを確実に解釈できる保証はありません。 このため多くの実装では初期化時に一回だけ通知するようにしています。

セッション途中で変更できるようになっているのは Windows用のビューワだけです。 そこでは変更指示を送る前に、 古い形式での画面データの通知が完了するようにディレイを入れています。 これは大抵の場合、ちゃんと動作しますが、 完全に動作する保証はありません。(2)

エンコード形式の指示

クライアントは更に 自分が受け取ることのできる矩形エンコーディングの形式を通知します。

バイト数意味
1CARD82メッセージタイプ
3パディング
2CARD16エンコーディング数
4×数CARD32 arrayエンコーディングのリスト

これもプロトコル定義上は初期化メッセージではなく クライアントが自由に送出できるメッセージです。 ただしこれについては 画面データにエンコーディングタイプが記載されていますので、 実際にセッションの途中で変更しても問題はありません。 しかし、多くのビューワでは初期化時に一回だけ通知しています。

エンコーディングは以下の5種類で、 先にリストされたものが優先されます。

バイト数意味
4CARD32エンコーディングの種類
0Raw
1Copy Rectangle
2RRE
4CoRRE
5hextile

指示されていなくてもRawエンコーディングは無条件に有効になります。 それぞれのエンコーディングの方法については 画面データの通知のところで説明します。

入力イベントの通知

クライアント側で発生した入力イベントはキーイベント、 マウスイベントとしてサーバに通知されます。 実際にはキーやマウス以外のイベント(ペン、メニュー等)を これらにマッピングして送ることもできます。

キーイベントの通知

キーイベントは以下のメッセージでサーバに通知されます。

バイト数意味
1CARD84メッセージタイプ
1CARD8ダウンフラグ
2パディング
4CARD32キーコード

キーコードにはXのkeysymdeef.hの値を使います。

このため、XvncとXビューワとの組み合わせにおいては、 どのようなキーでも確実に通知されます。 しかしその他の組み合わせの場合にはサーバ、クライアントの いずれかで変換が必要になります。 これらの変換ではkeysymdef.h のすべてのキーを サポートしているわけではありませんので、 一部のキー(NLSキーなど)が通知されないことがあります。 実際、WinVNCでは日本語変換関連のキーはまったく動作しません。

マウスイベントの通知

マウスイベントは以下のメッセージでサーバに通知されます。

バイト数意味
1CARD85メッセージタイプ
1CARD8ボタンマスク
2CARD16x座標
2CARD16y座標

これによってマウスの移動とボタンの状態がサーバに通知されます。 ボタンマスクは0-7の各ビットがボタン1-8の状態

0 : ボタンは押されていない
1 : ボタンは押されている

を示します。

ボタン数の差はビューワで吸収する必要があります。 古いWindows版ビューワでは右ボタンを2/3に切り替えて 使うようになっていましたが、最新版では 同時押下によるセンターボタンエミュレーションが追加されました。

画面の通知

画面の通知は、クライアントからの通知要求と、 それに対して返されるサーバからの画面更新データとから構成されます。 なお、これらは一対一に対応するものではなく、 ひとつの通知要求に対して複数の画面更新データが返されることもあれば、 複数の通知要求に対する画面更新データがマージされて返されることもあります。 実装上、画面更新データは通知要求とは独立したものとして扱う必要があります。

更新通知要求

リモートデスクトップの画面が必要な場合、 クライアントは以下のメッセージでサーバにデータの通知を要求します。

バイト数意味
1CARD83メッセージタイプ
1CARD8インクリメンタル
2CARD16x座標
2CARD16y座標
2CARD16
2CARD16高さ

このメッセージはリモートデスクトップの座標、幅、高さで指示される 矩形領域の画面データを送るようにサーバに要求するものです。 インクリメンタルフラグがセットされた場合には 以前に要求した時点以降に更新された画面データだけが通知されます。

多くのビューワでは、最初に全画面データを要求し、 以降インクリメンタルフラグをセットして、 全画面上の更新された部分のみの画面データを要求します。 これらの場合、対象となる矩形領域はいつも リモートデスクトップの全体になります。

しかし、ビューワ側の処理の都合で 一部の画面データが失われるような場合 (画面イメージをキャッシュしないでスクロールするようなケース)には、 失われた範囲だけを対象として通知要求を発行すると、 無駄なく画面イメージを取得することができるわけです。

画面更新データ

サーバからの画面更新データは矩形データの列として通知されてきます。 画面データの先頭にはメッセージのタイプと 後続する矩形データの数が記録されています。

バイト数意味
1CARD80メッセージタイプ
1パディング
2CARD16矩形の数

この後に指示された数の矩形データが続きます。 各々の矩形データの先頭には以下のヘッダ情報が記載されています。

バイト数意味
2CARD16x座標
2CARD16y座標
2CARD16
2CARD16高さ
4CARD32エンコーディング形式
0Raw
1Copy Rectangle
2RRE
4CoRRE
5hextile

ヘッダに続く矩形データはエンコーディング形式によって異なります。

Raw Encoding
この場合には、幅×高さ分のピクセルデータが続きます。 各々のピクセルデータはクライアントが指示した形式で、 左から右へ、上から下の順に並んでいます。
Copy Rectangle Encoding
この場合にはコピー元の矩形の座標が続きます。
バイト数意味
2CARD16x座標
2CARD16y座標
RRE Encoding
RREエンコーディングは画面イメージを、ある色の大きな矩形(背景)と、 その上で異なった色を持った矩形領域(サブ矩形)の集まりとして 表現するものです。RREエンコーディングでは後続の以下のデータで サブ矩形の数と背景色を指示します。
バイト数意味
4CARD32サブ矩形の数
nピクセル値背景色
この後に以下の形式のサブ矩形が続きます。
バイト数意味
nピクセル値サブ矩形の色
2CARD16x座標
2CARD16y座標
2CARD16
2CARD16高さ
ピクセル値のサイズはクライアントが要求したピクセル形式により 1、2、4バイトのいずれかになります。
CoRRE Encoding
CoRREは、矩形のサイズを256以下にすることによって サブ矩形の表現に必要なデータサイズを減少させたものです。 RREと同じくサブ矩形の数と背景色で始まります。
バイト数意味
4CARD32サブ矩形の数
nピクセル値背景色
この後にサブ矩形が続きますが、その表現が少しだけ異なります。
バイト数意味
nピクセル値サブ矩形の色
1CARD8x座標
1CARD8y座標
1CARD8
1CARD8高さ
Hextile Encoding
hextile はサブ矩形を16×16の小領域(タイル)によって表現するものです。 このエンコーディングでは、矩形ヘッダの後に、タイルデータが連続します。 タイルは矩形の左から右、上から下へ並んでいます。 右端、下端で16に満たない場合には、それぞれの幅、高さは16以下になります。
このようにすると、 タイル毎に異なったエンコーディングを使って 効率よくイメージを表現することができます。 また、タイル内でサブ矩形を使う場合には、 座標もサイズもそれぞれ4ビットで表現できますので、 よりコンパクトに表現することが可能になります。
タイルデータは以下のサブエンコーディングマスクで始まります。
バイト数意味
1CARD8サブエンコーディングマスク
1Raw
2背景色有り
4前景色有り
8サブ矩形有り
16色付き
Raw
Raw ビットがセットされていれば、タイルはRaw エンコードされています。 この場合には幅×高さ分のピクセルデータが続きます。
背景色有り
この後に背景色を示すピクセルデータが存在していることを示します。
バイト数意味
nピクセル値背景色
前景色有り
この後に前景色を示すピクセルデータが存在していることを示します。
バイト数意味
nピクセル値前景色
サブ矩形有り
この後にタイル上のサブ矩形数が記録されていることを示します。
バイト数意味
1CARD8サブ矩形数
色付き
このビットがセットされている場合には、 タイル上のサブ矩形はそれぞれ異なった色を持ちます。 この場合、サブ矩形は以下の形式で表現されます。
バイト数意味
nピクセル値サブ矩形の色
1CARD8x,y座標
1CARD8幅、高さ
ビットがオフであればタイル上のサブ矩形はどれも その時の前景色で塗りつぶされます。 この場合、サブ矩形は以下の形式で表現されます。
バイト数意味
1CARD8x,y座標
1CARD8幅、高さ

クリップボードの転送

プロトコル上はサーバとクライアント間で クリップボード上のデータを交換することができるようになっています。

サーバからクライアントへ
バイト数意味
1CARD83メッセージタイプ
1パディング
4CARD32長さ
長さCARD8 arrayテキスト
クライアントからサーバへ
バイト数意味
1CARD86メッセージタイプ
1パディング
4CARD32長さ
長さCARD8 arrayテキスト

改行は'\n'で表現することになっています。 ただ、日本語を使う場合のエンコードについては (あたりまえですが)規定されていません。 同じコード系であれば問題無く転送できますが、 コード系が異なっていると役に立ちません。

ベル

サーバからクライアントにベルを通知することができます。

バイト数意味
1CARD82メッセージタイプ

これをどのように扱うかはビューワに依存します。 多くのビューワでは、このメッセージ受信で、 最小化されていたウィンドウをリストアすることもできるようになっています。

カラーマップの設定/変更

これは、pseudo-colorを使う場合に使用されるものです。

FixColourMapEntries
クライアントがサーバに対して ここで指示されるカラーマップを使用することを要求するものです。
バイト数意味
1CARD81メッセージタイプ
1パディング
2CARD16先頭の色インデックス
2CARD16色の数
この後に指示された数のRGBデータが続きます。
バイト数意味
2CARD16
2CARD16
2CARD16
SetColourMapEntries
これは、上で指示されなかったエントリについて、 サーバ側のカラーインデックスとRGB値の対応を通知するものです。
バイト数意味
1CARD82メッセージタイプ
1パディング
2CARD16先頭の色インデックス
2CARD16色の数
この後に指示された数のRGBデータが続きます。

ですが、現在のところpseudo-colorがサポートされているサーバは 存在していないので使うことはまずないでしょう。