WINVNC源码阅读(六)
VNC客户端源码
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中对应的函数。
WINVNC源码阅读(六)相关推荐
- mybatis源码阅读(六) ---StatementHandler了解一下
转载自 mybatis源码阅读(六) ---StatementHandler了解一下 StatementHandler类结构图与接口设计 BaseStatementHandler:一个抽象类,只是实 ...
- mysql 1260,MYSQL 源码阅读 六
前期节要 MYSQL源码阅读 一 MYSQL源码阅读 二 MYSQL源码阅读 三 MYSQL 源码阅读 四 MYSQL 源码阅读 五 上次有两个问题没搞明白 1 是 为什么一定要开启调试线程 ? 因为 ...
- WINVNC源码阅读(五)
VNC图像更新机制 VNC的图像更新机制核心为,桌面区域更新记录策略和更新区域通知策略.桌面更新区域记录主要是通过hooks记录桌面上变化的矩形区域,只记录更新的矩形区不记录具体更新的数据.更新区域记 ...
- Struts2源码阅读(六)_ActionProxyActionInvocation
下面开始讲一下主菜ActionProxy了.在这之前最好先去了解一下动态Proxy的基本知识. ActionProxy是Action的一个代理类,也就是说Action的调用是通过ActionProxy ...
- 源码阅读:AFNetworking(十六)——UIWebView+AFNetworking
该文章阅读的AFNetworking的版本为3.2.0. 这个分类提供了对请求周期进行控制的方法,包括进度监控.成功和失败的回调. 1.接口文件 1.1.属性 /**网络会话管理者对象*/ @prop ...
- 源码阅读:SDWebImage(六)——SDWebImageCoderHelper
该文章阅读的SDWebImage的版本为4.3.3. 这个类提供了四个方法,这四个方法可分为两类,一类是动图处理,一类是图像方向处理. 1.私有函数 先来看一下这个类里的两个函数 /**这个函数是计算 ...
- Soul 网关源码阅读(六)Sofa请求处理概览
Soul 网关源码阅读(六)Sofa请求处理概览 简介 今天来探索一下Sofa请求处理流程,看看和前面的HTTP.Dubbo有什么异同 Sofa示例运行 PS:如果请求加上参数运行不成功,请更 ...
- Soul网关源码阅读(六)请求类型探索
Soul网关源码阅读(六)请求类型探索 简介 在上几篇文章中分析了请求的处理流程,HTTP和RPC请求处理是互斥的,通过请求类型来判断,这篇文章来探索下请求类型的前世今生 源码分析 通 ...
- 【vn.py学习笔记(六)】vn.py constant源码阅读、委托生命周期
[vn.py学习笔记(六)]vn.py constant源码阅读.委托生命周期 写在前面 1 constant 1.1 Direction 1.2 Offset 1.3 Status 1.4 Prod ...
最新文章
- int/double/string使用
- C#中三种定时器对象的比较
- appium的demo编程
- redis 经纬度_原来用Redis实现查找附近的人这么容易
- python以运行效率高著称吗_几个提升Python运行效率的方法之间的对比
- 昨天订了一台FSC Lifebook S6220
- get方法请求返回一个文件_一键转换多种文件格式,完全免费,总有一个方法适合你...
- “拉勾2020年超级雇主”奖项颁布:美团、腾讯等获得“巅峰雇主”奖
- cocos2d笔记 (3)cocos2d四个基本概念
- 万娟 白话大数据和机械学习_白话大数据与机器学习 (高扬著) 带书签目录 完整pdf扫描版[71MB]...
- 医院时钟系统(卫星校时钟)设计与答疑
- ​从ASML年报看半导体产业的未来
- 简单计算器 求一元二次方程的根
- 电脑的dns服务器未响应怎么解决,电脑DNS服务器未响应怎么解决
- 关于MybatisX别名报红问题
- 消息循环中的TranslateMessage函数和DispatchMessage函数
- ICPC 沈阳M - United in Stormwind SOSDP+FWT+容斥
- 云服务器和虚拟主机的区别是什么
- 用 Python 把你的朋友变成表情包
- 全国计算机考的是ms还是mps,艺术留学读研学位分类:MA、MS、MPS 是什么学位?...
热门文章
- 安装AE报错131,Ae安装时报错误代码131
- 如何看开源项目的源代码
- react-native Image 实现placeholder占位图
- 【Unity数据持久化_Json】(二)Excel转Json
- tensorflow实现泰坦尼克号生存率预测(逻辑回归)
- 合肥达内培训php,合肥PHP开发培训班介绍PHP7是什么?
- Qt开发之画图画一把尺子入门案例项目分享
- 《算法竞赛进阶指南》0x6B T2 升降梯上
- Informix IDS 11琐屑经管(918考验)认证指南,第8部分:面向经管员的SQL特性(2)
- 数字标牌无线联网方案