Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1132818
  • 博文数量: 222
  • 博客积分: 5262
  • 博客等级: 大校
  • 技术积分: 3028
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-22 19:10
文章分类

全部博文(222)

文章存档

2012年(2)

2011年(192)

2010年(28)

分类: 嵌入式

2010-12-25 18:42:55

VNC源码阅读笔记
2010-03-26 21:08

我阅读的是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中对应的函数。

待续……

阅读(4077) | 评论(0) | 转发(0) |
0

上一篇:BMP格式详解

下一篇:TightVNC分析文档

给主人留下些什么吧!~~