艰难困苦,玉汝于成,经过多天的阅读、调试原代码,现今终于理清了一些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()。
阅读(1332) | 评论(0) | 转发(0) |