国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Visual C++實現對計算機遠程監控

2019-11-17 05:41:02
字體:
來源:轉載
供稿:網友
摘要:本文講述了利用Socket套接字進行網絡編程的一般技術,并通過該技術實現了對計算機的遠程監控。

  要害字:Socket套接字、服務器、客戶端、遠程監控

  引言

  在工程施工中經常碰到中心主控機房和工程現場相分離的情況,這就需要工程設計人員經常往返于中心機房與工程現場之間,有時甚至為了修改幾個數據也要相關人員的現場操作才能解決。而且也不能很好的對工程現場進行實時的監測,這就為工程施工與系統的維護帶來了極大的不便。現在局域網的技術已相當成熟,在中心機房和工程現場之間構建一個局域網也并不困難。所以我們可以在局域網的物理架構基礎之上通過Socket套接字來實現計算機之間的通訊,使維護人員能在中心機房內足不出戶就可以實時地監測、控制遠在工程現場的計算機的工作狀態。本文就對類似程序的實現方法進行簡單的介紹。

  Socket網絡程序的一般思路

  Windows Sockets 規范定義了一個基于 Microsoft Windows 的網絡編程界面,它源于加里弗尼亞大學伯克利分校的伯克利軟件發布(BSD)。它既包括熟悉的伯克利 Socket 風格的例程,也包括了一組 Windows 特有的擴展,使程序員可以利用Windows 原有的消息驅動機制進行網絡方面的編程。而此類程序中最常用的一種模式就是客戶/服務器模式。在這種框架中,客戶應用程序向服務器應用程序請求服務。服務器應用程序一般在一個周知地址上偵聽(listen)服務請求。就是說,直到一個客戶向服務器發出聯接請求之前,服務器進程進程是休眠的。收到請求時,服務器進程"醒來(wake up)",完成客戶請求的相應的活動。

  套接字共有三種類型:流式套接字,數據報套接字以及原始套接字等。流式套接字定義了一種可靠的面向連接的服務,實現了無差錯無重復的順序數據傳輸;數據報套接字定義了一種無連接的服務,數據通過相互獨立的報文進行傳輸,是無序的,并且不保證可靠;原始套接字則答應對低層協議如ip或ICMP等協議進行直接訪問,主要用于對新的網絡協議實現的測試等。無連接服務器一般都是面向事務處理的,一個請求一個應答就完成了客戶程序與服務程序之間的相互作用。而面向連接服務器處理的請求往往比較復雜,不是一來一去的請求應答所能解決的,而且往往是并發服務器。

  本文所采用的就是面向連接的套接字,其工作過程如下:服務器首先啟動,通過調用socket()建立一個套接字,然后調用bind()將該套接字和本地網絡地址聯系在一起,再調用listen()使套接字做好偵聽的預備,并規定它的請求隊列的長度,之后就調用accept()來接收連接。客戶在建立套接字后就可調用connect()和服務器建立連接。連接一旦建立,客戶機和服務器之間就可以通過調用read()和write()來發送和接收數據。最后,待數據傳送結束后,雙方調用close()關閉套接字。其主要的流程時序可以通過圖1來表示:

Visual C++實現對計算機遠程監控 服務器端程序設計實現

由于我們的目的是通過在位于中心機房的客戶端來監控遠程的服務器端,而根據前面介紹的面向連接套接字應用程序的工作方式,要求服務器必須先于客戶端而運行。所以根據實際需要,我們應當讓服務器程序能自啟動。一般有三種方法:在Autoexec.bat里添加代碼;在Win.ini的Run項里添加啟動路徑;在注冊表里添加鍵值。本文在此采用后一種方法,通過向注冊表的Software//Microsoft//Windows//CurrentVersion//Run下添加鍵值的方式來實現,另外也可以在RunServer下添加鍵值實現之:

