2022年C++远程监控 .pdf

上传人:C****o 文档编号:32034602 上传时间:2022-08-08 格式:PDF 页数:9 大小:73.85KB
返回 下载 相关 举报
2022年C++远程监控 .pdf_第1页
第1页 / 共9页
2022年C++远程监控 .pdf_第2页
第2页 / 共9页
点击查看更多>>
资源描述

《2022年C++远程监控 .pdf》由会员分享,可在线阅读,更多相关《2022年C++远程监控 .pdf(9页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、C+ 远程监控由 :VC+学习网 _VC+教程 http:/收集整理 ,是VC+源代码 栏目的文章 ,如果您喜欢本站,请您把本站加入您的收藏或者分享给您的朋友吧。在工程施工中经常遇到中心主控机房和工程现场相分离的情况,这就需要工程设计人员经常往返于中心机房与工程现场之间,有时甚至为了修改几个数据也要相关人员的现场操作才能解决。 而且也不能很好的对工程现场进行实时的监测,这就为工程施工与系统的维护带来了极大的不便。现在局域网的技术已相当成熟,在中心机房和工程现场之间构建一个局域网也并不困难。所以我们可以在局域网的物理架构基础之上通过Socket套接字来实现计算机之间的通讯,使维护人员能在中心机房

2、内足不出户就可以实时地监测、控制远在工程现场的计算机的工作状态。本文就对类似程序的实现方法进行简单的介绍。Socket网络程序的一般思路Windows Sockets 规范定义了一个基于 Microsoft Windows 的网络编程界面,它源于加里弗尼亚大学伯克利分校的伯克利软件发布(BSD )。它既包括熟悉的伯克利 Socket 风格的例程,也包括了一组 Windows 特有的扩展,使程序员可以利用Windows 原有的消息驱动机制进行网络方面的编程。而此类程序中最常用的一种模式就是客户 /服务器模式。在这种框架中,客户应用程序向服务器应用程序请求服务。服务器应用程序一般在一个周知地址上侦

3、听(listen )服务请求。就是说,直到一个客户向服务器发出联接请求之前,服务器进程进程是休眠的。收到请求时,服务器进程 醒来( wake up) ,完成客户请求的相应的活动。套接字共有三种类型:流式套接字, 数据报套接字以及原始套接字等。流式套接字定义了一种可靠的面向连接的服务,实现了无差错无重复的顺序数据传输;数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠;原始套接字则允许对低层协议如IP 或 ICMP 等协议进行直接访问,主要用于对新的网络协议实现的测试等。 无连接服务器一般都是面向事务处理的,一个请求一个应答就完成了客户程序与服务程序之间

4、的相互作用。而面向连接服务器处理的请求往往比较复杂,不是一来一去的请求应答所能解决的,而且往往是并发服务器。本文所采用的就是面向连接的套接字,其工作过程如下:服务器首先启动,通过调用socket()建立一个套接字,然后调用bind()将该套接字和本地网络地址联系在一起,再调用 listen()使套接字做好侦听的准备,并规定它的请求队列的长度,之后就调用accept()来接收连接。客户在建立套接字后就可调用connect()和服务器建立连接。连接一旦建立,客户机和服务器之间就可以通过调用read()和 write()来发送和接收数据。最后,待数据传送结束后,双方调用close()关闭套接字。其主

5、要的流程时序可以通过图1 来表示:由于我们的目的是通过在位于中心机房的客户端来监控远程的服务器端,而根据前面介绍的名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 9 页 - - - - - - - - - 面向连接套接字应用程序的工作方式,要求服务器必须先于客户端而运行。所以根据实际需要,我们应当让服务器程序能自启动。一般有三种方法:在Autoexec.bat里添加代码;在 Win.ini的 Run 项里添加启动路径;在注册表里添加键值。本文在此采用后一种方法,通过向注册

6、表的SoftwareMicrosoftWindowsCurrentVersionRun下添加键值的方式来实现,另外也可以在RunServer下添加键值实现之: / 设定待添加的注册表的路径Run ; / 获取系统路径GetSystemDirectory(SysPath,size); GetModuleFileName(NULL,CurrentPath,size); / 把服务程序从当前位置拷贝到系统目录中FileCurrentName = CurrentPath; FileNewName = lstrcat(SysPath,System_Server.exe); ret = CopyFile(

7、FileCurrentName,FileNewName,TRUE); / 打开键值ret=RegOpenKeyEx(HKEY_LOCAL_MACHINE,Rgspath,0,KEY_WRITE, &hKEY); if(ret!=ERROR_SUCCESS) RegCloseKey(hKEY); return FALSE; / 设置键值ret=RegSetValueEx(hKEY,System_Server,NULL,type, (const unsigned char*)FileNewName,size); if(ret!=ERROR_SUCCESS) RegCloseKey(hKEY); r

8、eturn FALSE; / 关闭键值RegCloseKey(hKEY); 注册完之后就完成了自启动。下面进行本文的重点:对套接字进行编程,首先初始化Socket端口,并在初始化成功的前提下通过调用socket()创建一个套接字, 然后调用 bind()名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 9 页 - - - - - - - - - 将该套接字和本地网络地址联系在一起,再调用 listen()使套接字做好侦听的准备,并规定它的请求队列的长度。其中listen()

9、函数主要用来建立一个socket套接字以侦听到来的联接,而且仅用于支持联接的 socket,即类型为 SOCK_STREAM 的 socket。该套接字被设为 被动 模式,负责响应到来的联接,并由进程将到来的联接排队挂起。该函数典型地用于需要同时有多个联接的服务器:如果一个联接请求到达且队列已满,客户端将收到一个 WSAECONNREFUSED 的错误。当没有可用的描述符时,listen() 将试图把函数合理地继续下去。它将接受联接直到队列为空。如果描述符变为可用,后来的对 listen() 或 accept() 调用将会把队列填充到当前或最近的累积数(the current or most

10、recent backlog ),可能的话,继续侦听到来的联接。下面是这部分的主要代码:Page wMajorVersion = MAJOR_VERSION; wMinorVersion = MINOR_VERSION; wVersionReqd = MAKEWORD(wMajorVersion,wMinorVersion); Status = WSAStartup(wVersionReqd,&lpmyWSAData); if (Status != 0) return FALSE; / 创建 Socket套接字ServerSock = socket(AF_INET,SOCK_STREAM,0)

11、; if (ServerSock=INVALID_SOCKET) return FALSE; dstserver_addr.sin_family = PF_INET; dstserver_addr.sin_port = htons(7016); dstserver_addr.sin_addr.s_addr = INADDR_ANY; /BIND Status = bind(ServerSock,(struct sockaddr far *)&dstserver_addr,sizeof(dstserver_addr); if (Status != 0) return FALSE; /LISTEN

12、 Status = listen(ServerSock,1); if (Status != 0) return FALSE; 接下来需要调用accept()来接收连接。客户在建立套接字后就可调用connect()和服务器建立连接。其函数原形为:SOCKET PASCAL FAR accept ( SOCKET s, struct sockaddr FAR * addr, int FAR 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 9 页 - - - - - - - -

13、 - * addrlen );该例程从在 s 上挂起的联接队列中取出第一个联接,用和 s 相同的特性创建一个新的 socket 并返回新 socket 的句柄。如果队列中没有挂起的联接,并且 socket 也未 标 明 是 非 阻 塞 的 , 则 accept() 阻 塞 调 用 者 直 到 有 一 个 联 接 。 已 经 接 受 联 接的 socket (accepted socket)不应用于接受更多的联接。参数 addr 是一个返回参数,填入的是通信层的联接实体地址。地址参数 addr 的严格格式由进行通信的地址族确定。addrlen 是一个返回参数值;该值在调用前包含 addr 指向的

14、缓冲区空间长度;调用返回时包含返回地址的实际长度。/ACCEPT int len = sizeof(dstserver_addr); NewSock = accept(ServerSock,(struct sockaddr far *)&dstserver_addr,&len); if (NewSock 0) closesocket(ServerSock); return FALSE; / 获取屏幕大小SysWidth = GetSystemMetrics(SM_CXSCREEN); SysHeight = GetSystemMetrics(SM_CYSCREEN); 连接一旦建立,客户机和服

15、务器之间就可以通过调用read()和 write()来发送和接收数据。最后,待数据传送结束后,调用close()关闭套接字。下面的函数就负责将当前的屏幕状态, 以数据的形式通过send 函数发送给客户程序,以实现对远程服务器端的计算机的远程监视:/Send Falg FALG = US_FLAG; send(NewSock,(char*)&FALG,sizeof(FALG)+1,MSG_OOB); /Get Message length = recv(NewSock,(char*)&iMsg,sizeof(iMsg)+1,0); if (length 0) /Close Sock closes

16、ocket(NewSock); closesocket(ServerSock); return FALSE; /GetMessageData if (iMsg 4500) send(NewSock,(char*)&SysWidth,sizeof(SysWidth)+1,MSG_OOB); send(NewSock,(char*)&SysHeight,sizeof(SysHeight)+1,MSG_OOB); 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 9 页 - -

17、- - - - - - - switch(iMsg) case US_DESKTOPBIT: /发送当前屏幕图像SendDesktop(); break; 其中, SendDesktop()函数负责将屏幕保存成位图,然后再通过send ()函数将其以数据的形式发送出去,这一部分牵扯较多的位图操作,比较繁琐, 由于本文重点并不在此,仅作为一个功能函数将其关键性代码摘选如下:Page void SendDesktop() / 创建桌面设备环境句柄hdcmy = CreateDC(DISPLAY,NULL,NULL,NULL); hbufferdc = CreateCompatibleDC(hdcm

18、y); / 创建位图hBit = CreateCompatibleBitmap(hdcmy, BitWidth, BitHeight); hOldBitmap = (HBITMAP)SelectObject(hbufferdc, hBit); StretchBlt(hbufferdc, 0, 0, BitWidth, BitHeight, hdcmy, 0, 0,SysWidth,SysHeight, SRCCOPY); hBit = (HBITMAP)SelectObject(hbufferdc, hOldBitmap); /DDBtoDIB hPal = (HPALETTE) GetSto

19、ckObject(DEFAULT_PALETTE ); / 获取位图信息GetObject(bitmap,sizeof(bm),(LPSTR)&bm); / 初始化位图信息头bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = bm.bmWidth; bi.biHeight = bm.bmHeight; bi.biPlanes = 1; /bi.biBitCount = bm.bmPlanes * bm.bmBitsPixel; bi.biBitCount = 4; bi.biCompression = BI_RGB; bi.biSizeImag

20、e = 0; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrUsed = 0; bi.biClrImportant = 0; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 5 页,共 9 页 - - - - - - - - - lpbi = (LPBITMAPINFOHEADER)hDib; *lpbi = bi; GetDIBits(hdc, bitmap, 0L, (DWORD)bi.biHeight,(LPBY

21、TE)NULL, (LPBITMAPINFO)lpbi, (DWORD)DIB_RGB_COLORS ); bi = *lpbi; if (bi.biSizeImage = 0) bi.biSizeImage = (bi.biWidth * bi.biBitCount) + 31) & 31) / 8) * bi.biHeight; dwLen += bi.biSizeImage; if (handle = GlobalReAlloc(hDib, dwLen, GMEM_MOVEABLE) hDib = handle; lpbi = (LPBITMAPINFOHEADER)hDib; BOOL

22、 bgotbits = GetDIBits( hdc, bitmap0L, (DWORD)bi.biHeight,(LPBYTE)lpbi+ (bi.biSize + ncolors * sizeof(RGBQUAD),(LPBITMAPINFO)lpbi, (DWORD)DIB_RGB_COLORS); SelectPalette(hdc,hPal,FALSE); send(NewSock,(char*)&bitSize,sizeof(bitSize)+1,MSG_OOB); recv(NewSock,(char*)&BitMsg,sizeof(BitMsg)+1,0); plmagePoi

23、nt = (LPBYTE)hDib; for(WORD i=0;ibitSize/US_MAXSIZE;i+) send(NewSock,(char*)plmagePoint,sizeof(BYTE)*US_MAXSIZE,MSG_OOB); plmagePoint = plmagePoint + US_MAXSIZE; recv(NewSock,(char*)&BitMsg,sizeof(BitMsg)+1,0); if (bitSize%US_MAXSIZE) send(NewSock,(char*)plmagePoint,sizeof(BYTE)*GlobalSize(hDib)%US_

24、MAXSIZE,MSG_OOB); recv(NewSock,(char*)&BitMsg,sizeof(BitMsg)+1,0); 客户机端程序设计实现相比而言, 客户端程序的网络通讯部分的实现较为简单,只需创建socket套接字端口,并用 connect()同服务器建立起连接后就可以用recv()和 send()同服务器收发数据了。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 6 页,共 9 页 - - - - - - - - - 初始化 Socket端口部分同服务器的实现部分

25、类似, wVersionrequested = MAKEWORD(2,0); Page / 启动套接字WSAStartup(wVersionrequested,&wsaData); SetTimer(hWnd,IDT_TIMER,US_TIME,NULL); 在此,通过设置定时器来及时地把远程计算机的当前屏幕以位图数据的形式传到客户端, 并显示在屏幕上, 使维护人员能及时了解到远程计算机的工作状态。首先要用 connect()先建立一个到对等端(peer )的联接。 connect()函数主要用于创建到指定的外部关联的联接。如果 socket 尚未绑扎( unbound) ,则由系统为本地关联

26、指定一个唯一值。注意如果名字结构的地址域(the address field of the name structure)为全 0 ,connect()将返回错误 WSAEADDRNOTAVAIL。对流式 sockets (SOCK_STREAM 类) ,将启动一个到使用名字(该 socket 名字空间的一个地址)的外部主机的活动联接。当调用成功完成时,该 socket 已准备好发送/ 接收数据。下面是定时器消息响应函数的部分主要代码: clientSock = socket(AF_INET,SOCK_STREAM,0); if (clientSock 0) return FALSE; / 建

27、立连接client.sin_family = PF_INET; client.sin_port = htons(7016); client.sin_addr.s_addr = inet_addr(client_address); msgsock = connect(clientSock,(struct sockaddr*)&client,sizeof(client); if (msgsock!=0) return FALSE; / 获取屏幕尺寸GetWindowRect(hWnd,&rect); BitWidth = rect.right - rect.left; BitHeight = re

28、ct.bottom - rect.top; recv(clientSock,(char*)&Flag,sizeof(Flag)+1,0); if (Flag = US_FLAG) MouseEventFlag = false; / 发送消息Msg = US_DESKTOPBIT; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 7 页,共 9 页 - - - - - - - - - send(clientSock,(char*)&Msg,sizeof(Msg)+1,MSG_OOB);

29、 /Send Bit Height and Weidth send(clientSock,(char*)&BitWidth,sizeof(BitWidth)+1,MSG_OOB); send(clientSock,(char*)&BitHeight,sizeof(BitHeight)+1,MSG_OOB); / 接收数据GetDesktopBit(hWnd); MouseEventFlag = true; / 关闭套接字,释放数据closesocket(clientSock); 这里的在从服务器接收到数据后通过调用GetDesktopBit()来完成数据的显示,使从远程计算机传来的数据能在本地

30、客户机中再现: /Get Bit Size recv(clientSock,(char*)&bitSize,sizeof(bitSize)+1,0); send(clientSock,(char*)&Flag,sizeof(Flag)+1,MSG_OOB); / 锁定内存hDib = GlobalAlloc(GMEM_MOVEABLE,bitSize); p = (LPBYTE)GlobalLock(hDib); p2 = p; for(WORD i=0;ibitSize/US_MAXSIZE;i+) len = recv(clientSock,buf,US_MAXSIZE,0); CopyM

31、emory(p2,buf,US_MAXSIZE); p2 = p2 + US_MAXSIZE; send(clientSock,(char*)&Flag,sizeof(Flag)+1,MSG_OOB); if (bitSize%US_MAXSIZE) len = recv(clientSock,buf,bitSize%US_MAXSIZE,0); CopyMemory(p2,buf,len); p2 = p2 + bitSize%US_MAXSIZE; send(clientSock,(char*)&Flag,sizeof(Flag)+1,MSG_OOB); p2 = p2 - bitSize

32、; hdc = GetDC(hWnd); GetClientRect(hWnd,&rect); / 定义颜色int color = (1biBitCount); if(color256) color = 0; Page 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 8 页,共 9 页 - - - - - - - - - / 显示StretchDIBits(hdc, 0, 0, rect.right,rect.bottom,0,0, (LPBITMAPINFOHEADER)p)-bi

33、Width, (LPBITMAPINFOHEADER)p)-biHeight, (LPBYTE)p+(sizeof(BITMAPINFOHEADER)+color*sizeof(RGBQUAD), (LPBITMAPINFO)p,DIB_RGB_COLORS, SRCCOPY); 不论是服务器还是客户端,对于数据的传输都频繁地使用了recv 和 send 函数。其中前者主要用于从一个 socket 接收数据、 读取收到的数据。对流式套接字,将返回当前所有的尽可能多的数据,最长达到所提供的缓冲区的长度。如果该 socket 已经配置为线内(in-line)接收带外数据且有未读出的带外数据,则仅返

34、回带外数据。应用程序可以使用 ioctlsocket() SIOCATMARK选项确定是否还有其它的带外数据待读。如果 socket 上没有到来的数据,则除非 socket 是非阻塞的,recv() 调用会等待数据到达。send()用于已联接的数据报或流式套接字,用来在一个 socket 上写出( write outgoing)数据。在这里需要特别指出的是:一个 send() 的成功完成并不能表明数据已被成功传递。如果保存(hold ) 待发送数据的传输系统中没有缓冲区空间可用,send() 将会阻塞, 除非 socket 已设置为非阻塞 I/O 模式。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 9 页,共 9 页 - - - - - - - - -

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 高考资料

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