2007 年 1 月 9 日 23 時 36 分

FramebufferUpdateRequest メッセージ


このアーカイブは同期化されません。 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 は、
ポーリングの役目を持っていると同時に、
副作用的に通信のタイムアウトを防ぐ役割も持っている。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.