……
//設定待添加的注冊表的路徑
LPCTSTR Rgspath="Software//Microsoft//Windows//CurrentVersion//Run" ;
……
//獲取系統路徑
GetSystemDirectory(SysPath,size);
GetModuleFileName(NULL,CurrentPath,size);
……
//把服務程序從當前位置拷貝到系統目錄中
FileCurrentName = CurrentPath;
FileNewName = lstrcat(SysPath,"http://System_Server.exe");
ret = CopyFile(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);
  return FALSE;
}
//關閉鍵值
RegCloseKey(hKEY);

  注冊完之后就完成了自啟動。下面進行本文的重點:對套接字進行編程,首先初始化Socket端口,并在初始化成功的前提下通過調用socket()創建一個套接字,然后調用bind()將該套接字和本地網絡地址聯系在一起,再調用listen()使套接字做好偵聽的預備,并規定它的請求隊列的長度。其中listen()函數主要用來建立一個socket套接字以偵聽到來的聯接,而且僅用于支持聯接的 socket,即類型為 SOCK_STREAM 的 socket。該套接字被設為"被動"模式,負責響應到來的聯接,并由進程將到來的聯接排隊掛起。該函數典型地用于需要同時有多個聯接的服務器:假如一個聯接請求到達且隊列已滿,客戶端將收到一個 WSAECONNREFUSED 的錯誤。當沒有可用的描述符時,listen() 將試圖把函數合理地繼續下去。它將接受聯接直到隊列為空。假如描述符變為可用,后來的對 listen() 或 accept() 調用將會把隊列填充到當前或最近的累積數(the current or most recent "backlog’’),可能的話,繼續偵聽到來的聯接。下面是這部分的主要代碼:

……
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);
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
Status = listen(ServerSock,1);
if (Status != 0)
return FALSE;

  接下來需要調用accept()來接收連接。客戶在建立套接字后就可調用connect()和服務器建立連接。其函數原形為:SOCKET PASCAL FAR accept ( SOCKET s, struct sockaddr FAR * addr, int FAR * addrlen );該例程從在 s 上掛起的聯接隊列中取出第一個聯接,用和 s 相同的特性創建一個新的 socket 并返回新 socket 的句柄。假如隊列中沒有掛起的聯接,并且 socket 也未標明是非阻塞的,則 accept() 阻塞調用者直到有一個聯接。已經接受聯接的 socket (accepted socket)不應用于接受更多的聯接。參數 addr 是一個返回參數,填入的是通信層的聯接實體地址。地址參數 addr 的嚴格格式由進行通信的地址族確定。addrlen 是一個返回參數值;該值在調用前包含 addr 指向的緩沖區空間長度;調用返回時包含返回地址的實際長度。

//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);
……

  連接一旦建立,客戶機和服務器之間就可以通過調用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
  closesocket(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);
}
switch(iMsg)
{
  case US_DESKTOPBIT: //發送當前屏幕圖像
   SendDesktop();
   break;
  ……
}

  其中,SendDesktop()函數負責將屏幕保存成位圖,然后再通過send()函數將其以數據的形式發送出去,這一部分牽扯較多的位圖操作,比較繁瑣,由于本文重點并不在此,僅作為一個功能函數將其要害性代碼摘選如下:

void SendDesktop()
{
  ……
  //創建桌面設備環境句柄
  hdcmy = CreateDC("DISPLAY",NULL,NULL,NULL);
  hbufferdc = CreateCompatibleDC(hdcmy);
  //創建位圖
  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) GetStockObject(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.biSizeImage = 0;
  bi.biXPelsPerMeter = 0;
  bi.biYPelsPerMeter = 0;
  bi.biClrUsed = 0;
  bi.biClrImportant = 0;
  ……
  lpbi = (LPBITMAPINFOHEADER)hDib;
  *lpbi = bi;
  GetDIBits(hdc, bitmap, 0L, (DWORD)bi.biHeight,(LPBYTE)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 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);
   plmagePoint = (LPBYTE)hDib;
   for(WORD i=0;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_MAXSIZE,MSG_OOB);
    recv(NewSock,(char*)&BitMsg,sizeof(BitMsg)+1,0);
   }
   ……
}

客戶機端程序設計實現

  相比而言,客戶端程序的網絡通訊部分的實現較為簡單,只需創建socket套接字端口,并用connect()同服務器建立起連接后就可以用recv()和send()同服務器收發數據了。

  初始化Socket端口部分同服務器的實現部分類似,

