程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 基於套接字通信的遠程截屏顯示與控制技術

基於套接字通信的遠程截屏顯示與控制技術

編輯:關於VC++

遠程控制技術在遠程設備(軟件)的維護、監控與故障診斷等方面有廣泛的應用前景,且大 都使用Client/Server模式。該結構包括連接在網絡中的多台計算機,那些處理應用、請求另 一計算機服務的計算機為客戶機(本地機),而響應請求並處理請求的計算機稱為服務器(目標 機)。

遠程控制的原理[1]是:用戶連接到網絡上,客戶程序發送身份驗證信息和與遠 程主機連接的請求,遠程主機的服務器端程序驗證客戶身份,若驗證通過,就與客戶建立連 接,並向用戶發送驗證通過和已建立連接的信息。此時用戶便可以通過客戶端程序向遠程主 機發送要執行的指令,而服務器端程序則執行這些指令,並把鍵盤、鼠標和屏幕刷新傳給客 戶端程序,客戶端程序通過處理把主機屏幕等信息進行顯示,使用戶就像親自在遠程主機上 操作一樣。這種方式稱為基於遠程服務的遠程控制(Remote Control over Remote Service) 。

綜上所述,實現客戶端對服務端的遠程控制,需要就解決雙機Socket網絡通信、遠 程截取屏幕顯示以及屏幕數據傳送控制等3個問題進行討論。

1 雙機Socket網絡通信 [2]

初始化服務端Socket:在調用Socket前先要初始化,即加載相應版本的DLL,通過 調用WSAStartup函數,將加載成功的Socket庫版本的相關信息填在LPWSADATA結構中;

WSADATA lpWSAData;

WSAStartup(MAKEWORD(1,1),&lpWSAData);

創建服務端Socket:完成初始化之後,調用socket函數創建一個套接字,返回套接字句柄, 在其後通信中始終用來標識套接字,若調用失敗則返回INVALID_SOCKET;

SOCKET sktConnect=socket(AF_INET,SOCK_STREAM,0);

綁定服務端地址:在為某種特定協議 創建了套接字後,就用bind函數將套接字綁定到一個本機地址,其類型是sockaddr,用於指 明套接字綁定地址,包括IP地址與端口號;

bind(sktConnect,(struct sockaddr far *)&sockaddrin,sizeof(sockaddrin));

服務端監聽網絡:socket利用listen函數 設置狀態位,用來檢測是否有到來的連接請求,然後調用accept函數,准備接收客戶端連接 信號,無連接請求時,服務進程被阻塞;

listen (sktConnect,1);

sktClient=accept(sktConnect,(struct sockaddr far *) &sockaddrin,& sockaddrlen);

初始化與創建客戶端Winsock:首先利用 AfxSocketInit函數判斷參數lpwsaData是否為空,從而確定是否調用WSAStartup函數來填充 WSADATA結構,隨後同樣調用socket函數創建客戶端的套接字,給客戶端Sockaddr_in結構賦 值,地址類型和端口號與服務端相同;

套接字選項設置:使用setsockopt函數設置套 接字選項,比如發送或者接收的超時設置,緩沖區的容量設置,使用ioctlsocket函數設置 socket的I/O模式等;

int ret=ioctlsocket(sktClient,FIONBIO,(unsigned long*) &ul);

雙方建立連接:客戶端調用connect函數向服務端發出連接請求,當連接請 求到來時,被阻塞服務端的accept函數生成一個新的字節流套接字,返回客戶端Sockaddr_in 結構變量,用帶有客戶端地址的套接字與客戶端進行連接,然後向客戶端返回接收信號;

connect(sktClient,(const struct sockaddr *)&sockaddrin,sizeof (sockaddrin));

收發數據:一旦客戶端套接字接收到來自服務端的接受信號,則表示 雙方已經實現連接,任何一方均可使用Send/Write函數和Recv/Read函數向對方發送或者接收 數據;

send(sktClient,chrSend,10,0);recv(sktClient,chrReceive,10,0);

關閉套接字與winsock注銷:服務端和客戶端可以通過調用closesocket函數關閉套接字上的 所有發送和接收操作,撤銷套接字並且中斷連接。同時,winsock服務的動態鏈接庫在使用結 束後,應用程序必須調用WSACleanup函數將其注銷,並釋放分配的資源。

