このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
今日は FramebufferUpdateRequest メッセージだ。
これは種類 3 番のクライアントメッセージで、
クライアントがサーバに対して、
現在の画面のデータを送信するように要求する。
このメッセージは 10 バイト固定長で、
以下のような構造を持つ。
U8 messageType; // 常に 3
U8 incremental; // 0 以外なら増分のみ、0 なら全体。
U16 x; // 更新を要求する矩形範囲の左端 X 座標
U16 y; // 更新を要求する矩形範囲の上端 Y 座標
U16 width; // 更新を要求する矩形範囲の幅
U16 height; // 更新を要求する矩形範囲の高さ
FramebufferUpdateRequest では、
更新を要求する範囲を指定する事ができる。
例えば、サーバの仮想デスクトップが巨大で、
クライアントのディスプレイで表示しきれない場合、
クライアントはスクロールバー等を使って表現するが、
その際、サーバの画面全体を要求する必要はない。
incremental は、増分更新を指定するフラグだ。
RFB では、通信量を削減するために、
変化のあった領域のみを送信するという方法を定義している。
増分を指定してメッセージを送れば、
もしサーバの画面に変更がなければ、
一切データは送られることはないため効率が良い。
逆に、増分を指定しない場合は、
常に指定範囲全体のデータが送り返されてくるため、
クライアントが領域を再描画する際などに利用できる。
=========== FramebufferUpdateRequest.java ===========
package jp.loafer.rfb.message.client;
import java.io.IOException;
import jp.loafer.rfb.RFBContext;
import jp.loafer.rfb.io.RFBInputStream;
import jp.loafer.rfb.io.RFBOutputStream;
/**
* FramebufferUpdateRequest クライアントメッセージ。
* @author kes
*/
public class FramebufferUpdateRequestMessage extends BaseClientMessage {
/**
* 永続化用既定コンストラクタ。
*/
public FramebufferUpdateRequestMessage() {
//
}
/**
* FramebufferUpdateRequest メッセージを作成。
* @param x X 座標。
* @param y Y 座標。
* @param width 幅。
* @param height 高さ。
* @param incremental 増分のみを更新するか。
*/
public FramebufferUpdateRequestMessage(int x, int y, int width, int height, boolean incremental) {
if (x < 0 || y < 0)
throw new IllegalArgumentException("Invalid coordinate.");
if (width < 0 || height < 0)
throw new IllegalArgumentException("Invalid size.");
this.incremental = incremental;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
/**
* 増分のみを更新するかどうかを取得。
* @return 増分のみを更新する場合 true。
*/
public boolean isIncremental() {
return incremental;
}
/**
* 左上の X 座標を取得。
* @return 左上の X 座標。
*/
public int getX() {
return x;
}
/**
* 左上の Y 座標を取得。
* @return 左上の Y 座標。
*/
public int getY() {
return y;
}
/**
* 幅を取得。
* @return 幅。
*/
public int getWidth() {
return width;
}
/**
* 高さを取得。
* @return 高さ。
*/
public int getHeight() {
return height;
}
/**
* @see ClientMessage#getType()
*/
public int getType() {
return ClientMessage.FRAMEBUFFER_UPDATE_REQUEST;
}
/**
* @see ClientMessage#read(RFBContext, RFBInputStream)
*/
@Override
public void read(RFBContext context, RFBInputStream in) throws IOException {
super.read(context, in);
incremental = in.readU8() != 0;
x = in.readU16();
y = in.readU16();
width = in.readU16();
height = in.readU16();
}
/**
* @see ClientMessage#write(RFBContext, RFBOutputStream)
*/
@Override
public void write(RFBContext context, RFBOutputStream out) throws IOException {
super.write(context, out);
out.writeU8(incremental ? 1 : 0);
out.writeU16(x);
out.writeU16(y);
out.writeU16(width);
out.writeU16(height);
}
private boolean incremental;
private int x;
private int y;
private int width;
private int height;
}
========== end of FramebufferUpdateRequest.java ==========
通常、サーバはクライアントのメッセージに応じて、
サーバの画面に操作を行い、
画面の変化があればその内容をクライアントに送信する。
なので、FramebufferUpdateRequest を送らなくとも、
ほとんどの場合、別のメッセージへの返信として、
サーバから画面の内容が送信されてくる。
しかし、サーバとクライアントの両者は同等の立場ではない。
クライアントは好きなタイミングでメッセージを送れるが、
サーバは、クライアントからメッセージが届かない限り、
勝手にメッセージを返信することはない。
そのため、サーバ側でアニメーションが動作している時や、
タイマーによって画面が随時変更されている時、
もしクライアントで全く操作が行われない場合は、
クライアントはメッセージを送らないため、
サーバは画面の変更をクライアントに通知できない。
これを防ぐために、FramebufferUpdateRequest がある。
RFB クライアントは回線の速度を考えながら、
FramebufferUpdateRequest を定期的に送信することで、
サーバからの画面の更新情報を受け取る事ができる。
つまり、FramebufferUpdateRequest は、
ポーリングの役目を持っていると同時に、
副作用的に通信のタイムアウトを防ぐ役割も持っている。