1.检测VNC是否安装
输入命令:rpm -q vnc-server,返回信息如下:
[root@wasServer ~]# rpm -q vnc-server
vnc-server-4.1.2-14.el5_3.1
已安装。
如果未安装,可用如下命令安装:
rpm -ivh /mnt/Server/vnc-server-4.1.2-9.el5.x86_64.rpm
2.设置 vncserver 开机自启动 # chkconfig vncserver on
3.启动VNC服务
# /etc/init.d/vncserver start /启动
4.启动vnc窗口1并设置密码为:
[root@testdb ~]# vncserver :1
password:
vnc#@!
3.修改配置文件,使其在 VNC 登录时调出图形界面。
# vi /root/.vnc/xstartup
#!/bin/sh
# Uncomment the following two lines for normal desktop: # unset SESSION_MANAGER # exec /etc/X11/xinit/xinitrc
[ ‐x /etc/vnc/xstartup ] && exec /etc/vnc/xstartup
[ ‐r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot ‐solid grey
vncconfig ‐iconic &
xterm ‐geometry 80x24+10+10 ‐ls ‐title "$VNCDESKTOP Desktop" &
#twm & //注释该行
gnome-session & /添加该行
保存退出后 需重新启动 vncserver服务使其生效,并杀掉启动的VNC第一个session。 vncserver -kill :1
重新启动vnc窗口1:vncserver :1,OK
4.VNC服务使用的端口号与桌面号的关系 -------------------------------------------------------------
VNC使用TCP端口从5900开始:
桌面号为“1” ---- 端口号为5901
桌面号为“2” ---- 端口号为5902
桌面号为“3” ---- 端口号为5903
基于Java的VNC客户程序Web服务TCP端口从5800开始,也是与桌面号相关,对应关系如下
桌面号为“1” ---- 端口号为5801
桌面号为“2” ---- 端口号为5802
桌面号为“3” ---- 端口号为5803
5.测试VNC服务是否通过了。
10.16.255.132:5901
第一种方法是使用VNC Viewer软件登陆测试,操作流程如下
启动VNC Viewer软件 --> Server输入“192.168.0.3:1” --> 点击“OK” --> Password输入登陆密码 --> 点击“OK”登陆到X-Window图形桌面环境 --> 测试成功
第二种方法是使用Web浏览器(如Firefox,IE,Safari)登陆测试,防火墙限制。
第二篇:vnc代码
vnc
win-hook + event-simulate + gdi-bitmap-capture(delay send) + libjpeg + socket
*******************************************************************************************************************
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->wm_hooks.setUpdateTracker(this);
4.把SDisplay中记录的数据传给VNCServerST 对象 core->using_hooks =
在 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;
}
}
Poll机制:客户端通过发送更新请求,请求更新某一个区域。
第一步:读取到一个更新某一个区域的请求
void SMsgReaderV3::readMsg()
{
case msgTypeFramebufferUpdateRequest: readFramebufferUpdateRequest(); break;
}
第二步:调用网络事件处理对象handler处理事件
void SMsgReader::readFramebufferUpdateRequest()
{
bool inc = is->readU8();
int x = is->readU16();
int y = is->readU16();
int w = is->readU16();
int h = is->readU16();
endMsg();
handler->framebufferUpdateRequest(Rect(x, y, x+w, y+h), inc);//handler 为 SMsgHandler* handler指针 是指向VNCSConnectionST对象
}
第三步:
void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
{
if (!(accessRights & AccessView)) return;
SConnection::framebufferUpdateRequest(r, incremental);
Region reqRgn(r);
requested.assign_union(reqRgn);
if (!incremental) {
updates.add_changed(reqRgn);
server->comparer->add_changed(reqRgn);
}
writeFramebufferUpdate();
}
第四步: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;
}
}