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

全部博文(222)

文章存档

2012年(2)

2011年(192)

2010年(28)

分类: C/C++

2011-03-01 17:50:12

   艰难困苦,玉汝于成,经过多天的阅读、调试原代码,现今终于理清了一些winvnc的流程,主要分析服务器端抓取屏幕数据,等待客户端请求数据更新,发送更新数据的过程,现在记录于此,以备今后查阅。
  首先,winvnc服务器端运行有7个线程:
   1.winvnc主线程,主要负责启动时的初始化和管理线程工作,在winvnc.cpp文件中。
   2.vncclient线程,如果有客户端连接,则创建此线程。负责和客户端的连接细节处理。
   3.vncbuffer线程,用来存储本地framerbuffer数据。
   4.vncdesktopthread线程,负责处理本地framebuffer数据到desktop桌面和更新的工作。
   5.timer线程,定时器。
   6.socket recv线程,负责接收网络数据。
   7.socket send线程,负责发送网络数据。
  我们主要分析vncclient和vncdesktopthread线程,这两个线程的核心都是run_undetached函数。
 (一)vncclient线程的run_undetached函数:
      设置一个while(1)死循环来循环处理连接数据。
    1.用互斥锁锁住本循环,防止临界资源被多个线程读取和修改:omni_mutex_lock l(m_client->GetUpdateLock());
    2.等待别的线程更新数据完毕,或客户端暂时没有更新请求。
    3.发送屏幕参数,在新的更新请求到来之前。包括屏幕大小,buffer和desktop的宽度和高度。
    4.获取调色面板、需要更新的矩形区域、剪贴板、update tracker,渲染鼠标。
    5.发送剪贴板。
    6.发送颜色面板。
    7.加入检查过程,以解决可能发生的同步错误问题。
    8.发送一个更新数据。此处检查了屏幕的大小、宽度和高度是否等于buffer的。然后调用m_client->SendUpdate(update)正式发送更新数据。
   追踪SendUpdate(update)进入此函数:
   1.查看是否有数据发送。
   2.发送新的屏幕大小参数。
   3.查看有多少矩形要被更新。
   4.发送更新数据的缓存。
   5.开始计时。
   6.发送rfb协议数据(更新数据的头部)。
   7.处理光标。
   8.发送拷贝矩形。
   9.发送矩形缓存。
   10.发送多个矩形数据。
   11.发送最后一个矩形。
   12.清除发送队列。
   我们发现此函数主要的时间都花在第10步,发送多个矩形数据上。继续跟踪:
   进入SendRectangles函数。
   1.用互斥锁锁住本次处理过程。
   2.for循环,调用SendRectangle(*i)发送单个矩形数据。
   进入SendRectangle(*i)函数:
   1.创建矩形对象,用来填充数据。
   2.初始化矩形对象,长、宽。
   3.是否启用DSM机制。进入不同的分支。
   4.两个分支都调用m_socket->SendExactQueue((char*)&Size, sizeof(CARD32));来发送矩形数据和大小。
   继续跟进。进入SendExactQueue函数,发现此函数调用SendQueued(pBuffer, nBufflen);函数,跟进。
最终来到SendQueued(pBuffer, nBufflen);发送矩形队列函数,终于顺藤摸瓜摸到了瓜。
   发现此函数调用windows的socket api函数send(sock,queuebuffer,G_SENDBUFFER,0);来发送矩形数据。我们终于见到了它的庐山真面目。但是我从打印出来的数据看到在此函数阻塞了,为什么呢?我们来看看第二部分。
   (二)vncdesktopthread线程的run_undetached函数:
    跳过前面一大批的初始化过程,我们来到的令人兴奋的while函数:
    while (looping && !fShutdownOrdered)当轮询过程开关打开,并且没有关闭本次处理流程。(真是绕啊)
    1.计算更新的节拍,等待更新事件的到来。
    2.休眠一段时间,限制每秒最多处理30帧的数据。
    3.检查视频播放缓冲有无更新。(我们最关心的地方)
    4.是否加载hook到本程序。
    5.检查屏幕有无更新。
    6.获取当前窗口的位置。
    7.处理光标信息。
    8.客户端是否有更新请求,如果有则进入下面的处理,无则回到while循环。(关注点,耗时点)
    9.轮询或者驱动模式。
    10.处理鼠标信息。
    11.捕捉屏幕。(关注点)
    12.扫描整个屏幕以确定有哪些区域改变了。
    13.从其他的更新移除拷贝矩形。
    14.确保拷贝矩形被检查过,在下一次更新之前。
    15.选择所有改变的区域,并且保存起来。这是非常耗时的,仅执行一次。
    16.触发更新触发器,m_server->GetUpdateTracker()。
    17.清除更新触发器,clipped_updates.clear()。
阅读(1296) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~