RFB协议及源码

1、VNC简介

VNC采用RFB通信协议。RFB("remote 帧缓存")是一个远程图形用户的简单协议,因为它工作在帧缓存级别上。VNC(Virtual
Network Computing)基本上是属于一种显示系统,也就是说它能将完整的窗口界面通过网络,传输到另一台计算机的屏幕上.

独特的计算环境 

RFB协议可进行可靠的传输,如字节流或基于消息的。和大多数协议一样,它也是通过TCP/IP协议簇连接。

协议由三步完成连接:

首先是握手报文,目的是对协议版本和加密方式进行协商。

第二步是初始化报文,主要用于客户和服务器的初始化消息。

最后就是正常握手始于服务器向客户发送协议版本的消息,告知客户服务器所能支持RFB 协议的最高版本号。此时客户端会发送相似的消息告诉服务器将要使用的协议版本。客户端不应该请求高于服务器的协议版本。如此一来就给客户和服务器端提供了一种向后兼容机制。

一旦协议版本被确定,服务器和客户端必须一致同意连接的安全类型。服务器发送所支持的安全类型,当客户端支持服务器的某一种安全类型,客户端选择这种安全认证类型并发送给服务器。否则客户端发送失败,并标识出失败原因。

安全认证

安全认证有多种,有一种为VNC安全认证,当用VNC认证的时候,协议数据采用明文发送,服务器发送一个16字节的随机数验证给客户端,客户端用DES对验证进行加密,用用户密码作为密钥回复给服务器16字节,这时服务器会返回安全结果给客户端。如果成功就进入初始化报文阶段。不成功就关闭连接。

当安全认证成功后,客户端会发送客户端是否共享服务器初始化报文,当客户端设置报文为可共享时,服务器查看当前配置是否允许共享连接,如果同意,则不关闭之前连接的客户端,否则断开之前连接的客户端。

这时服务器会发送客户端初始化信息。这些信息包括:服务器上帧缓存的高宽,像素格式还有与桌面相关的名称,其中服务器象素定义服务器本来的象素格式,这种象素格式会被一直使用,除非客户端使用设置象素格式消息来请求另一种象素格式。

至此初始化报文阶段完成,进入协议交互阶段。

协议交互分为客户到服务器消息和服务器到客户消息。

2、UltraVNC、TightVNC、RealVNC联系与比较:

RealVNC:由VNC團隊部份成員開發,分為全功能商业版和免费版。RealVNC只提供旧版本的源码。

TightVNC:強調節省頻寬使用。

UltraVNC:加入了TightVNC的部份程式及加強效能的图形映射驱动程式,并結合Active Directory及NTLM的账号密码认证,但仅仅有Windows版本。UltraVNC每更新一个版本都会随之附加最新版源码。

3、RFB协议详解

RFB协议,用于VNC(VIRTUAL NETWORK COMPUTING)连接。

RFB协议可以通过字节流或数据报发送。换言之,TCP UDP都是可以的,不过,我们大多喜欢可靠的连接TCP。

协议的基本数据类型有:U8, U16, U32, S8, S16, S32,U代表UNSIGNED,S代表SIGNED

详见:http://mnstory.net/2013/09/rfc6143-rfb-protocol-for-vnc/

提供一份Android客户端RFB协议的源码,结合上述连接里的RFB协议详解,加深感官认识:

package android.androidVNC;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import android.util.Log;

/**
 * 通过Socket访问RFB协议
 * <p>
 * 该类定义了RFB协议中帧缓存(framebuffer)更新和输入事件。
 *
 */
class RfbProto {

	final static String TAG = "RfbProto";

	// RFB协议的版本号
	final static String versionMsg_3_3 = "RFB 003.003\n",
			versionMsg_3_7 = "RFB 003.007\n", versionMsg_3_8 = "RFB 003.008\n";

	// 供应商签名: standard VNC/RealVNC, TridiaVNC, and TightVNC
	final static String StandardVendor = "STDV", TridiaVncVendor = "TRDV",
			TightVncVendor = "TGHT";

	// 安全类型(Security types)
	final static int SecTypeInvalid = 0, SecTypeNone = 1, SecTypeVncAuth = 2,
			SecTypeTight = 16, SecTypeUltra34 = 0xfffffffa;

	// 支持的通道类型(Supported tunneling types)
	final static int NoTunneling = 0;
	final static String SigNoTunneling = "NOTUNNEL";

	// 支持的验证类型(Supported authentication types)
	final static int AuthNone = 1, AuthVNC = 2, AuthUnixLogin = 129,
			AuthUltra = 17;
	final static String SigAuthNone = "NOAUTH__", SigAuthVNC = "VNCAUTH_",
			SigAuthUnixLogin = "ULGNAUTH";

	// VNC认证结果( VNC authentication results)
	final static int VncAuthOK = 0, VncAuthFailed = 1, VncAuthTooMany = 2;

	// Server-to-client messages
	final static int FramebufferUpdate = 0, SetColourMapEntries = 1, Bell = 2,
			ServerCutText = 3, TextChat = 11;

	// Client-to-server messages
	final static int SetPixelFormat = 0, FixColourMapEntries = 1,
			SetEncodings = 2, FramebufferUpdateRequest = 3, KeyboardEvent = 4,
			PointerEvent = 5, ClientCutText = 6;