……
wVersionrequested = MAKEWORD(2,0);
//啟動套接字
WSAStartup(wVersionrequested,&wsaData);
……
SetTimer(hWnd,IDT_TIMER,US_TIME,NULL);

  在此,通過設置定時器來及時地把遠程計算機的當前屏幕以位圖數據的形式傳到客戶端,并顯示在屏幕上,使維護人員能及時了解到遠程計算機的工作狀態。首先要用connect()先建立一個到對等端(peer)的聯接。connect()函數主要用于創建到指定的外部關聯的聯接。假如 socket 尚未綁扎(unbound),則由系統為本地關聯指定一個唯一值。注重假如名字結構的地址域(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;
//建立連接
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 = rect.bottom - rect.top;
recv(clientSock,(char*)&Flag,sizeof(Flag)+1,0);
if (Flag == US_FLAG)
{
  MouseEventFlag = false;
  //發送消息
  Msg = US_DESKTOPBIT;
  send(clientSock,(char*)&Msg,sizeof(Msg)+1,MSG_OOB);
  //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()來完成數據的顯示,使從遠程計算機傳來的數據能在本地客戶機中再現:

……
//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;i<bitSize/US_MAXSIZE;i++)
{
  len = recv(clientSock,buf,US_MAXSIZE,0);
  CopyMemory(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;
……
hdc = GetDC(hWnd);
GetClientRect(hWnd,&rect);
//定義顏色
int color = (1<<((LPBITMAPINFOHEADER)p2)->biBitCount);
if(color>256)
color = 0;
//顯示
StretchDIBits(hdc, 0, 0, rect.right,rect.bottom,0,0,
((LPBITMAPINFOHEADER)p)->biWidth,
((LPBITMAPINFOHEADER)p)->biHeight,
(LPBYTE)p+(sizeof(BITMAPINFOHEADER)+color*sizeof(RGBQUAD)),
(LPBITMAPINFO)p,DIB_RGB_COLORS, SRCCOPY);
……

  不論是服務器還是客戶端,對于數據的傳輸都頻繁地使用了recv和send函數。其中前者主要用于從一個 socket 接收數據、讀取收到的數據。對流式套接字,將返回當前所有的盡可能多的數據,最長達到所提供的緩沖區的長度。假如該 socket 已經配置為線內(in-line)接收帶外數據且有未讀出的帶外數據,則僅返回帶外數據。應用程序可以使用 ioctlsocket() SIOCATMARK選項確定是否還有其它的帶外數據待讀。假如 socket 上沒有到來的數據,則除非 socket 是非阻塞的,recv() 調用會等待數據到達。send()用于已聯接的數據報或流式套接字,用來在一個 socket 上寫出(write outgoing)數據。在這里需要非凡指出的是:一個 send() 的成功完成并不能表明數據已被成功傳遞。假如保存(hold)待發送數據的傳輸系統中沒有緩沖區空間可用,send() 將會阻塞,除非 socket 已設置為非阻塞 I/O 模式。

  小結

  本文通過Socket流式套接字實現了對計算機的遠程監控。隨著計算機網絡化的深入,計算機網絡已滲透到各種傳統行業中,計算機網絡編程尤其是基于Windows Socket套接字的網絡程序的編程日益顯得重要。本文采用直接利用動態連接庫wsock32.dll,以WinSock API對程序進行設計講解,雖實現比較繁瑣,但能地對程序的設計實現有很好的理解。隨著經驗的豐富,也可以采用VC++的MFC類庫中提供的CAsyncSocket套接字類來實現Socket編程,比較方便。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 平舆县| 利辛县| 怀集县| 阜城县| 启东市| 黄大仙区| 紫阳县| 永福县| 保亭| 台山市| 临漳县| 邹平县| 三门峡市| 石门县| 通化县| 达拉特旗| 长海县| 桃园市| 偃师市| 延寿县| 临漳县| 无为县| 陇南市| 东平县| 长武县| 珲春市| 右玉县| 博罗县| 荔浦县| 海晏县| 罗甸县| 剑阁县| 丹棱县| 高碑店市| 大宁县| 渑池县| 蛟河市| 肃北| 齐河县| 康定县| 酒泉市|