转载自:http://blog.csdn.net/songshimvp1/article/details/51481962
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每更新一个版本都会随之附加最新版源码。
- The original developers. Out in the market the longest.
- The free version lacks a lot of features which are included in other VNC clients and it not being actively developed. Not a good thing.
RealVNC provides remote control software which lets you see and interact with desktop applications across any network.
The software has a widespread user base from individuals to the largest multi-national companies. Founded by the original developers of VNC to promote, enhance and commercialise VNC.
- Const Kaplinsky’s project to improve VNC’s compression between server and viewer. Good replacement for RealVNC, but graphic and performance is sluggish sometimes.
- Does not allow remote copy and paste
- The project is being actively maintain, which is a plus.
TightVNC is a free remote control software package. With TightVNC, you can see the desktop of a remote machine and control it with your local mouse and keyboard, just like you would do it sitting in the front of that computer. TightVNC is:
* free for both personal and commercial usage, with full source code available (GPL-licensed);
* useful in remote administration, remote customer support, education, and for many other purposes;
* cross-platform, available for Windows and Unix, compatible with other VNC software.
- A project to add in file transfer, chat messaging and NTLM authentication to VNC.
- Based on experience, UltraVNC runs faster than the other two and has better graphics.
- Has file transfer, faster connections, chat interface, etc
- Allows you to copy and paste from your local computer to remote computer and vice versa
UltraVNC is a powerful, easy to use and free software that can display the screen of another computer (via internet or network) on your own screen. The program allows you to use your mouse and keyboard to control the other PC remotely. It means that you can work on a remote computer, as if you were sitting in front of it, right from your current location. If you provide computer support, you can quickly access your customer’s computers from anywhere in the world and resolve helpdesk issues remotely! With addons like SingleClick your customers don’t even have to pre-install software or execute complex procedures to get remote helpdesk support.
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) // sf@2005)
- 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); // sf@2005
- 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
- }
- }