	// 支持的编码和伪编码(Supported encodings and pseudo-encodings)
	final static int EncodingRaw = 0, EncodingCopyRect = 1, EncodingRRE = 2,
			EncodingCoRRE = 4, EncodingHextile = 5, EncodingZlib = 6,
			EncodingTight = 7, EncodingZRLE = 16,
			EncodingCompressLevel0 = 0xFFFFFF00,
			EncodingQualityLevel0 = 0xFFFFFFE0, EncodingXCursor = 0xFFFFFF10,
			EncodingRichCursor = 0xFFFFFF11, EncodingPointerPos = 0xFFFFFF18,
			EncodingLastRect = 0xFFFFFF20, EncodingNewFBSize = 0xFFFFFF21;
	final static String SigEncodingRaw = "RAW_____",
			SigEncodingCopyRect = "COPYRECT", SigEncodingRRE = "RRE_____",
			SigEncodingCoRRE = "CORRE___", SigEncodingHextile = "HEXTILE_",
			SigEncodingZlib = "ZLIB____", SigEncodingTight = "TIGHT___",
			SigEncodingZRLE = "ZRLE____",
			SigEncodingCompressLevel0 = "COMPRLVL",
			SigEncodingQualityLevel0 = "JPEGQLVL",
			SigEncodingXCursor = "X11CURSR",
			SigEncodingRichCursor = "RCHCURSR",
			SigEncodingPointerPos = "POINTPOS",
			SigEncodingLastRect = "LASTRECT",
			SigEncodingNewFBSize = "NEWFBSIZ";

	final static int MaxNormalEncoding = 255;

	// 在Hextile解码器中用到的常量(Contstants used in the Hextile decoder)
	final static int HextileRaw = 1, HextileBackgroundSpecified = 2,
			HextileForegroundSpecified = 4, HextileAnySubrects = 8,
			HextileSubrectsColoured = 16;

	// 在Tight解码器中用到的常量(Contstants used in the Tight decoder)
	final static int TightMinToCompress = 12;
	final static int TightExplicitFilter = 0x04, TightFill = 0x08,
			TightJpeg = 0x09, TightMaxSubencoding = 0x09,
			TightFilterCopy = 0x00, TightFilterPalette = 0x01,
			TightFilterGradient = 0x02;

	// 用与UltraVNC“交流”扩展的常量(Constants used for UltraVNC chat extension)
	final static int CHAT_OPEN = -1, CHAT_CLOSE = -2, CHAT_FINISHED = -3;

	String host; // 服务端IP
	int port; // 服务端密码
	Socket sock; // 与服务端通信的Socket
	DataInputStream is; // 数据输入流
	OutputStream os; // 数据输出流

	DH dh;
	long dh_resp;

	// - SessionRecorder rec;
	boolean inNormalProtocol = false;
	// - VncViewer viewer;

	// Java on UNIX does not call keyPressed() on some keys, for example
	// swedish keys To prevent our workaround to produce duplicate
	// keypresses on JVMs that actually works, keep track of if
	// keyPressed() for a "broken" key was called or not.
	boolean brokenKeyPressed = false;

	// 在第一次包含Zlib-, ZRLE-或者Tight编码的数据的framebuffer更新时被设置为true
	// This will be set to true on the first framebuffer update
	// containing Zlib-, ZRLE- or Tight-encoded data.
	boolean wereZlibUpdates = false;

	// This will be set to false if the startSession() was called after
	// we have received at least one Zlib-, ZRLE- or Tight-encoded
	// framebuffer update.
	boolean recordFromBeginning = true;

	// This fields are needed to show warnings about inefficiently saved
	// sessions only once per each saved session file.
	boolean zlibWarningShown;
	boolean tightWarningShown;

	// Before starting to record each saved session, we set this field
	// to 0, and increment on each framebuffer update. We don't flush
	// the SessionRecorder data into the file before the second update.
	// This allows us to write initial framebuffer update with zero
	// timestamp, to let the player show initial desktop before
	// playback.
	int numUpdatesInSession;

	// 测量网络的吞吐量(Measuring network throughput)
	boolean timing;
	long timeWaitedIn100us;
	long timedKbits;

	// 协议版本和TightVNC特殊的协议选项(Protocol version and TightVNC-specific protocol
	// options.)
	int serverMajor, serverMinor;
	int clientMajor, clientMinor;
	boolean protocolTightVNC;
	CapsContainer tunnelCaps, authCaps;
	CapsContainer serverMsgCaps, clientMsgCaps;
	CapsContainer encodingCaps;

	// RFB Socket关闭(If true, informs that the RFB socket was closed.)
	private boolean closed;

	// Constructor. Make TCP connection to RFB server.
	// 构造函数————用TCP连接到RFB服务器
	// -RfbProto(String h, int p, VncViewer v) throws IOException {
	RfbProto(String h, int p) throws IOException {
		// - viewer = v;
		host = h;
		port = p;

		sock = new Socket(host, port);
		is = new DataInputStream(new BufferedInputStream(sock.getInputStream(),
				16384));
		os = sock.getOutputStream();

		timing = false;
		timeWaitedIn100us = 5;
		timedKbits = 0;
	}

