介绍一种通过套接字 络编程和屏幕捕获技术实现的对远程计算机屏幕进行监视的方法。
至于用流式套接字对 络进行编程的主要过程可用下图来表示。服务器方在使用套接字之前,首先必须拥有一个Socket,可用socket()函数创建之:
sock=socket(AF_INET,SOCK_STREAM,0);
[图见文末]
其中AF_INET 和SOCK_STREAM指定了创建的是采用了TCP/IP地址族的流式套接字。该套接字实际上是提供了一个通信端口,通过这个端口可与任何一个具有套接字端口的计算机实施通信。一旦获×诵碌奶捉幼郑 α⒓赐ü齜ind()将该套接字与本机上的一个端口建立关联。需要预先对一个指向包含有本机IP地址和端口信息的sockaddr_in结构填充一些必要的信息,如本地端口 和本地主机地址等,并通过bind()将服务器进程在 络上标识出来:
sockin_s.sin_family=AF_INET;
sockin_s.sin_addr.s_addr=0;
sockin_s.sin_port=htons(PORT);
bind(sock,(LPSOCKADDR)&sockin_s,sizeof(sockin_s));
在完成接下来的listen()侦听后,需要用accept()等待接收客户端的连接,由于该函数在没有客户端进行申请连接之前会处于阻塞状态,因此需要为其单独开辟一个线程,以免影响到程序整体:
AfxBeginThread(Server,NULL);//创建一个新的线程
……
UINT Server(LPVOID lpVoid)
{
CSurveillant_ServerView* pView=((CSurveillant_ServerView*)((CFrameWnd*)
AfxGetApp()->m_pMainWnd)->GetActiveView());
int nLen=sizeof(SOCKADDR);
pView->newskt_s= accept(pView->sock,(LPSOCKADDR)& pView->sockin_s,(LPINT)& nLen);
WSAAsyncSelect(pView->newskt_s,pView->m_hWnd,WM_MSG,FD_CLOSE);
pView->SetTimer(0,2500,NULL);
return 1;
}
在这里通过WSAAsyncSelect()异步选择函数来以异步的形式响应关心的 络事件FD_CLOSE,并在该事件发生时发出自定义WM_MSG 消息,通过响应这个消息可以得之当前与服务器联系的客户机程序已关闭退出,由于服务器部分是运行于远程工程现场的,为了使控制中心的监控程序(客户)在下次发出监控请求时能为其提供服务需要在WM_MSG的消息响应函数里关闭由accept() 所产生的新的套接字newskt_s,并重新启动该线程等待监控程序的再次连接。在accept()函数成功返回后,就可以在定时器响应函数里用send () 函数与之建立了连接的监控主机定时发送捕获的远程屏幕信息了。
作为客户的监控程序,其实现过程要比服务器简单许多。由于需要接收数据,因此在异步选择函数中需要设定待监测的 络事件为FD_CLOSE和FD_READ。在消息响应函数中可以通过对消息参数的低位字节进行判断而区分出具体发生是何种 络事件,并对其做出响应的反应。下面是监控端程序 络部分的主要代码:
……
IPaddr=inet_addr(strIP);
sock=socket(AF_INET,SOCK_STREAM,0); //创建套接字
sockin_c.sin_family=AF_INET;
sockin_c.sin_addr.S_un.S_addr=IPaddr;
sockin_c.sin_port=m_Port;
connect(sock,(LPSOCKADDR)&sockin_c,sizeof(sockin_c));//连接服务器
……
WSAAsyncSelect(sock,m_hWnd,WM_MSG,FD_READ|FD_CLOSE);
……
通过异步选择函数的设定,在有数据到达时会由FD_READ触发WM_MSG消息,并在处理函数中通过调用recv ()将远程主机的屏幕信息从 络接收到缓存,并完成在本地机的重显。通过以上几步,已经初步具备了在远程服务器和本地客户机之间的 络通讯能力,可以完成屏幕信息的 络传送任务。
对远程计算机屏幕的捕捉和显示
char dot[1572864]; //1024*768*2
CBitmap bmp; //内存位图
CDC wdc; //设备环境
CDC* pDC; //指向桌面窗口的设备环境指针
……
static CWindowDC ddc(GetDesktopWindow()); //引用桌面窗口指针定义对象ddc
pDC=&ddc; //将指针pdc指向ddc
wdc.CreateCompatibleDC(pDC); //建立与ddc兼容的设备环境
bmp.CreateCompatibleBitmap(pDC,1024,768); //建立与ddc兼容的位图
wdc.SelectObject(&bmp); //选择bmp
……
wdc.BitBlt(0,0,1024,768,pDC,0,0,SRCCOPY); //把桌面图像复制到wdc的bmp中
这时虽以获取到了屏幕的信息,并将其复制到内存位图之中,但此时还不能直接将其发送出去,需要调用CBitmap 类的成员函数GetBitmapBits()来将图像信息从内存位图拷贝到缓存,并通过套接字的send()函数将缓存中存放的屏幕信息通过 络从现场主机发送到控制中心。
现场主机的屏幕信息在控制中心的再现,基本上是屏幕截取的逆过程:先建立一个同客户区相关的设备环境并建立一个与之兼容的设备环境,然后按位图格式在内存中创建一个与之兼容的内存位图。在从 络接收完一屏信息后,通过CBitmap的成员函数 SetBitmapBits()把缓存中的屏幕信息按位图格式拷贝到内存位图,最后完成对内存位图的显示。其主要过程如下:
CDC* pDC=GetDC(); //引用用户窗口指针定义对象pDC
wdc.CreateCompatibleDC(pDC); //建立与pDC兼容的device context
bmp.CreateCompatibleBitmap(pDC,1024,768); //建立与pDC兼容的位图
wdc.SelectObject(&bmp);
……
iReadLen = recv(sock,buffer,60000,0); //从 络接收数据
for(i=0;i
dot[pointer]=buffer;
pointer++;
if(pointer==1572864) //判断接收到的信息是否已满一屏
{
GetClientRect(&rect);
bmp.SetBitmapBits(1572864,(LPVOID)dot); //把内存数据复制到bmp中
//把bmp中图像复制到用户窗口中
pDC->StretchBlt(0,0,rect.Width(),rect.Height(),&wdc,0,0,1024,768,SRCCOPY);
pointer=0; //接收完一屏后指针复位,准备接收下一屏
}
}
服务程序的自动加载及扩展
从功能上看,服务端程序只负责为远程客户提供服务,在全部运行期间根本不需要人为的外来干预,因此可以隐藏其界面并将其作成后台服务程序:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
……
cs.cx=200;
cs.cy=10;
cs.style=WS_POPUP;
cs.dwExStyle|=WS_EX_TOOLWINDOW;
return TRUE;
}
小结:
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!