Winsock套 接字主要工作流程如圖1,①~⑩標識網絡數據交換順序。

圖1 Winsock 套接字主要工作流程

2 遠程截取屏幕顯示

服務端在接收到客戶端的屏幕數據 請求後,通過使用當前屏幕設備的句柄,開始向開辟的內存區域復制屏幕數據,得到與設備 相關的GDI位圖;然後再通過設置位圖信息頭、調色板等,最後得到與設備無關的DIB位圖。

2.1獲取當前屏幕的設備相關位圖[3](DDB)

設備相關位圖(DDB)也稱為圖形設 備接口(GDI)位圖,在MFC庫中用CBitmap類來存儲。該對象包含與設備相關的GDI模塊數據結 構。應用程序在截獲屏幕顯示數據的時候,將數據填充到開辟的相容性內存區域中,並與 CBitmap對象的句柄建立關聯,從而得到GDI位圖數據的備份。但由於GDI位圖中關於位的安排 完全依賴於顯示設備,在不同類型計算機間傳遞GDI位圖是沒有意義的。所以還需要進一步轉 化,得到設備無關位圖DIB。

(1)得到當前屏幕的分辨率,從而確定截取屏幕的范圍;

ScreenX=GetSystemMetrics(SM_CXSCREEN);

ScreenY=GetSystemMetrics (SM_CYSCREEN);

(2)得到屏幕HDC,並開辟相容性內存區域,建立相容性的HBITMAP;

HDC hdcmy=CreateDC("DISPLAY",NULL,NULL, NULL);

HDC hbufdc=CreateCompatibleDC(hdcmy);

HBITMAP hBit=CreateCompatibleBitmap (hdcmy, ScreenX,ScreenY);

(3)將當前屏幕內容復制到之前開辟的內存區域中,得到 當前屏幕的GDI位圖;

HBITMAP hOldBitmap=(HBITMAP)SelectObject (hbufdc,hBit);

StretchBlt (hbufdc,0,0,ScreenX,ScreenY,hdcmy,0,0,ScreenX,ScreenY,SRCCOPY);

hBit= (HBITMAP)SelectObject(hbufdc,hOldBitmap);

2.2 轉化設備相關位圖(DDB)至設備無 關位圖[3](DIB)

DIB自帶顏色信息,可以實現調色板管理,任何運行Windows的計算機 中都可以處理這種標准的位圖格式,BMP文件中就包含了一個DIB,主要由位圖文件頭、位圖 信息頭、調色板和DIB圖像數據4個部分組成,DDB向DIB的轉化實際上就是利用DDB中包含的圖 像信息,填充DIB除位圖文件頭的另外3個部分,從而得到與設備無關的位圖數據。最後可再 通過添加位圖文件頭,構成一幅標准的BMP圖像。

(1)通過BITMAP句柄hBit,得到位圖 信息,隨後填充BITMAPINFOHEADER結構,計算InfoHeader長度,初始化調色板,最後分配存 儲空間存放上述信息頭與調色板數據;

GetObject(hBit,sizeof(bitmap),(LPSTR) &bitmap);

int ncolors=1<< (bitmap.bmPlanes*bitmap.bmBitsPixel);

DWORD dwLen=sizeof(BITMAPINFOHEADER) +ncolors*sizeof (RGBQUAD);

HANDLE hDib=GlobalAlloc (GMEM_FIXED,dwLen);

(2)計算位圖數據實際占用的字節數,使其寬度大於或者等於離 4最近的整數倍,修正原biSizeImage數值,然後重新計算並分配空間用於存儲信息頭,調色 板和實際圖像數據;

bi.biSizeImage=((((bi.biWidth*bi.biBitCount)+31) &~31)/8)*bi.biHeight;

dwLen += bi.biSizeImage;

if (handle=GlobalReAlloc(hDib,dwLen,GMEM_MOVEABLE)) hDib=handle;

(3)向開辟的指 定存儲區域中復制上述信息頭、調色板以及實際圖像信息3部分數據,最後返回該存儲區域的 句柄,得到最終的DIB位圖;

LPBITMAPINFOHEADER lpbi=(LPBITMAPINFOHEADER) hDib;

GetDIBits( hdc, bitmap,0L,(DWORD)bi.biHeight,(LPBYTE)lpbi + (bi.biSize+ncolors

*sizeof(RGBQUAD)),(LPBITMAPINFO)lpbi,(DWORD) DIB_RGB_COLORS);

3 屏幕數據傳送控制

屏幕數據的傳送控制主要在如何確認 服務端與客戶端之間的連接,服務端如何定時分塊發送屏幕數據,客戶端如何拼接屏幕數據 並顯示圖像,服務端如何響應客戶端的鼠標事件等幾個方面。

3.1服務端與客戶端的 連接確認

雙方連接可以由客戶端指定服務端IP地址,或者在子網段內發送通信對方標 識,服務端接收到該標識後,向客戶端發送確認標識,客戶端收到確認信息後,表明雙方實 現連接。

以下代碼為客戶端程序片斷,服務端程序將發送和接收函數的順序對調即可 。

char cFlag[8]="CopyScr\0";

send(sktClient,cFlag,8,0);//發 送client端標志

recv(sktClient,cFlag,8,0);//接收server端標志

3.2 服務端 定時分塊發送屏幕數據

客戶端以某一定時器設定為間隔,向服務端請求屏幕數據,服 務端收到請求後,首先獲取當前屏幕的GDI位圖數據,並轉化為DIB位圖數據,隨後采用分塊 傳送的方式,向客戶端發送屏幕的位圖數據,分塊過程如下所示:

(1)發送屏幕位圖 數據的相關信息,諸如尺寸、長度、高度等信息至客戶端;

send(sktClient, (char*)&ScrInfo,sizeof(ScrInfo)+1,0);

(2)分塊發送DIB位圖數據,以 SENDBLOCK為分塊尺寸,同時調整當前數據指針位置;

LPBYTE plmagePoint= (LPBYTE)hDib;

for(WORD i=0;i<(ScrInfo.dwSize/SENDBLOCK);i++) {

send(sktClient,(char*)plmagePoint,sizeof(BYTE) *SENDBLOCK,0);

plmagePoint=plmagePoint + SENDBLOCK;

recv(sktClient, (char*)&StopFlag,sizeof(int)+1,0);}

(3)當屏幕位圖數據不是剛好等於分塊尺 寸倍數的時候,用於處理余下的數據傳送;

if (ScrInfo.dwSize %SENDBLOCK)

send(sktClient,(char*)plmagePoint,ScrInfo.dwSize%SENDBLOCK,0);

3.3客 戶端拼接屏幕數據並顯示圖像

客戶端的屏幕數據拼接程序,剛好與服務端的屏幕數據 切分程序相對應,首先是接收屏幕位圖相關信息,然後按照指定的分塊大小接收屏幕數據, 最後將小於分塊尺寸的屏幕數據單獨進行接收處理,得到服務端完整的一次屏幕數據,位圖 采用StretchDIBits函數顯示。

StretchDIBits (dc,0,0,rect.right,rect.bottom,0,0,

((LPBITMAPINFOHEADER)SvrData)- >biWidth,

((LPBITMAPINFOHEADER)SvrData)->biHeight,

(LPBYTE) SvrData+(sizeof(BITMAPINFOHEADER)+color*sizeof(RGBQUAD)),

(LPBITMAPINFO) SvrData,DIB_RGB_COLORS,SRCCOPY);

3.4 服務端響應客戶端的鼠標事件

當使用 鼠標點擊客戶端中顯示服務端當前屏幕的區域,客戶端程序將會記錄下具體的左/右鍵,單/ 雙擊,X/Y坐標位置等信息,作為鼠標事件發送給服務端,服務端隨後進行解析,並作出相應 的響應,從而實現客戶端得到服務端屏幕並加以控制的功能。

mouse_event (MOUSEEVENTF_LEFTDOWN ,0,0,0,GetMessageExtraInfo());

4 結論

通過上述 方式,客戶端可以定時接收服務端當前的屏幕信息,同時服務端也可以對客戶端的鼠標事件 作出響應,從而實現本地機對目的機的遠程控制。本顯示控制技術已經被成功應用於電子測 量儀器的遠程監控系統中。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved