VNC图像更新机制

VNC的图像更新机制核心为,桌面区域更新记录策略和更新区域通知策略。桌面更新区域记录主要是通过hooks记录桌面上变化的矩形区域,只记录更新的矩形区不记录具体更新的数据。更新区域记录步骤大致如下:1.wm_hooks截获桌面变化的相关消息,并转化为自定义的消息发送给WMHooksThread线程处理。 2. WMHooksThread 中用SimpleUpdateTracker new_changes记录新的更新区域.3.把SimpleUpdateTracker new_changes更新拷贝到SDisplay中。4.每次要发送桌面更新的时候,把SDisplay中记录的更新区域传给VNCServerST 对象中。更新区域的通知主要有poll和push两种机制。push是服务器每隔10ms检查有没有更新,如果有更新则主动把更新推送给客户端,poll机制则是客户端主动请求更新,客户端通过发送framebufferupdate请求某一个区域更新,服务器处理该消息发送相应的更新。详细分析如下:
1.Wm_hooks截获消息并转化为自定义的消息发送给WMHooksThread线程处理。
Wm_hooks自定义的消息:
UINT WM_HK_WindowChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowChanged"));
UINT WM_HK_WindowClientAreaChanged = UINT WM_HK_WindowBorderChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowBorderChanged"));
UINT WM_HK_RectangleChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.RectangleChanged"));
UINT WM_HK_CursorChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.CursorChanged"));
钩子截获到消息以后,把它转化为自定义的消息,然后发送给WMHooksThread线程处理,消息转化如下:
边框更新消息:WM_NCPAINT,WM_NCACTIVATE
客户区域更新消息:BM_SETCHECK, BM_SETSTATE,EM_SETSEL,WM_CHAR,WM_ENABLE,WM_KEYUP,WM_LBUTTONUP,WM_MBUTTONUP,WM_PALETTECHANGED,WM_RBUTTONUP,WM_SYSCOLORCHANGE,WM_SETTEXT。
窗口改变消息:WM_HSCROLL,WM_VSCROLL,482,485。
矩形区更新消息:WM_DESTROY
窗口客户区消息:WM_PAINT
鼠标消息:WM_NCMOUSEMOVE,WM_MOUSEMOVE
2 . WMHooksThread 中用SimpleUpdateTracker new_changes记录新的更新区域
WMHooksThread::run() 函数中先判断出矩形区域改变的大小,然后调用NotifyHooksRegion(const Region& r)把改变的区域记录到SimpleUpdateTracker new_changes中。
NotifyHooksRegion(const Region& r) {
Lock l(hook_mgr_lock);
std::list<WMHooks*>::iterator i;
for (i=hooks.begin(); i!=hooks.end(); i++) {
    (*i)->new_changes.add_changed(r);
    if (!(*i)->notified) {
      (*i)->notified = true;
      PostMessage((*i)->getHandle(), WM_USER, 0, 0); // 把消息通知到clipper见下面一个处理函数
    }
}
}
3.把更新区域拷贝到SDisplay中
rfb::win32::WMHooks::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_USER:
    {
      Sleep(0);
      Lock l(hook_mgr_lock);
      notified = false;
      new_changes.get_update(*clipper); //把更新通知到clipper中
      new_changes.clear();
    }
    break;
}
return MsgWindow::processMessage(msg, wParam, lParam);
}
Cliper在下面设置
rfb::win32::WMHooks::setUpdateTracker(UpdateTracker* ut) {
if (clipper) delete clipper;
clipper = new ClippedUpdateTracker(*ut);
clipper->set_clip_region(clip_region);
return AddHook(this);
}
UpdateTracker* ut 为void SDisplay::start(VNCServer*vs)中设置 core->using_hooks = core->wm_hooks.setUpdateTracker(this);
4.把SDisplay中记录的数据传给VNCServerST 对象
在 SDisplay::processEvent(HANDLE event) {
try_update = flushChangeTracker() || try_update; //把变化的区域拷贝到VNCServerST中
      if (try_update)
        server->tryUpdate();          //把更新发送给服务器
}
flushChangeTracker()实现如下:
bool SDisplay::flushChangeTracker() {
if (change_tracker.is_empty())
    return false;
change_tracker.translate(screenRect.tl.negate());
change_tracker.get_update(*server); //server 实际指向VNCServerST 对象该函数把SDisplay中的更新拷贝到VNCServerST中。
change_tracker.clear();
return true;
}
两种数据更新方式:Push机制和Pull机制
Push:
SdisplayCore 中IntervalTimer cursorTimer定时器,每隔10ms尝试着检查一下是否有更新,如果有更新就发送更新给客户端。
第一步:
LRESULT SDisplayCore::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
case TIMER_CURSOR:
     display->triggerUpdate(); //SDisplay* display;
}
第二步:
void SDisplay::triggerUpdate() {
     if (core)
         SetEvent(updateEvent); //使事件对象为受信状态
}
第三步:
SDisplay::processEvent(HANDLE event) {
     if (event == updateEvent) {
         if (try_update)
                   server->tryUpdate(); // VNCServer* server指针 指向子类VNCServerST
}
}
第四步:向每一个连接的客户端发送更新
void VNCServerST::tryUpdate()
{
     std::list<VNCSConnectionST*>::iterator ci, ci_next;
     for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
         ci_next = ci; ci_next++;
         (*ci)->writeFramebufferUpdateOrClose();
     }
}
第五步:
void VNCSConnectionST::writeFramebufferUpdateOrClose()
{
     try {
         writeFramebufferUpdate();
     } catch(rdr::Exception &e) {
         close(e.str());
     }
}
第六步:SimpleUpdateTracker updates对象记录更新的区域,如果屏幕有更新则发送更新
void VNCSConnectionST::writeFramebufferUpdate(){
if (!update.is_empty() || writer()->needFakeUpdate() || drawRenderedCursor) {
         int nRects = update.numRects() + (drawRenderedCursor ? 1 : 0);
         writer()->writeFramebufferUpdateStart(nRects);
         Region updatedRegion;
         writer()->writeRects(update, &image_getter, &updatedRegion); // SmsgWriter *
         updates.subtract(updatedRegion);
         if (drawRenderedCursor)
              writeRenderedCursorRect();
         writer()->writeFramebufferUpdateEnd();
         requested.clear();
     }
}
第七步:利用RFB协议发送更新
void SMsgWriterV3::writeFramebufferUpdateStart(int nRects)
{
     startMsg(msgTypeFramebufferUpdate);
     os->pad(1);
     if (wsccb) nRects++;
     if (needSetDesktopSize) nRects++;
     os->writeU16(nRects);
     nRectsInUpdate = 0;
     nRectsInHeader = nRects;
     if (wsccb) {
         wsccb->writeSetCursorCallback();
         wsccb = 0;
     }

WINVNC源码阅读(五)相关推荐

  1. mybatis源码阅读(五) ---执行器Executor

    转载自  mybatis源码阅读(五) ---执行器Executor 1. Executor接口设计与类结构图 public interface Executor {ResultHandler NO_ ...

  2. WINVNC源码阅读(六)

    VNC客户端源码 Windows版本的VNC客户端源码阅读笔记. while (!hosts.empty()) { char* hostinfo = hosts.front(); Thread* co ...

  3. Spark源码阅读(五) --- Spark的支持的join方式以及join策略

    版本变动 2021-08-30 增加了对Broadcast Hash Join小表大小的评估内容 增加了对Sort Merge Join优于Shuffle Hash Join调用的解释 目录 Spar ...

  4. Struts2源码阅读(五)_FilterDispatcher核心控制器

    Dispatcher已经在之前讲过,这就好办了.FilterDispatcher是Struts2的核心控制器,首先看一下init()方法. public void init(FilterConfig ...

  5. redis源码阅读-持久化之RDB

    持久化介绍: redis的持久化有两种方式: rdb :可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot) aof : 记录redis执行的所有写操作命令 根 ...

  6. redis源码阅读-zset

    前段时间给小伙伴分享redis,顺带又把redis撸了一遍了,对其源码,又有了比较深入的了解.(ps: 分享的文章再丰富下再放出来). 数据结构 我们先看下redis 5.0的代码.本次讲解主要是zs ...

  7. redis源码阅读-持久化之aof与aof重写详解

    aof相关配置 aof-rewrite-incremental-fsync yes # aof 开关,默认是关闭的,改为yes表示开启 appendonly no # aof的文件名,默认 appen ...

  8. mysql 1260,MYSQL 源码阅读 六

    前期节要 MYSQL源码阅读 一 MYSQL源码阅读 二 MYSQL源码阅读 三 MYSQL 源码阅读 四 MYSQL 源码阅读 五 上次有两个问题没搞明白 1 是 为什么一定要开启调试线程 ? 因为 ...

  9. Alibaba Druid 源码阅读(五)数据库连接池 连接关闭探索

    Alibaba Druid 源码阅读(五)数据库连接池 连接关闭探索 简介 在上文中探索了数据库连接池的获取,下面接着初步来探索下数据库连接的关闭,看看其中具体执行了那些操作 连接关闭 下面的具体的代 ...

最新文章

  1. 初涉SQL Server性能问题(1/4):服务器概况
  2. 希尔排序python实现
  3. IA-32 Intel手册学习笔记(二)保护模式下的内存管理
  4. jQuery --- 简单操作合集
  5. dispatch的action带参数
  6. Ubuntu18.04TLS运行linux版百度网盘客户端的问题
  7. 配置maven的settings文件
  8. Android Studio 安装记录
  9. LeetCode 简单算法题
  10. 梦幻风图片?我用Python分分钟做出来!!
  11. 【技能教学】如何通过FFMPEG编码推RTSP视频直播流到EasyDarwin开源平台时叠加时间水印?
  12. 盘点那些Wifi破解姿势(2)
  13. 大力哥谈 DALI - DALI 调光电源怎么用
  14. 美团一面--后台开发
  15. 基于区块链的内容社交平台,他们凭什么说比知乎更吸引人?
  16. neo4j图数据库安装实践与报错解决
  17. 生命不可承受之重, 请关爱过劳人员
  18. #1045 无法登录 MySQL 服务器(实际上是我第一次使用,不知道密码)
  19. iframe载入完成时的事件监听
  20. 针对WM6.5系统和软件的注册表修改以及优化

热门文章

  1. 一位久经沙场的嵌入式er站在初学者角度谈谈嵌入式开发与学习的一些问题
  2. 计算机网络安全等级可以划分为几级,网络安全级别划分为几个等级?
  3. js-export2Excel.js-带图片excel生成器
  4. 01触摸屏_Project Manager的设定和下载方式视频教程_EB8000
  5. pdm生成mysql sql语句_PDM文件生成sql执行语句
  6. 详解GPFS文件系统架构、组网和Building Block。
  7. linux用rpm包装ftp,linux以rpm方式安装ftp软件
  8. 补发DAL层SQLHelper
  9. 树莓派 cuda加速_机器人梦系列-树莓派资源整理汇总
  10. python 股票自动交易助手_hikyuu: 基于C++/Python的开源量化交易研究框架