我阅读的是VNC官方下载的4.1.3版本的源码。下面是Windows版本的VNC客户端源码阅读笔记。
while (!hosts.empty()) { char* hostinfo = hosts.front(); Thread* connThread = new CConnThread(hostinfo);//创建一个连接线程 strFree(hostinfo); hosts.pop_front(); }
跟进去CConnThread的构造类,
CConnThread::CConnThread(const char* hostOrConfig_, bool isConfig_) : Thread("CConnThread"), hostOrConfig(strDup(hostOrConfig_)), isConfig(isConfig_), sock(0), reverse(false) { vlog.info("CConnThread (host/port)"); setDeleteAfterRun(); Lock l(threadsLock);//线程同步锁,在CConnThread构造完成时l自动析构,析构时退出临界区。 threads.insert(this); start(); }
其中Lock l(Mutex &)是一个锁,对该定义以后的操作加互斥,直到程序块结束l被析构时调用~Lock自动解锁。CConnThread由
Thread派生,跟进去Thread的构造类,可以看到构造类里面基于threadProc创建了一个线程。线程创建后是挂起的。其中threadProc就是
关键线程。在完成父类Thread的构造后,CConnThread的构造函数中把新建的线程插入线程表中,然后调用CConnThread::start()唤醒刚
创建的线程。
Thread::Thread(const char* name_) : name(strDup(name_ ? name_ : "Unnamed")), sig(0), deleteAfterRun(false) { sig = new Condition(mutex); cond_event.h = CreateEvent(NULL, TRUE, FALSE, NULL); thread.h = CreateThread(NULL, 0, threadProc, this, CREATE_SUSPENDED, &thread_id); state = ThreadCreated; logAction(this, "created"); } Thread::threadProc(LPVOID lpParameter) { Thread* thread = (Thread*) lpParameter; TlsSetValue(threadStorage, thread); logAction(thread, "started"); try { thread->run();//线程核心执行函数,虚函数,连接线程在CConnThread中实现 logAction(thread, "stopped"); } catch (rdr::Exception& e) { logError(thread, e.str()); } bool deleteThread = false; { Lock l(thread->mutex); thread->state = ThreadStopped; thread->sig->signal(); deleteThread = thread->deleteAfterRun; } if (deleteThread) delete thread; return 0; }
Thread::start() { Lock l(mutex); if (state == ThreadCreated) { state = ThreadStarted; sig->signal(); ResumeThread(thread); } }
CConnThread中创建了一个CConn类。CConn类继承于CConnection类,是CConnection类的Windows实现类。CConn初始化连接线程
后,调用processMsg()函数来处理消息。
conn.initialise(sock, reverse); while (!conn.isClosed()) { try { conn.getInStream()->check(1,1); conn.processMsg(); } ... }
processMsg()是父类CConnection的一个方法函数,收到的信息依次是版本信息,安全认证信息,窗口初始化信息,初始化完成
后,主要调用reader_->readMsg()来读取与服务器的信息。
void CConnection::processMsg() { switch (state_) {
case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break; //RFB版本 case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break; //加密类型 case RFBSTATE_SECURITY: processSecurityMsg(); break; //密码认证 case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;//认证结果 case RFBSTATE_INITIALISATION: processInitMsg(); break; //初始化窗口 case RFBSTATE_NORMAL: reader_->readMsg(); break; //读取服务端的信息 case RFBSTATE_UNINITIALISED: throw Exception("CConnection::processMsg: not initialised yet?"); default: throw Exception("CConnection::processMsg: invalid state"); } }
对着RFB的协议文档来看,服务端的信息类型如下:
Number Name 0 FramebufferUpdate 1 SetColourMapEntries 2 Bell 3 ServerCutText
用的最多的是,FramebufferUpdate,其消息格式如下:
No. of bytes Type [Value] Description 1 U8 0 message-type 1 padding 2 U16 number-of-rectangles
帧缓存更新消是以矩形为单位来传输更新数据的,紧接着更新包头之后是个number-of-rectangles个矩形的数据,每个矩形可以
用不同的编码来传输。每个矩形的描述如下(矩形描述之后接的就是实际的图像内容的对应的编码数据):
No. of bytes Type [Value] Description 2 U16 x-position 2 U16 y-position 2 U16 width 2 U16 height 4 S32 encoding-type
对着协议看代码,很容易就可以理解处理流程。
void CMsgReaderV3::readMsg() { if (nUpdateRectsLeft == 0) {
int type = is->readU8(); switch (type) { case msgTypeFramebufferUpdate: readFramebufferUpdate(); break; case msgTypeSetColourMapEntries: readSetColourMapEntries(); break; case msgTypeBell: readBell(); break; case msgTypeServerCutText: readServerCutText(); break; default: fprintf(stderr, "unknown message type %d\n", type); throw Exception("unknown message type"); }
} else {
int x = is->readU16(); int y = is->readU16(); int w = is->readU16(); int h = is->readU16(); unsigned int encoding = is->readU32();
switch (encoding) { case pseudoEncodingDesktopSize: handler->setDesktopSize(w, h); break; case pseudoEncodingCursor: readSetCursor(w, h, Point(x,y)); break; default: readRect(Rect(x, y, x+w, y+h), encoding); break; };
nUpdateRectsLeft--; if (nUpdateRectsLeft == 0) handler->framebufferUpdateEnd(); } }
VNC的核心就是分块编码传输显示,下面的代码就是读取矩形数据的代码。CMsgReader是一个读取并处理收到的消息的类。
CMsgReader有个Decoder指针数组decoders。Decoder是一个解码类,类中有方法对VNC服务端传来不同编码的矩形数据解码。decoders保
存着各种编码对应的解码器。当消息是矩形区域更新的时候,会调用decoders中对应的解码器的Decoder::readRect()来解码。如果对应
的decoders数组没有保存解码器,就先调用Decoder::createDecoder()来生成解码器。
void CMsgReader::readRect(const Rect& r, unsigned int encoding) { if ((r.br.x > handler->cp.width) || (r.br.y > handler->cp.height)) { fprintf(stderr, "Rect too big: %dx%d at %d,%d exceeds %dx%d\n", r.width(), r.height(), r.tl.x, r.tl.y, handler->cp.width, handler->cp.height); throw Exception("Rect too big"); }
if (r.is_empty()) fprintf(stderr, "Warning: zero size rect\n");
handler->beginRect(r, encoding);
if (encoding == encodingCopyRect) { readCopyRect(r); } else { if (encoding > encodingMax) throw Exception("Unknown rect encoding"); if (!decoders[encoding]) { decoders[encoding] = Decoder::createDecoder(encoding, this); if (!decoders[encoding]) { fprintf(stderr, "Unknown rect encoding %d\n", encoding); throw Exception("Unknown rect encoding"); } } decoders[encoding]->readRect(r, handler); }
handler->endRect(r, encoding); }
Decoder类中有些代码比较晦涩。
typedef Decoder* (*DecoderCreateFnType)(CMsgReader*); ...... static DecoderCreateFnType createFns[encodingMax+1]; 阅读这段代码需要对typedef了解比较好,下面这个网站会有较大帮助。typedef的四个用途和两个陷阱:
DecoderCreateFnType是一个函数指针,指向的函数的参数是指向CMsgReader的指针,返回值是指向Decoder的指针。 createFns是一个静态的指针数组。每一个成员用于生成一个与序号对应的解码器。createDecoder使用的就是createFns中对应的函数。
待续…… |