	// 关闭
	synchronized void close() {
		try {
			sock.close();
			closed = true;
			Log.v(TAG, "RFB socket closed");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	synchronized boolean closed() {
		return closed;
	}

	// Read server's protocol version message
	// 读取服务器的协议版本
	void readVersionMsg() throws Exception {

		byte[] b = new byte[12];

		readFully(b);

		if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ')
				|| (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9')
				|| (b[6] < '0') || (b[6] > '9') || (b[7] != '.')
				|| (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9')
				|| (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) {
			Log.i(TAG, new String(b));
			throw new Exception("Host " + host + " port " + port
					+ " is not an RFB server");
		}

		serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0');
		serverMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0');

		if (serverMajor < 3) {
			throw new Exception(
					"RFB server does not support protocol version 3");
		}
	}

	// Write our protocol version message
	// 写入我们客户端的协议版本
	synchronized void writeVersionMsg() throws IOException {
		clientMajor = 3;
		if (serverMajor > 3 || serverMinor >= 8) {
			clientMinor = 8;
			os.write(versionMsg_3_8.getBytes());
		} else if (serverMinor >= 7) {
			clientMinor = 7;
			os.write(versionMsg_3_7.getBytes());
		} else {
			clientMinor = 3;
			os.write(versionMsg_3_3.getBytes());
		}
		protocolTightVNC = false;
	}

	// Negotiate the authentication scheme.
	// 协商认证方案
	int negotiateSecurity(int bitPref) throws Exception {
		return (clientMinor >= 7) ? selectSecurityType(bitPref)
				: readSecurityType(bitPref);
	}

	//
	// Read security type from the server (protocol version 3.3).
	// 读取服务器的安全类型
	int readSecurityType(int bitPref) throws Exception {
		int secType = is.readInt();

		switch (secType) {
		case SecTypeInvalid:
			readConnFailedReason();
			return SecTypeInvalid; // should never be executed
		case SecTypeNone:
		case SecTypeVncAuth:
			return secType;
		case SecTypeUltra34:
			if ((bitPref & 1) == 1)
				return secType;
			throw new Exception("Username required.");
		default:
			throw new Exception("Unknown security type from RFB server: "
					+ secType);
		}
	}

	//
	// Select security type from the server's list (protocol versions 3.7/3.8).
	// 从服务器的列表中读取安全类型
	int selectSecurityType(int bitPref) throws Exception {
		int secType = SecTypeInvalid;

		// Read the list of security types.
		int nSecTypes = is.readUnsignedByte();
		if (nSecTypes == 0) {
			readConnFailedReason();
			return SecTypeInvalid; // should never be executed
		}
		byte[] secTypes = new byte[nSecTypes];
		readFully(secTypes);

		// Find out if the server supports TightVNC protocol extensions
		// 查明服务器是否支持TightVNC协议的扩展
		for (int i = 0; i < nSecTypes; i++) {
			if (secTypes[i] == SecTypeTight) {
				protocolTightVNC = true;
				os.write(SecTypeTight);
				return SecTypeTight;
			}
		}

		// Find first supported security type.
		// 找到第一个支持的安全类型
		for (int i = 0; i < nSecTypes; i++) {
			if (secTypes[i] == SecTypeNone || secTypes[i] == SecTypeVncAuth) {
				secType = secTypes[i];
				break;
			}
		}

		if (secType == SecTypeInvalid) {
			throw new Exception("Server did not offer supported security type");
		} else {
			os.write(secType);
		}

		return secType;
	}

	//
	// Perform "no authentication".
	// 执行“无认证”
	void authenticateNone() throws Exception {
		if (clientMinor >= 8)
			readSecurityResult("No authentication");
	}

	//
	// Perform standard VNC Authentication.
	// 执行标准VNC认证
	void authenticateVNC(String pw) throws Exception {
		byte[] challenge = new byte[16];
		readFully(challenge);

		if (pw.length() > 8)
			pw = pw.substring(0, 8); // Truncate to 8 chars

		// Truncate password on the first zero byte.
		int firstZero = pw.indexOf(0);
		if (firstZero != -1)
			pw = pw.substring(0, firstZero);

		byte[] key = { 0, 0, 0, 0, 0, 0, 0, 0 };
		System.arraycopy(pw.getBytes(), 0, key, 0, pw.length());

		DesCipher des = new DesCipher(key);

		des.encrypt(challenge, 0, challenge, 0);
		des.encrypt(challenge, 8, challenge, 8);

		os.write(challenge);

		readSecurityResult("VNC authentication");
	}

	//
	// Read security result.
	// Throws an exception on authentication failure.
	// 读取安全结果,如果认证失败抛出异常。
	void readSecurityResult(String authType) throws Exception {
		int securityResult = is.readInt();

		switch (securityResult) {
		case VncAuthOK:
			System.out.println(authType + ": success");
			break;
		case VncAuthFailed:
			if (clientMinor >= 8)
				readConnFailedReason();
			throw new Exception(authType + ": failed");
		case VncAuthTooMany:
			throw new Exception(authType + ": failed, too many tries");
		default:
			throw new Exception(authType + ": unknown result " + securityResult);
		}
	}

	//
	// Read the string describing the reason for a connection failure,
	// and throw an exception.
	// 读取描述连接失败的原因
	void readConnFailedReason() throws Exception {
		int reasonLen = is.readInt();
		byte[] reason = new byte[reasonLen];
		readFully(reason);
		String reasonString = new String(reason);
		Log.v(TAG, reasonString);
		throw new Exception(reasonString);
	}

	// 在各方之间交换秘钥时会用到DH类(即使第三方偷听者获取到了传输的值,也不能得到秘钥)
	void prepareDH() throws Exception {
		long gen = is.readLong();
		long mod = is.readLong();
		dh_resp = is.readLong();

		dh = new DH(gen, mod);
		long pub = dh.createInterKey();

		os.write(DH.longToBytes(pub));
	}

	void authenticateDH(String us, String pw) throws Exception {
		long key = dh.createEncryptionKey(dh_resp);

		DesCipher des = new DesCipher(DH.longToBytes(key));

		byte user[] = new byte[256];
		byte passwd[] = new byte[64];
		int i;
		System.arraycopy(us.getBytes(), 0, user, 0, us.length());
		if (us.length() < 256) {
			for (i = us.length(); i < 256; i++) {
				user[i] = 0;
			}
		}
		System.arraycopy(pw.getBytes(), 0, passwd, 0, pw.length());
		if (pw.length() < 64) {
			for (i = pw.length(); i < 64; i++) {
				passwd[i] = 0;
			}
		}

		des.encryptText(user, user, DH.longToBytes(key));
		des.encryptText(passwd, passwd, DH.longToBytes(key));

		os.write(user);
		os.write(passwd);

		readSecurityResult("VNC authentication");
	}

	//
	// Initialize capability lists (TightVNC protocol extensions).
	// 初始化容量列表(TightVNC协议的扩展)
	void initCapabilities() {
		tunnelCaps = new CapsContainer();
		authCaps = new CapsContainer();
		serverMsgCaps = new CapsContainer();
		clientMsgCaps = new CapsContainer();
		encodingCaps = new CapsContainer();

		// Supported authentication methods
		// 支持的认证类型
		authCaps.add(AuthNone, StandardVendor, SigAuthNone, "No authentication");
		authCaps.add(AuthVNC, StandardVendor, SigAuthVNC,
				"Standard VNC password authentication");

		// Supported encoding types
		// 支持的编码类型
		encodingCaps.add(EncodingCopyRect, StandardVendor, SigEncodingCopyRect,
				"Standard CopyRect encoding");
		encodingCaps.add(EncodingRRE, StandardVendor, SigEncodingRRE,
				"Standard RRE encoding");
		encodingCaps.add(EncodingCoRRE, StandardVendor, SigEncodingCoRRE,
				"Standard CoRRE encoding");
		encodingCaps.add(EncodingHextile, StandardVendor, SigEncodingHextile,
				"Standard Hextile encoding");
		encodingCaps.add(EncodingZRLE, StandardVendor, SigEncodingZRLE,
				"Standard ZRLE encoding");
		encodingCaps.add(EncodingZlib, TridiaVncVendor, SigEncodingZlib,
				"Zlib encoding");
		encodingCaps.add(EncodingTight, TightVncVendor, SigEncodingTight,
				"Tight encoding");

		// Supported pseudo-encoding types
		// 支持的伪编码类型
		encodingCaps.add(EncodingCompressLevel0, TightVncVendor,
				SigEncodingCompressLevel0, "Compression level");
		encodingCaps.add(EncodingQualityLevel0, TightVncVendor,
				SigEncodingQualityLevel0, "JPEG quality level");
		encodingCaps.add(EncodingXCursor, TightVncVendor, SigEncodingXCursor,
				"X-style cursor shape update");
		encodingCaps.add(EncodingRichCursor, TightVncVendor,
				SigEncodingRichCursor, "Rich-color cursor shape update");
		encodingCaps.add(EncodingPointerPos, TightVncVendor,
				SigEncodingPointerPos, "Pointer position update");
		encodingCaps.add(EncodingLastRect, TightVncVendor, SigEncodingLastRect,
				"LastRect protocol extension");
		encodingCaps.add(EncodingNewFBSize, TightVncVendor,
				SigEncodingNewFBSize, "Framebuffer size change");
	}

	//
	// Setup tunneling (TightVNC protocol extensions)
	// 设置通道(TightVNC协议的扩展)
	void setupTunneling() throws IOException {
		int nTunnelTypes = is.readInt();
		if (nTunnelTypes != 0) {
			readCapabilityList(tunnelCaps, nTunnelTypes);

			// We don't support tunneling yet.
			writeInt(NoTunneling);
		}
	}

	//
	// Negotiate authentication scheme (TightVNC protocol extensions)
	// 协商认证方案(TightVNC协议的扩展)
	int negotiateAuthenticationTight() throws Exception {
		int nAuthTypes = is.readInt();
		if (nAuthTypes == 0)
			return AuthNone;

		readCapabilityList(authCaps, nAuthTypes);
		for (int i = 0; i < authCaps.numEnabled(); i++) {
			int authType = authCaps.getByOrder(i);
			if (authType == AuthNone || authType == AuthVNC) {
				writeInt(authType);
				return authType;
			}
		}
		throw new Exception("No suitable authentication scheme found");
	}

	//
	// Read a capability list (TightVNC protocol extensions)
	// 读容量列表(TightVNC协议的扩展)
	void readCapabilityList(CapsContainer caps, int count) throws IOException {
		int code;
		byte[] vendor = new byte[4];
		byte[] name = new byte[8];
		for (int i = 0; i < count; i++) {
			code = is.readInt();
			readFully(vendor);
			readFully(name);
			caps.enable(new CapabilityInfo(code, vendor, name));
		}
	}

	//
	// Write a 32-bit integer into the output stream.
	// 向输出流切一个32位的整数
	byte[] writeIntBuffer = new byte[4];

	void writeInt(int value) throws IOException {
		writeIntBuffer[0] = (byte) ((value >> 24) & 0xff);
		writeIntBuffer[1] = (byte) ((value >> 16) & 0xff);
		writeIntBuffer[2] = (byte) ((value >> 8) & 0xff);
		writeIntBuffer[3] = (byte) (value & 0xff);
		os.write(writeIntBuffer);
	}

	//
	// Write the client initialisation message
	// 写入客户端初始化信息
	void writeClientInit() throws IOException {
		os.write(0);
	}

	//
	// Read the server initialisation message
	// 读取服务器初始化信息
	String desktopName;
	int framebufferWidth, framebufferHeight;
	int bitsPerPixel, depth;
	boolean bigEndian, trueColour;
	int redMax, greenMax, blueMax, redShift, greenShift, blueShift;

	void readServerInit() throws IOException {
		framebufferWidth = is.readUnsignedShort();
		framebufferHeight = is.readUnsignedShort();
		bitsPerPixel = is.readUnsignedByte();
		depth = is.readUnsignedByte();
		bigEndian = (is.readUnsignedByte() != 0);
		trueColour = (is.readUnsignedByte() != 0);
		redMax = is.readUnsignedShort();
		greenMax = is.readUnsignedShort();
		blueMax = is.readUnsignedShort();
		redShift = is.readUnsignedByte();
		greenShift = is.readUnsignedByte();
		blueShift = is.readUnsignedByte();
		byte[] pad = new byte[3];
		readFully(pad);
		int nameLength = is.readInt();
		byte[] name = new byte[nameLength];
		readFully(name);
		desktopName = new String(name);

		// Read interaction capabilities (TightVNC protocol extensions)
		// 读取交互能力(TightVNC协议的扩展)
		if (protocolTightVNC) {
			int nServerMessageTypes = is.readUnsignedShort();
			int nClientMessageTypes = is.readUnsignedShort();
			int nEncodingTypes = is.readUnsignedShort();
			is.readUnsignedShort();
			readCapabilityList(serverMsgCaps, nServerMessageTypes);
			readCapabilityList(clientMsgCaps, nClientMessageTypes);
			readCapabilityList(encodingCaps, nEncodingTypes);
		}

		inNormalProtocol = true;
	}

	//
	// Set new framebuffer size
	// 设置新的帧缓存framebuffer的大小
	void setFramebufferSize(int width, int height) {
		framebufferWidth = width;
		framebufferHeight = height;
	}

	//
	// Read the server message type
	// 读取服务器消息类型
	int readServerMessageType() throws IOException {
		int msgType = is.readUnsignedByte();

		return msgType;
	}

	//
	// Read a FramebufferUpdate message
	// 读取一个更新帧缓存的消息
	int updateNRects;

	void readFramebufferUpdate() throws IOException {
		is.readByte();
		updateNRects = is.readUnsignedShort();

		// If the session is being recorded:
		/*-
		if (rec != null) {
		  rec.writeByte(FramebufferUpdate);
		  rec.writeByte(0);
		  rec.writeShortBE(updateNRects);
		}
		 */

		numUpdatesInSession++;
	}

	// Read a FramebufferUpdate rectangle header
	// 读取一个更新帧缓存矩形的首部
	int updateRectX, updateRectY, updateRectW, updateRectH, updateRectEncoding;

	void readFramebufferUpdateRectHdr() throws Exception {
		updateRectX = is.readUnsignedShort();
		updateRectY = is.readUnsignedShort();
		updateRectW = is.readUnsignedShort();
		updateRectH = is.readUnsignedShort();
		updateRectEncoding = is.readInt();

		if (updateRectEncoding == EncodingZlib
				|| updateRectEncoding == EncodingZRLE
				|| updateRectEncoding == EncodingTight)
			wereZlibUpdates = true;

		if (updateRectEncoding != RfbProto.EncodingPointerPos
				&& (updateRectEncoding < 0 || updateRectEncoding > MaxNormalEncoding))
			return;

		if (updateRectX + updateRectW > framebufferWidth
				|| updateRectY + updateRectH > framebufferHeight) {
			throw new Exception("Framebuffer update rectangle too large: "
					+ updateRectW + "x" + updateRectH + " at (" + updateRectX
					+ "," + updateRectY + ")");
		}
	}

	// Read CopyRect source X and Y.
	//
	int copyRectSrcX, copyRectSrcY;

	void readCopyRect() throws IOException {
		copyRectSrcX = is.readUnsignedShort();
		copyRectSrcY = is.readUnsignedShort();
	}

	//
	// Read a ServerCutText message
	//
	String readServerCutText() throws IOException {
		byte[] pad = new byte[3];
		readFully(pad);
		int len = is.readInt();
		byte[] text = new byte[len];
		readFully(text);
		return new String(text);
	}

	//
	// Read an integer in compact representation (1..3 bytes).
	// Such format is used as a part of the Tight encoding.
	// Also, this method records data if session recording is active and
	// the viewer's recordFromBeginning variable is set to true.
	//
	int readCompactLen() throws IOException {
		int[] portion = new int[3];
		portion[0] = is.readUnsignedByte();
		int byteCount = 1;
		int len = portion[0] & 0x7F;
		if ((portion[0] & 0x80) != 0) {
			portion[1] = is.readUnsignedByte();
			byteCount++;
			len |= (portion[1] & 0x7F) << 7;
			if ((portion[1] & 0x80) != 0) {
				portion[2] = is.readUnsignedByte();
				byteCount++;
				len |= (portion[2] & 0xFF) << 14;
			}
		}
		/*-
		if (rec != null && recordFromBeginning)
		  for (int i = 0; i < byteCount; i++)
		rec.writeByte(portion[i]);
		 */
		return len;
	}

	//
	// Write a FramebufferUpdateRequest message
	// 写入一个要求更新帧缓存的消息
	byte[] framebufferUpdateRequest = new byte[10];

	synchronized void writeFramebufferUpdateRequest(int x, int y, int w, int h,
			boolean incremental) throws IOException {
		framebufferUpdateRequest[0] = (byte) FramebufferUpdateRequest;
		framebufferUpdateRequest[1] = (byte) (incremental ? 1 : 0);
		framebufferUpdateRequest[2] = (byte) ((x >> 8) & 0xff);
		framebufferUpdateRequest[3] = (byte) (x & 0xff);
		framebufferUpdateRequest[4] = (byte) ((y >> 8) & 0xff);
		framebufferUpdateRequest[5] = (byte) (y & 0xff);
		framebufferUpdateRequest[6] = (byte) ((w >> 8) & 0xff);
		framebufferUpdateRequest[7] = (byte) (w & 0xff);
		framebufferUpdateRequest[8] = (byte) ((h >> 8) & 0xff);
		framebufferUpdateRequest[9] = (byte) (h & 0xff);

		os.write(framebufferUpdateRequest);
	}

	//
	// Write a SetPixelFormat message
	// 写入一个设置像素格式的消息
	synchronized void writeSetPixelFormat(int bitsPerPixel, int depth,
			boolean bigEndian, boolean trueColour, int redMax, int greenMax,
			int blueMax, int redShift, int greenShift, int blueShift,
			boolean fGreyScale) // [email protected])
			throws IOException {
		byte[] b = new byte[20];

		b[0] = (byte) SetPixelFormat;
		b[4] = (byte) bitsPerPixel;
		b[5] = (byte) depth;
		b[6] = (byte) (bigEndian ? 1 : 0);
		b[7] = (byte) (trueColour ? 1 : 0);
		b[8] = (byte) ((redMax >> 8) & 0xff);
		b[9] = (byte) (redMax & 0xff);
		b[10] = (byte) ((greenMax >> 8) & 0xff);
		b[11] = (byte) (greenMax & 0xff);
		b[12] = (byte) ((blueMax >> 8) & 0xff);
		b[13] = (byte) (blueMax & 0xff);
		b[14] = (byte) redShift;
		b[15] = (byte) greenShift;
		b[16] = (byte) blueShift;
		b[17] = (byte) (fGreyScale ? 1 : 0); // [email protected]

		os.write(b);
	}

	//
	// Write a FixColourMapEntries message. The values in the red, green and
	// blue arrays are from 0 to 65535.
	// 颜色Map条目
	synchronized void writeFixColourMapEntries(int firstColour, int nColours,
			int[] red, int[] green, int[] blue) throws IOException {
		byte[] b = new byte[6 + nColours * 6];

		b[0] = (byte) FixColourMapEntries;
		b[2] = (byte) ((firstColour >> 8) & 0xff);
		b[3] = (byte) (firstColour & 0xff);
		b[4] = (byte) ((nColours >> 8) & 0xff);
		b[5] = (byte) (nColours & 0xff);

		for (int i = 0; i < nColours; i++) {
			b[6 + i * 6] = (byte) ((red[i] >> 8) & 0xff);
			b[6 + i * 6 + 1] = (byte) (red[i] & 0xff);
			b[6 + i * 6 + 2] = (byte) ((green[i] >> 8) & 0xff);
			b[6 + i * 6 + 3] = (byte) (green[i] & 0xff);
			b[6 + i * 6 + 4] = (byte) ((blue[i] >> 8) & 0xff);
			b[6 + i * 6 + 5] = (byte) (blue[i] & 0xff);
		}

		os.write(b);
	}

	//
	// Write a SetEncodings message
	// 写入设置编码消息
	synchronized void writeSetEncodings(int[] encs, int len) throws IOException {
		byte[] b = new byte[4 + 4 * len];

		b[0] = (byte) SetEncodings;
		b[2] = (byte) ((len >> 8) & 0xff);
		b[3] = (byte) (len & 0xff);

		for (int i = 0; i < len; i++) {
			b[4 + 4 * i] = (byte) ((encs[i] >> 24) & 0xff);
			b[5 + 4 * i] = (byte) ((encs[i] >> 16) & 0xff);
			b[6 + 4 * i] = (byte) ((encs[i] >> 8) & 0xff);
			b[7 + 4 * i] = (byte) (encs[i] & 0xff);
		}

		os.write(b);
	}

	//
	// Write a ClientCutText message
	// 写入一个客户端切割文本的消息
	synchronized void writeClientCutText(String text) throws IOException {
		byte[] b = new byte[8 + text.length()];

		b[0] = (byte) ClientCutText;
		b[4] = (byte) ((text.length() >> 24) & 0xff);
		b[5] = (byte) ((text.length() >> 16) & 0xff);
		b[6] = (byte) ((text.length() >> 8) & 0xff);
		b[7] = (byte) (text.length() & 0xff);

		System.arraycopy(text.getBytes(), 0, b, 8, text.length());

		os.write(b);
	}

	//
	// A buffer for putting pointer and keyboard events before being sent. This
	// is to ensure that multiple RFB events generated from a single Java Event
	// will all be sent in a single network packet. The maximum possible
	// length is 4 modifier down events, a single key event followed by 4
	// modifier up events i.e. 9 key events or 72 bytes.
	//
	byte[] eventBuf = new byte[72];
	int eventBufLen;

	/**
	 * Write a pointer event message. We may need to send modifier key events
	 * around it to set the correct modifier state.
	 *
	 * @param x
	 * @param y
	 * @param modifiers
	 * @param pointerMask
	 * @throws IOException
	 */
	synchronized void writePointerEvent(int x, int y, int modifiers,
			int pointerMask) throws IOException {
		eventBufLen = 0;
		writeModifierKeyEvents(modifiers);

		eventBuf[eventBufLen++] = (byte) PointerEvent;
		eventBuf[eventBufLen++] = (byte) pointerMask;
		eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff);
		eventBuf[eventBufLen++] = (byte) (x & 0xff);
		eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff);
		eventBuf[eventBufLen++] = (byte) (y & 0xff);

		//
		// Always release all modifiers after an "up" event
		//

		if (pointerMask == 0) {
			writeModifierKeyEvents(0);
		}

		os.write(eventBuf, 0, eventBufLen);
	}

	void writeCtrlAltDel() throws IOException {
		final int DELETE = 0xffff;
		final int CTRLALT = VncCanvas.CTRL_MASK | VncCanvas.ALT_MASK;
		try {
			// Press
			eventBufLen = 0;
			writeModifierKeyEvents(CTRLALT);
			writeKeyEvent(DELETE, true);
			os.write(eventBuf, 0, eventBufLen);

			// Release
			eventBufLen = 0;
			writeModifierKeyEvents(CTRLALT);
			writeKeyEvent(DELETE, false);

			// Reset VNC server modifiers state
			writeModifierKeyEvents(0);
			os.write(eventBuf, 0, eventBufLen);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	//
	// Write a key event message. We may need to send modifier key events
	// around it to set the correct modifier state. Also we need to translate
	// from the Java key values to the X keysym values used by the RFB protocol.
	//
	synchronized void writeKeyEvent(int keySym, int metaState, boolean down)
			throws IOException {
		eventBufLen = 0;
		if (down)
			writeModifierKeyEvents(metaState);
		if (keySym != 0)
			writeKeyEvent(keySym, down);

		// Always release all modifiers after an "up" event
		if (!down)
			writeModifierKeyEvents(0);

		os.write(eventBuf, 0, eventBufLen);
	}

	//
	// Add a raw key event with the given X keysym to eventBuf.
	//

	private void writeKeyEvent(int keysym, boolean down) {
		eventBuf[eventBufLen++] = (byte) KeyboardEvent;
		eventBuf[eventBufLen++] = (byte) (down ? 1 : 0);
		eventBuf[eventBufLen++] = (byte) 0;
		eventBuf[eventBufLen++] = (byte) 0;
		eventBuf[eventBufLen++] = (byte) ((keysym >> 24) & 0xff);
		eventBuf[eventBufLen++] = (byte) ((keysym >> 16) & 0xff);
		eventBuf[eventBufLen++] = (byte) ((keysym >> 8) & 0xff);
		eventBuf[eventBufLen++] = (byte) (keysym & 0xff);
	}

	//
	// Write key events to set the correct modifier state.
	//

	int oldModifiers = 0;

	void writeModifierKeyEvents(int newModifiers) {
		if ((newModifiers & VncCanvas.CTRL_MASK) != (oldModifiers & VncCanvas.CTRL_MASK))
			writeKeyEvent(0xffe3, (newModifiers & VncCanvas.CTRL_MASK) != 0);

		if ((newModifiers & VncCanvas.SHIFT_MASK) != (oldModifiers & VncCanvas.SHIFT_MASK))
			writeKeyEvent(0xffe1, (newModifiers & VncCanvas.SHIFT_MASK) != 0);

		if ((newModifiers & VncCanvas.META_MASK) != (oldModifiers & VncCanvas.META_MASK))
			writeKeyEvent(0xffe7, (newModifiers & VncCanvas.META_MASK) != 0);

		if ((newModifiers & VncCanvas.ALT_MASK) != (oldModifiers & VncCanvas.ALT_MASK))
			writeKeyEvent(0xffe9, (newModifiers & VncCanvas.ALT_MASK) != 0);

		oldModifiers = newModifiers;
	}

	//
	// Compress and write the data into the recorded session file. This
	// method assumes the recording is on (rec != null).
	//

	public void startTiming() {
		timing = true;

		// Carry over up to 1s worth of previous rate for smoothing.

		if (timeWaitedIn100us > 10000) {
			timedKbits = timedKbits * 10000 / timeWaitedIn100us;
			timeWaitedIn100us = 10000;
		}
	}

	public void stopTiming() {
		timing = false;
		if (timeWaitedIn100us < timedKbits / 2)
			timeWaitedIn100us = timedKbits / 2; // upper limit 20Mbit/s
	}

	public long kbitsPerSecond() {
		return timedKbits * 10000 / timeWaitedIn100us;
	}

	public long timeWaited() {
		return timeWaitedIn100us;
	}

	public void readFully(byte b[]) throws IOException {
		readFully(b, 0, b.length);
	}

	public void readFully(byte b[], int off, int len) throws IOException {
		long before = 0;
		timing = false; // for test

		if (timing)
			before = System.currentTimeMillis();

		is.readFully(b, off, len);

		if (timing) {
			long after = System.currentTimeMillis();
			long newTimeWaited = (after - before) * 10;
			int newKbits = len * 8 / 1000;

			// limit rate to between 10kbit/s and 40Mbit/s

			if (newTimeWaited > newKbits * 1000)
				newTimeWaited = newKbits * 1000;
			if (newTimeWaited < newKbits / 4)
				newTimeWaited = newKbits / 4;

			timeWaitedIn100us += newTimeWaited;
			timedKbits += newKbits;
		}
	}

	synchronized void writeOpenChat() throws Exception {
		os.write(TextChat); // byte type
		os.write(0); // byte pad 1
		os.write(0); // byte pad 2
		os.write(0); // byte pad 2
		writeInt(CHAT_OPEN); // int message length
	}

	synchronized void writeCloseChat() throws Exception {
		os.write(TextChat); // byte type
		os.write(0); // byte pad 1
		os.write(0); // byte pad 2
		os.write(0); // byte pad 2
		writeInt(CHAT_CLOSE); // int message length
	}

	synchronized void writeFinishedChat() throws Exception {
		os.write(TextChat); // byte type
		os.write(0); // byte pad 1
		os.write(0); // byte pad 2
		os.write(0); // byte pad 2
		writeInt(CHAT_FINISHED); // int message length
	}

	String readTextChatMsg() throws Exception {
		byte[] pad = new byte[3];
		readFully(pad);
		int len = is.readInt();
		if (len == CHAT_OPEN) {
			// Remote user requests chat
			// /viewer.openChat();
			// Respond to chat request
			writeOpenChat();
			return null;
		} else if (len == CHAT_CLOSE) {
			// Remote user ends chat
			// /viewer.closeChat();
			return null;
		} else if (len == CHAT_FINISHED) {
			// Remote user says chat finished.
			// Not sure why I should care about this state.
			return null;
		} else {
			// Remote user sends message!!
			if (len > 0) {
				byte[] msg = new byte[len];
				readFully(msg);
				return new String(msg);
			}
		}
		return null;
	}

	public synchronized void writeChatMessage(String msg) throws Exception {
		os.write(TextChat); // byte type
		os.write(0); // byte pad 1
		os.write(0); // byte pad 2
		os.write(0); // byte pad 2
		byte[] bytes = msg.getBytes("8859_1");
		byte[] outgoing = bytes;
		if (bytes.length > 4096) {
			outgoing = new byte[4096];
			System.arraycopy(bytes, 0, outgoing, 0, 4096);
		}
		writeInt(outgoing.length);  // int message length
		os.write(outgoing);   // message
	}
}
时间: 2024-10-01 08:16:23

RFB协议及源码的相关文章

C#实现联通短信Sgip协议程序源码

此程序为中国联通Sgip协议程序接口,适合在中国联通申请了短信发送端口的公司使用. 短信群发已经成为现在软件系统.网络营销等必不可少的应用工具.可应用在短信验证.信息群发.游戏虚拟商品购买.事件提醒.送祝福等方面. 本程序功能包括: 1.支持中国联通Sgip1.2协议: 2.支持一般的短信发送.上行短信接收.状态报告: 3.支持长短信: 4.支持普通手机号和物联网卡: 5.全套C#.Net源码 程序适用于Sgip 1.2协议,可用.Net任何版本编译. DLL版:短信接口程序DLL源码,调用此D

微信IPAD/MAC协议《源码》全套甩手。

微信IPAD协议 6.7.2版本 ,稳定一手,现全套甩手, github体验地址: winfrom客户端:https://github.com/changtuiqie/Mac.WeChatwebapi一键部署:https://github.com/changtuiqie/Mac.WeChat.WebApi.Simple 可实现微信80%功能:支持62数据登录.扫码登录.收发朋友圈.查看朋友圈.微信建群.微信拉人进群.微信公众号阅读.微信消息收发.微信附近的人定位.微信添加好友.微信红包接收.微信

开发交通运输部&#183;车载导航系统中的第一关——硬件终端如何与服务器通信——玩转通信协议(源码下载)

一.引子与协议说明 之前开发了一个项目——车载导航系统.遇到的第一个问题就是硬件设备如何与服务器通信. 关键在于通信协议! 众所周知:要想实现通信,首先通信双方就要达成通信协议. 话不多说,且看协议:  ————————————————华丽的分割线————————————————— 以上的这些协议说明是不是看得很头大呢? 遵循如此这般的通信协议的硬件设备又如何才能与服务器以及PC顺利通信呢? 还请各位看官稍安勿躁!且听我娓娓道来! 二.基础知识-TCP与粘包 我们都知道,互联网的核心是TCP/I

JAVA上百实例源码以及开源项目

简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级.中级.高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情.执着,对IT的憧憬.向往!此时此景,笔者只专注Android.Iphone等移动平台开发,看着这些源码心中有万分感慨,写此文章纪念那时那景! Java 源码包 Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能.编辑音乐软件的朋友,这款实例会对你有所帮助.Calendar万年历 1个目标文件EJ

vlc源码分析(七) 调试学习HLS协议

HTTP Live Streaming(HLS)是苹果公司提出来的流媒体传输协议.与RTP协议不同的是,HLS可以穿透某些允许HTTP协议通过的防火墙. 一.HLS播放模式 (1) 点播模式(Video on demand, VOD) 点播模式是指当前时间点可以获取到所有index文件和ts文件,二级index文件中记录了所有ts文件的地址.这种模式允许客户端访问全部内容.上面的例子中就是一个点播模式下的m3u8的结构. (2) 直播模式(Live) 直播模式是指实时生成M3u8和ts文件.它的

基于XMPP协议的aSmack源码分析

在研究如何实现Pushing功能期间,收集了很多关于Pushing的资料,其中有一个androidnp开源项目用的人比较多,但是由于长时间没有什么人去维护,听说bug的几率挺多的,为了以后自己的产品稳定些,所以就打算自己研究一下asmack的源码,自己做一个插件,androidnp移动端的源码中包含了一个叫做asmack的jar. Reader和Writer 在asmack中有两个非常重要的对象PacketReader和PacketWriter,那么从类名上看Packet + (Reader/W

第4章2节《MonkeyRunner源码剖析》ADB协议及服务: ADB服务SERVICES.TXT翻译参考(原创)

天地会珠海分舵注:本来这一系列是准备出一本书的,详情请见早前博文"寻求合作伙伴编写<深入理解 MonkeyRunner>书籍".但因为诸多原因,没有如愿.所以这里把草稿分享出来,所以错误在所难免.有需要的就参考下吧,转发的话还请保留每篇文章结尾的出处等信息. ADB服务器端在接受到ADB客户端发送过来的命令后会进行相应的处理,如果是主机服务就在ADB服务器内部进行处理,如果是本地服务就会发送给Android目标机器端的adbd守护进程进行处理. 因为ADB相关的源代码不在我

协议的注册与维护——ndpi源码分析

在前面的文章中,我们对ndpi中的example做了源码分析.这一次我们将尽可能深入的了解ndpi内部的结构和运作.我们将带着下面三个目的(问题)去阅读ndpi的源代码. 1.ndpi内部是怎么样注册和维护需要检测的协议呢? 2.ndpi在初始化的过程中,做了怎么样的工作? 3.ndpi在底层的实现中具体又是使用怎样的数据结构? 注:这里限于篇幅,本文章指针对使用中的初始化部分进行源码分析.主体的分析函数和具体的各个协议将在后面的文中陆续介绍.如果有不正确或者理解不到位的地方,欢迎大家一起讨论.

简约之美Jodd-http--深入源码理解http协议

Jodd 是一个开源的 Java 工具集, 包含一些实用的工具类和小型框架.简单,却很强大! jodd-http是一个轻巧的HTTP客户端.现在我们以一个简单的示例从源码层看看是如何实现的? HttpRequest httpRequest = HttpRequest.get("http://jodd.org"); //1. 构建一个get请求 HttpResponse response = httpRequest.send(); //2.发送请求并接受响应信息 System.out.p