在客户端重新创建对象

tgzhac3451

tgzhac3451

2016-02-19 12:40

只要你有一台电脑或者手机,都能关注图老师为大家精心推荐的在客户端重新创建对象,手机电脑控们准备好了吗?一起看过来吧!

  至今我们还没有涉及到客户部分的操作,现在就讨论一下。客户端通过调用服务器端的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接口。我没有研究过,不过一些编程者坚持说这是唯一的方法。我觉得安全数组也不错,因此没有尝试过,这可能会在未来的文章中提到。
展开更多 50%)
分享

猜你喜欢

在客户端重新创建对象

编程语言 网络编程
在客户端重新创建对象

AJAX客户端说明XMLHttpRequest对象

Web开发
AJAX客户端说明XMLHttpRequest对象

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

Zune 客户端

平面设计 标志设计 UI设计 VI设计
Zune 客户端

win客户端和linux客户端的区别

服务器
win客户端和linux客户端的区别

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

Session对象失效的客户端解决方法

Web开发
Session对象失效的客户端解决方法

探讨:使用httpClient在客户端与服务器端传输对象参数的详解

编程语言 网络编程
探讨:使用httpClient在客户端与服务器端传输对象参数的详解

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

WPS巧设行间距 图片无法显示的解决方法

WPS巧设行间距 图片无法显示的解决方法

用ActivePHP打造版本管理系统

用ActivePHP打造版本管理系统
下拉加载更多内容 ↓