程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> C語言基礎知識 >> 在客戶端重新創建對象

在客戶端重新創建對象

編輯:C語言基礎知識

  至今我們還沒有涉及到客戶部分的操作,現在就討論一下。客戶端通過調用服務器端的GetArray()方法來開始整個處理。客戶端將會接收我們在服務器創建的安全對象。客戶端的程序則負責將這塊字節流變成為一個有效的C++對象。以下摘錄了做這部分工作的客戶端代碼:
  
  // create COM smart pointer from CLSID string
  
  IBlobDataPtr pI( "TestServer.BlobData.1" );
  
  SAFEARRAY *psa ;
  
  // Get the safearray from the server
  
  pI->GetArray( &psa );
  
  file:// create a pointer to an object
  
  CSimpleObj *dummy=NULL;
  
  file:// blob object to eXPand
  
  CBlob blob;
  
  file:// use the blob to expand the safearray into an object
  
  blob.Expand( (CObject*&)dummy, psa );
  
  file:// call a method on the object to test it
  
  dummy->Show(this->m_hWnd, "Client Message");
  
  // delete the object
  
  delete dummy;
  
  這段代碼挺簡單的。它使用一個COM智能指針與服務器進行連接。假如你對智能指針不熟悉,這些方便的小對象通過調用CoCreateInstance()就可做所有的工作,與服務器進行連接。
  
  CBlob類再一次完成這個步驟,這個類與服務器端的那一個是完全相同的。該類代碼事實上是由服務器工程中得到的。將一個安全數組變成一個CSimpleObject的工作由Expand()方法完成。該方法與服務器端調用的Load()方法是相對的。
  
  // Re-create an object from a SAFEARRAY
  
  BOOL CBlob::Expand(CObject * &rpObj, SAFEARRAY *psa)
  
   {
  
  CMemFile memfile; // memory file for de-serialize
  
  long lLength; // number of bytes
  
  char *pBuffer; // buffer pointer
  
  file:// lock Access to array data
  
  SafeArrayAccessData( psa, (void**)&pBuffer );
  
  // get number of elements in array. This is the number of bytes
  
  lLength = psa->rgsabound->cElements;
  
  // attach the buffer to the memory file
  
  memfile.Attach((unsigned char*)pBuffer, lLength);
  
  file:// start at beginning of buffer
  
  memfile.SeekToBegin();
  
  file:// create an archive with the attached memory file
  
  CArchive ar(&memfile, CArchive::loadCArchive::bNoFlushOnDelete);
  
  // document pointer is not used
  
  ar.m_pDocument = NULL;
  
  file:// inflate the object and get the pointer
  
  rpObj = ar.ReadObject(0);
  
  // close the archive
  
  ar.Close();
  
  file:// Note: pBuffer is freed when the SAFEARRAY is destroyed
  
  file:// Detach the buffer and close the file
  
  pBuffer = (char*) memfile.Detach();
  
  file:// release the safearray buffer
  
  SafeArrayUnaccessData( psa );
  
  return TRUE;
  
  }
  
  這個方法的大部分代碼你也在前面見過了,該方法接收一個安全數組的輸入。我們由安全數組中取出緩沖的數據。CArchive對象負責將緩沖的數據重新創建為對象。
  
  以下就是我們在剛才的Expand()方法中完成的事情
  
   1、鎖定訪問安全數組結構,並且得到它的長度(以字節計)。
  
   2、將安全數組的數據緩沖與一個CMemFile關聯
  
   3、將CMemFile與一個archive相關聯
  
   4、使用Archives的ReadObject()方法重新創建對象
  
   5、將SAFEARRAY的數據緩沖由CMemFile脫離
  
   6、返回一個指向新創建對象的指針給調用者
  
  我們在這裡談到了許多基層的東西。CBlob類負責串行化和還原對象的繁重工作。COM部分的程序確實是很易懂的。
     性能問題
  
  我並不是勸說你不要使用這裡提出的技巧,不過在性能方面,我要提醒你一下。以上我們提到的東西,對於小和中等大小的對象,可工作得很好的。不過,大型的對象將會有問題,我指的是超過一兆的大型對象。
  
  問題是,這些大型對象必須放在內存中,它可令系統開始使用分頁。你可能也知道,分頁的效率是很低的,非凡是對於內存少的機器。
  
  這些問題產生的原因是幾個這樣大的對象都必須同時放在內存中。假如客戶和服務器應用都運行在同一部機器(或者一個進程中),那麼在內存中同時可有高達三個這些對象的拷貝。這樣系統開銷就會很大,但這又是不可避免的。
  
  對於由許多小對象組成的大對象,在串行化處理方面可以作一些調整。你可以調用自己的CArchive,並且用更大的哈希表調用SetStoreParams和SetLoadParams。CArchive使用一個哈希表來存儲類的信息。假如哈希表填滿了,那麼串行化的處理將會變得很慢。默認的大小適合用在1000或者更少的對象,要了解如何做到這一點,可看一下sample目錄的CBigArchive類。你可以使用這個類來代替CArchive。在這個工程的CBigArchive類中,我也包含了它的一個例子(在我的例子中並沒有使用CBigArchive)。
  
  安全數組外的選擇
  
  安全數組並不是傳送二進行數據的唯一方法。我還簡要提一下其它三個辦法。我可以肯定還有其它的方式。
  
  1、使用一個VARIANT
  
  調用VariantInit API函數可創建一個VARIANT數據。該函數初始化VARIANT的數據結構。你還需要設置variant的類型,這可以通過設置它的"vt"成員做到。以下的代碼片段創建一個variant並且將它放到一個安全數組中。
  
  VARIANT *pVar
  
  // initialize the variant
  
  VariantInit( pVar );
  
  file:// set the variant type to an array of bytes
  
  pVar->vt = VT_ARRAY + VT_UI1;
  
  file:// create the safe array pointer
  
  SAFEARRAY *psa;
  
  psa = SafeArrayCreateVector( VT_UI1, 0, llen );
  
  file://Code to load the SAFEARRAY with data
  
   ...
  
  file:// assign the SAFEARRAY pointer to the VARIANT
  
  pVar->parray = psa;
  
  2、使用MIDL提供的非自動類型
  
  我們使用SAFEARRAY和VARIANT的原因是可以利用MIDL產生的類庫。該類庫可以簡化調用數據的操作,還提供COM的智能指針。使用一個類庫並不是必須的,但是不這樣做的話,你需要做一些額外的工作。
  
  MIDL答應一些"custom"(自定義)的數據類型。這些數據類型並不兼容類庫和"automation"的接口。一個二進制對象的MIDL接口可以使用一個byte *,而不是一個安全數組。該接口規范需要包含一些關於調用的明確信息,這些信息在[IN]和[OUT]屬性中定義。
  
  在TesServer.IDL文件中,我提到了一個關於如何定義MIDL接口的例子。該串行化的處理與這篇文章談到的是一樣的,不過緩沖分配有點不同。以下是IDL的定義。
  
  file:// get data from server
  
  HRESULT GetData([out] long* pcLen,[out,size_is(,*pcLen)] byte **pBuffer );
  
  file:// send data to server
  
  HRESULT SetData([in] long cLen,[in,unique,size_is(cLen)] byte Buffer[]);
  
  要使用這些接口你將必須做一些調用。"size_is"屬性告訴MIDL如何調用數據。(IDL屬性定義的信息與安全數組的成員變量存儲的信息是一樣的)。調用通過一個由MIDL產生的Proxy/Stub DLL完成。Proxy/Stub DLL需要建立,並且在客戶和服務器端使用。
  
  3、使用IStream接口
  
   一個完全不同的方法是使用標准的IStream接口。我沒有研究過,不過一些編程者堅持說這是唯一的方法。我覺得安全數組也不錯,因此沒有嘗試過,這可能會在未來的文章中提到。
 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved