程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 在Delphi中讀取另一台計算機的屏幕

在Delphi中讀取另一台計算機的屏幕

編輯:Delphi
編寫過Winsock應用程序的程序員都知道,編寫Winsock應用程序絕不是一件輕而易舉的事,您不得直接與復雜的Winsock中的Api打交道,幸運的是,Delphi4中的TclIEntsocket 和Tserversocket封裝了Windows中有關的Api,大為簡化了對Winsock的訪問,使得我們能夠非常輕易的編寫Winsock應用程序。本文通過一個讀取局域網內另一台計算機屏幕的示例,來介紹如何用Delphi編寫Winsock應用程序。
  
  在單位做過網管的人都可能有這樣的經歷,通過電話“遙控”指導別人操作是一件多麼心煩的事,而且我又是一個懶人,不想天天為一點小事從樓頂跑到樓下,怎麼辦呢?編一個讀取另一台計算機屏幕的程序怎麼樣?不就直觀多了。在局域網內進行通信,最好的選擇當然是用Winsock,編寫過Winsock應用程序的程序員都知道,編寫Winsock應用程序絕不是一件輕而易舉的事,您不得直接與復雜的Winsock中的Api打交道,幸運的是,Delphi4中的TclIEntsocket 和Tserversocket封裝了Windows中有關的Api,大為簡化了對Winsock的訪問,使得我們能夠非常輕易的編寫Winsock應用程序。盡管如此,最好還是對Winsock有一些了解,在這裡我就不再贅述,您可以找些書自己看看。
  通過網絡傳輸數據,您至少需要一對Socket,其中一個在客戶端,另一個在服務端,一旦客戶端與服務端的socket建立起連接,就可以相互通信了,用Socket建立連接是建立在Tcp/ip基礎上的,同時也支持ipx/spx等相關協議。在Delphi中 分別用TclIEntsocket 和Tserversocket來操縱客戶端與服務端Socket的連接和通信。要說明的是,這兩個元件用於管理服務器與客戶的連接,本身並不是Socket對象,操縱Socket對象的是TcustomwinSocket,如Tclientwinsocket、 TserverclIEntwinsocket、Tserverwinsocket。
  一、TclIEntsocket元件:
  把一個Tclientsocket加到Form上,應用程序就變為一個Tcp/ip客戶,就可以用TclIEntsocket來操縱客戶端的Socket對象。
  要建立與服務器的連接,應先指定要連接的服務器。指定服務器有兩種方式,一種是設置Host屬性指定服務器的主機名,如http://www.inprise.com或局域網中的機器名,這種方式直觀,但要進行域名解析,速度會稍慢一些;另一種方法是設置Adress屬性指定主機的ip地址,如130.0.1.1。這兩種方法是等價的,但如果同時設置了Host和Adress,Delphi將只使用Host屬性。
  然後要指定連接服務器的端口號,這裡也有兩種方式,一是設置Service使用默認端口號,一是直接設置Port端口號,在1024以下的端口號中,很多都已經分配出去了,如FTP的端口為20和21,SMTP的端口是25,WEB服務器的端口為80等,為防止無意間的沖突,建議在編制自已的應用程序時,最好將Port設為1024以上。如果同時設置了Service和port,Delphi將使用Service默認的端口。
  指定好服務器和端口號後,調用open方法或將Active屬性設為True,客戶端的Socket就會向服務端的Socket提出連接請求,如果此時服務端處於監聽狀態,就會自動接受請求建立連接,建立連接時,會觸發其Onconnet事件。需要斷開連接時,只需要調用close方法或將Active屬性設為False,此時會觸發ondisconnet事件。
  二、Tserversocket元件:
  同TclIEntsocket一樣,建造一個服務器,只需要將一個Tserversock元件放在Form即可。
  服務端的socket對象管理起來較為復雜。當服務器處於監聽狀態,此時服務端的socket對象用Tserversocket來操縱;當客戶提出請求,服務器響應請求並建立了連接,此時用TserverclIEntwinsocket來操縱服務端Socket與客戶端的Socket的連接。
  要使服務器處於監聽狀態,必須先指定端口號,當然,應該和客戶端的端口號相同。然後調用open方法或Active屬性設為True。
  三、通信:
  一旦建立起客戶與服務器的連接後,就可以進行相互間的通信了。Delphi為Tserversocket和TclIEntsocket提供了幾個通信用的方法,用sendtext發送本本信息,用sendstream發送流,用SendBuf發送指定長度的數據。
  需要注意的是,由於Windows默認緩沖區大小為4K,所以當發送長於4K的信息時,比如,從服務端向客戶端發送的一個二進制流,在服務端只需要用 Socket.SendStream()就行了,而在客戶端就不一樣,它將多次觸發onread事件,而Delphi又沒有定義如“onreadend”之類的事件,因此,必須在接收時程序員自己對數據進行組裝。本文采取的方法是先將流長度發給客戶端,然後發送流,客戶端將接收的數據寫進一個流中,當流長度等於服務端發回的長度時,就表明客戶端已接收完畢了。對服務端來說,做為sendstream參數的流,會為Socket 所“擁有”,Socket對象結束時,它也將結束,而不要自己去釋放它,否則,會觸發一個異常。
  同樣,當發送的文本小於4K,例如在客戶端程序中進行如下調用時
  clIEntsocket1.Socket.SendText(‘gets‘);
  clIEntsocket1.Socket.SendText(‘gets‘);
  clIEntsocket1.Socket.SendText(‘gets‘);
  服務端接收時會出現getsgets之類的現象,這可能是因為當緩沖區內的數據還未發送完時,又將新的文本放入緩沖區,計算機把它也當成同一批數據進行處理的緣故。為避免這個現象的發生,在程序內可采用一來一回“拋球”式的做法:
  客戶端 服務端
  clIEntsocket1.Socket.SendText(‘data1‘) socket.ReceiveText;
  socket.sendtext(‘ok‘);
  socket.receivetext;
  clIEntsocket1.Socket.SendText(‘ data2‘)
  socket.ReceiveText;
  socket.sendtext(‘end‘);
  socket.receivetext;
  
  在另一台計算機上運行服務端程序後,在您的客戶端程序上文本框內輸入該計算機名,接“連接”,按“取圖”,如何,對方計算機的屏幕一覽無余了吧。以下是程序的全部源代碼,本程序在NT4.0、Win95、Win98、局域網內運行通過,當然,Windows必須得裝tcp/ip協議,而且必須有動態分配的或指定的ip地址。
  如果您覺邊看邊“指揮”還是比較麻煩,您還可以對image1上的鍵盤、鼠標事件進行分析,然後發給服務端,服務端接收後,再進行同樣的操作,這樣您就可以不用麻煩操作員了。利用Delphi的TclIEntsocket 和Tserversocket,還可以完成諸如文件復制、網上聊天、ICQ等應用程序的開發,實現起來都很簡單,你可以自由的發揮出你的想象力,編寫出更富魅力的程序來。
  
  客戶端程序:
  unit cmain;
  
  interface
  
  uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ScktComp, StdCtrls, ExtCtrls,jpeg;
  
  type
  TForm1 = class(TForm)
  Panel1: TPanel;
  ScrollBox1: TScrollBox;
  Image1: TImage;
  Button1: TButton;
  Edit1: TEdit;
  Button2: TButton;
  ClientSocket1: TClIEntSocket;
  Label1: TLabel;
  procedure Button1Click(Sender: TObject);
  procedure Button2Click(Sender: TObject);
  procedure ClIEntSocket1Connect(Sender: TObject;
  Socket: TCustomWinSocket);
  procedure ClIEntSocket1Error(Sender: TObject; Socket: TCustomWinSocket;
  ErrorEvent: TErrorEvent; var ErrorCode: Integer);
  procedure ClIEntSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
  procedure FormCreate(Sender: TObject);
  procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
  { Private declarations }
  public
  { Public declarations }
  end;
  
  var
  Form1: TForm1;
  c:longint;
  m:tmemorystream;
  
  implementation
  
  {$R *.DFM}
  
  procedure TForm1.Button1Click(Sender: TObject);
  begin
  try
  clIEntsocket1.Close;
  clIEntsocket1.Host:=edit1.text;
  clIEntsocket1.Open; //連接服務端
  except
  showmessage(edit1.text+#13#10+‘未開機或未安裝服務程序‘);
  end;
  end;
  procedure TForm1.Button2Click(Sender: TObject);
  begin
  clIEntsocket1.Socket.SendText(‘gets‘); //發送申請,通知服務端需要屏幕圖象
  end;
  
  procedure TForm1.ClIEntSocket1Connect(Sender: TObject;
  Socket: TCustomWinSocket);
  begin
  caption:=‘連接到‘+edit1.text;
  end;
  
  procedure TForm1.ClIEntSocket1Error(Sender: TObject;
  Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
  var ErrorCode: Integer);
  begin
  caption:=‘連接‘+edit1.text+‘失敗‘;
  showmessage(edit1.text+#13#10+‘未開機或未安裝服務程序‘);
  errorcode:=0;
  
  end;
  
  procedure TForm1.ClIEntSocket1Read(Sender: TObject;
  Socket: TCustomWinSocket);
  var
  buffer:array [0..10000] of byte; //設置接收緩沖區
  len:integer;
  ll:string;
  b:tbitmap;
  j:tjpegimage;
  begin
  if c=0 then //C為服務端發送的字節數,如果為0表示為尚未開始圖象接收
  begin
  ll:=socket.ReceiveText;
  c:=strtoint(ll); //設置需接收的字節數
  clIEntsocket1.Socket.SendText(‘okok‘); //通知服務端開始發送圖象
  end else
  begin //以下為圖象數據接收部分
  len:=socket.ReceiveLength; //讀出包長度
  socket.ReceiveBuf(buffer,len); //接收數據包並讀入緩沖區內
  m.Write(buffer,len); //追加入流M中
  if m.Size>=c then //如果流長度大於需接收的字節數,則接收完畢
  begin
  m.Position:=0;
  b:=tbitmap.Create;
  j:=tjpegimage.Create;
  try
  j.LoadFromStream(m); //將流M中的數據讀至JPG圖像對象J中
  b.Assign(j); //將JPG轉為BMP
  Image1.Picture.Bitmap.Assign(b); //分配給image1元件
  finally //以下為清除工作
  b.free;
  j.free;
  clIEntsocket1.Active:=false;
  clIEntsocket1.Active:=true;
  m.Clear;
  c:=0;
  end;
  end;
  end;
  
  
  end;
  
  procedure TForm1.FormCreate(Sender: TObject);
  begin
  m:=tmemorystream.Create;
  
  end;
  
  procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
  begin
  m.free;
  ClIEntSocket1.Close;
  end;
  
  end.
  
  
  服務端程序:
  unit smain;
  
  interface
  
  uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ScktComp,jpeg;
  
  type
  TForm1 = class(TForm)
  ServerSocket1: TServerSocket;
  procedure ServerSocket1ClIEntRead(Sender: TObject;
  Socket: TCustomWinSocket);
  procedure FormCreate(Sender: TObject);
  private
  { Private declarations }
  public
  { Public declarations }
  end;
  
  var
  Form1: TForm1;
  m1:tmemorystream;
  
  implementation
  
  {$R *.DFM}
  
  procedure TForm1.ServerSocket1ClIEntRead(Sender: TObject;
  Socket: TCustomWinSocket);
  var
  s,s1:string;
  desk:tcanvas;
  bitmap:tbitmap;
  jpg:tjpegimage;
  begin
  s:=socket.ReceiveText;
  if s=‘gets‘ then //客戶端發出申請
  begin
  bitmap:=tbitmap.Create;
  jpg:=tjpegimage.Create;
  desk:=tcanvas.Create; //以下代碼為取得當前屏幕圖象
  desk.Handle:=getdc(hwnd_desktop);
  m1:=tmemorystream.Create; //初始化流m1,在用sendstream(m1)發送流後,
  //它將保留到socket對話結束,
  //不能用手工free掉,否則會觸發異常
  with bitmap do
  begin
  width:=screen.Width;
  height:=screen.Height;
  canvas.CopyRect(canvas.cliprect,desk,desk.cliprect);
  end;
  jpg.Assign(bitmap); //將圖象轉成JPG格式
  jpg.SaveToStream(m1); //將JPG圖象寫入流中
  jpg.free;
  m1.Position:=0;
  s1:=inttostr(m1.size);
  Socket.sendtext(s1); //發送圖象大小
  end;
  if s=‘okok‘ then //客戶端已准備好接收圖象
  begin
  m1.Position:=0;
  Socket.SendStream(m1); //發送JPG圖象
  end;
  
  end;
  
  procedure TForm1.FormCreate(Sender: TObject);
  begin
  ServerSocket1.open;
  end;
  
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved