民航中南空管局气象中心数据库室 欧壮杰
我们知道,在unix操作系统中,远程主机命令行程序的运行和进程的关闭可通过telnet来实现,当客户机装有x-windows时,可以运行远程主机图形界面的程序且界面显示在客户机上,相当于延长了远程主机显示屏的距离。但windows产品在windows2000以前只有一个叫“八爪鱼”的不成熟第三方产品可实现该功能。随着windows2000的发布,终端服务功能成为windows2000的一个亮点,只要在服务器端和客户端安装上相应的程序,就可以实现远程桌面的功能。但是对于要实时监控通过慢速的DDN专线连接的远程主机上的程序,则终端服务占用了太多的带宽。因此,我们采用自己编程序的方法,用较少的传输量就可以实时监控远程主机的程序。 我们的设计方法是:在服务器端运行一个实时进程监控程序,定时读取服务器进程的运行情况;在客户端运行一个终端程序,通过服务器端的进程监控程序把服务器的进程运行情况在终端显示出来,并可在终端发送指令指示服务器启动和停止特定的进程,甚至重启远程主机。 1、服务器端进程监控程序 在windows2000 和windows 95以上的版本中,Microsoft 提供了一套工具帮助函数(Tool Help),该套函数用于获得当前系统中运行的进程、堆、模块及进程使用的线程的快照集。在windows nt 4.0中是没有提供。但我们现在的远程主机都是安装了windows 2000,因此可在上面运行该套函数,下面是例子(采用delphi 语言): procedure TForm1.Button1Click(Sender: TObject); var i : integer; ContinueLoop:BOOL; FSnapshotHandle:THandle; FProcessEntry32:TProcessEntry32; begin FSnapshotHandle:=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); FProcessEntry32.dwSize:=Sizeof(FProcessEntry32); file://指定结构的大小 ContinueLoop:=Process32First(FSnapshotHandle,FProcessEntry32); while integer(ContinueLoop)<>0 do begin Demo1.Lines.Add(inttostr(FProcessEntry32.th32ProcessID)+ ':'+FProcessEntry32.szExeFile); ContinueLoop:=Process32Next(FSnapshotHandle,FProcessEntry32); end; CloseHandle (FSnapshotHandle); end; 程序中首先调用CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0)函数来获得当前进程快照集的打开句柄。 TProcessEntry32是一个进程入口结构,它用于存储所扫描到的进程信息,如进程号、进程可执行文件名字等。第二步就是调用Process32First(FSnapshotHandle,FProcessEntry32)函数来寻找第一个进程,当返回值ContinueLoop为True时说明已找到进程;而Process32Next(FSnapshotHandle,FProcessEntry32)用于取得记录在系统快照集里的下一个进程信息。当返回值为False为说明列举已完成。TProcessEntry32结构的th32ProcessID属性记录了当前进程的进程号;szExeFile属性记录了当前进程的可执行文件名字;获得每一进程的这两个信息,就可以知当前系统的进程运行情况。 当客户端软件提出浏览远端进程要求时,服务器端可运行该段程序并把获得的信息发给客户端。 再有,在windows2000下,可执行GetProcessTimes函数来获得该进程以内核模式已经执行的时间和以用户模式已经执行的时间,这样我们就可大概了解该进程对CPU的占用情况。以便及时采取相应的措施。 当获得服务器端进程后,我们在客户端可发出指令,让服务器端程序终止某一进程或启动某一程序,用TerminateProcess函数可结束指定的进程及其所拥有的线程,该函数使得一个进程中的所有线程都终止,且引起该进程退出,但进程终止的消息不通知给附加的动态连接库DLL,因此使用该函数不能太频繁。 我们还可以在服务器端截取屏幕,并把屏幕存为jpg格式图像后以数据流的方式发送给客户端。 另外,为了监控服务器上的进程所占用的内存资源,我们可利用 GetProcessMemoryInfo函数来获得当前进程所占用内存的大小,例子如下: procedure TForm1.Button1Click(Sender: TObject); var hd:HWND; dw:Dword; PMC: PPROCESS_MEMORY_COUNTERS; begin dw:=4294548877;//假设该数值为某一进程的进程号 hd:=OpenProcess(PROCESS_TERMINATE,FALSE,dw); file://获得进程的句柄 PMC.cb:=sizeof(PMC); if GetProcessMemoryInfo(hd, PMC, PMC.cb) then begin file://调用PMC.WorkingSetSize来获得所占用内存的大小 end; CloseHandle(hd); end; 需要注意的是GetProcessMemoryInfo函数只在windows nt/2000 下可用,在windows98中没有函数可获得进程内存的大小,另外要在uses部分引用PsAPI单元,里面有GetProcessMemoryInfo函数引用说明。 2、客户端程序 客户端的程序比较简单,主要功能是要实现指令的发送和信息的接收。 3、程序间的通信 我们采用socket套接字来实现程序间的通信,在服务器端运行一Socket服务器序,监听来自客户端的连接和接受指令。在delphi中,复杂的Socket函数被封装成一控件TserverSocket,该控件继承自TCustomSocket 对象,封装了对监听端口绑定和监听。只要调用TserverSocket控件的Open方法,就可使服务器端的Sockct处于监听状态,当客户端有连接请求时将自动接受连接,然后在ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket)事件中读取来自客户端的请求。为了安全起见,我们可自定义较高层的协议,在客端所发送的信息中加上内容标识,如"user:abcdef",表示收到用户的验证,然后和客户端之间进行安全的认证。在读到数据时,我们可根据内容做出相应的动作,如取得进程的信息并发送给客户。 在客户端,我们采用TclientSocket控件来同服务器端进行通信,在指定了服务器端的IP地址和端口后,调用Open方法来和服务端的Socket取得联系并发送用户名和密码到服务端进行验证,之后就可以发送指令和接收数据了。需要说明的是,客户发送的指令要和服务器端进行商议好。 以上只是一个初步的应用,利用该思路还可做得更多,我们在实际应用取得了较好的效果。 |