封装了P2P连接与数据传送过程的DLL正式版-PPQ.DLL v1.0(一)

类别:VC语言 点击:0 评论:0 推荐:

    
一  正式版中的改进功能:

    1 通过钩子函数的设定,为使用其他开发者自定义类提供了完整的解决方案。
    2 去掉了对象版本号和对象标识的设定,增加了一个类的ClassID,作为唯一区分一个类的标识。
    3 消除了原先进行TCP下载时的繁杂的设定,现在下载或传送文件,只需设定文件数字标识即可,其它如文件名、扩展名,分割块数、文件大小、保存的文件名等均可缺省。
    4 增加了HTTP下载,DLL会根据服务器端是否支持断点续传决定启动单线程或多线程下载。支持重定向。使实现网络蚂蚁和FlashGet软件的相应功能变得非常容易。
    5 对语音传输作了进一步的改进,加强了静音过滤功能,修正了一些潜在的BUG。
    6 修正了原先下载一旦启动后,当线程组中的一个线程的SOCKET连接出现问题时,无法启动新的线程来继续传输的BUG。改进后PPQ.DLL会自动重新连接,始终使线程连接数保持到设定的最大连接个数,除非你调用方法终止一个线程。
    7 允许开发者从已经建立的TCP连接中剥离出SOCKET。
    8 将原来的PThreadParm类更名为PTask类。
    9 提供了安全释放PTask对象的方法,避免直接delete一个PTask对象时容易当机的问题。
    10 提供了下载文件合并的方法,下载后的分割文件必需调用该方法才能合并成单个文件。
    11 提供了如何建立文件的数字ID标识与实际文件名的对应关系的方法。
    12 将原来类中提供的声明为DLL内部调用的公共属性和方法全部改变为私有方法和属性,避免错误产生。
    13 增强了接收指令对象时的安全性,将自动过滤掉坏指令、错误指令、攻击性指令和未知指令对象。
    14 统一了方法调用的规则和属性的命名规则。
    15 标准化了消息、常量、结构的命名。
    16 提供了非常详细地关于回调函数、消息、结构、函数、属性的说明。
    17 修正了测试版中的Bug。
    18 在PDefine中提供了一个静态的属性bIsReleaseVer,缺省值为true。该属性表明当前的运行程序是否是正式版本。如果该值为false,则可以进行单机的连接测试。在单机测试时,被连接方将返回一个固定的ID号"PPQ000000000"。如果你需要与其它程序相连,则必需设定该值为true。

    PPQ.DLL的正式版与测试版相比,进行了较大的改动,最终确定了整个程序的框架,提供了更强的扩展性和安全性,为了使更多的人更好地了解PPQ.DLL的强大功能,特意重新整理了一套完整的说明。

 

二 PPQ.DLL的特点:

    1 在P2P标准尚未统一的情况下,开发者都在独立的制定自己的传送协议,使开发出来的程序彼此无法互相通讯,而且采用不同编程语言开发出的产品,在互相通信时也会存在问题,PPQ.DLL正是为解决这个问题而开发的。PPQ.DLL是建立在任何一种标准协议之上的一种公共接口,它不但能简化开发过程,而且标准化了传送和接收的过程,使开发者可以在各自制定协议的前提下,仍然能够实现互相通讯,并且可以在采用不同的编程语言开发的产品之间互相传递指令对象。
   
  2 PPQ.DLL封装了采用TCP进行文件传送与接收的全过程。只需要给出连接方的IP地址、监听端口号和想传送(或下载)的文件的数字标识号,PPQ.DLL会自动地以多线程、断点续传的方式实现文件的传送与接收过程。

  3 PPQ.DLL封装了以HTTP方式从URL地址下载文件的全过程。只需要给出想下载的URL地址,PPQ.DLL会自动地根据服务器端是否支持断点续传,来决定采用单线程还是多线程方式下载。支持重定向。
   
    4 PPQ.DLL封装了以TCP方式进行语音聊天的全过程,只需简单地调用几个静态函数,就可以轻易地启动、暂停、关闭声音捕捉(录音)和声音回放(放音),并初步实现了静音过滤。
   
    5 PPQ.DLL封装了进行数据传输的编码方式和传送的具体过程,将数据的传送和接收转变成对象的传送和接收,使处理过程标准化。使用PPQ.DLL来开发程序,不用直接和字符串打交道,不必再去解析从SOCKET接收到的字符串编码,开发者可以将想传送的信息定义成指令对象的属性,直接发送指令对象,PPQ.DLL会自动将指令对象转变成数据流发出。SOCKET所接收到的数据,PPQ.DLL会自动转变成对应的指令对象,以WINDOWS消息或回调函数的方式通知接收方,使整个的开发过程变得更加简单和模块化。
   
    6 PPQ.DLL的对象数据流处理并不是MFC的序列化对象,它比MFC的序列化对象更加简单、易用,允许在修改对象属性后,重复发送对象。因为它不是MFC的序列化对象,因此,传送的数据流可以被任何一种编程语言所解释并且转化成对象提交给使用者来处理。
   
    7 PPQ.DLL的对象流的实现过程和方法被完全地封装在了DLL中,对于实现对象流的算法的优化,甚至是改变对象流的传送格式,都不会对使用PPQ.DLL的开发者造成任何影响,使开发者可以完全放心地开发程序,而不用担心标准与协议的改变。经过数次的改写和优化,正式版的PPQ.DLL不但可以传送任意大小的指令对象,并且在接收指令对象时,从SOCKET中读出的数据将直接被写到指令对象相应的属性的缓冲区中,中间不再需要经过任何一次缓冲区的复制过程,大大地加快了速度。
   
    8 如果你认为使用PPQ.DLL开发的不同产品,只不过是界面上的不同,那你就错啦。PPQ.DLL提供了丰富的接口和灵活的开发方式,使你完全可以开发出具有鲜明特点和独立功能的程序。PPQ.DLL只是封装了连接实现的过程和握手协议,对建立连接后,双方传送的信息并没有作出任何规定和假设,它只是提供了一种方便地开发方式。
   
    9 开发者可以根据自己的需要去创建新的类,来表明一种类型的指令,这种类型的指令完成一种特定的功能,开发者可以将自己创建的指令和指令的解析程序一起打包成一个DLL,发布出来,同时公布该类的ClassID。其它的开发者可以在自己的程序中直接引用这个DLL,来完成由其它开发者预先定义好的功能。PPQ.DLL内部也定义了一些PBaseAct的派生类,这些派生类都是完成一种特定功能的指令对象。
   
    10 PPQ.DLL提供了一个钩子函数,用来返回开发者自定义的类的对象。如果你的自定义类是你发布的DLL中的一个内部类,即自定义的类被完全地封装在了你提供的DLL中,那么这个类的类名可以是任意的,不用担心会重名。当其他开发者要使用你的DLL时,只需要增加你提供的钩子函数即可。但是类的ClassID还是需要公布的,以避免与其他开发者的ClassID重复。
    握手协议的传递和语音聊天的传送与接收采用的就是指令对象的方式来进行传送,这些对象都被封装在了PPQ.DLL内部,开发者只需要通过接口来启动一个或一组功能,而不需要直接去同这些对象打交道。因为这些类被完全地封装在了PPQ.DLL内部,因此,即使你在开发过程中定义了一个与这些对象重名的类,也不会对程序造成任何的影响。
   
    11 因为最近在忙着作一个图像合成的软件,原来打算完成的突破防火墙连接的工作暂时放了下来,现在图像合成软件已经完成,我会尽快了将突破防火墙连接的功能集成进PPQ.DLL中。
   
    12 以后的开发计划:
        集成UDP传输,采用UDP同样可以实现指令对象的传送与接收。实现IP组播。
        集成FTP下载和上传。
        增加网络电话会议功能。
        提供一个新的类,实现通过文件数字标识直接查找到实际文件名,并提供权限设定的方案。
        集成视频捕捉与回放。
   
   
三  PPQ.DLL的工作方式

    PPQ.DLL通过消息与回调函数和DLL外部进行交互,要想正确地使用这个DLL,就需要了解PPQ.DLL中对外发布的几个类。PPQ.DLL中一共对外提供了5个类:PDefine、PFriend、PBaseAct、PTask和CStringEx。
   
    PDefine类中定义了开发者需要使用的结构、回调函数、常量以及PPQ.DLL定义的一些消息和静态方法。这个类不需要去创建实例,它里面的所有方法和属性都是静态的。这些定义对正确地了解和撑握PPQ.DLL是非常关键的。
   
    PPQ的整个连接过程是建立在一种"信任"的基础之上的。即如果A信任B,B也信任A,那么A与B之间可以互相连接,否则连接不能被建立。这种"信任"的关系不能够被继承,即如果A信任B,B信任A、C,C信任B,这并不表示A也信任C,A可以和B之间互连,B即可以和A,也可以和C之间互连,但A不可以和C这间互连。即"信任"只能是双方的事情。这种"信任"关系的表现的实体,就是PFriend类。
    PFriend类中定义了被连接方的身份标识(ID)、IP地址、监听端口号等相关信息,是对被连接方的一个描述(连接与被连接都是从自己这一方来看的)。通常开发者需要从该类中派生出新的类,以记录关于被连接方的更多的详细资料。在PPQ.DLL中,被连接的一方都称为好友,每一个好友都必需有一个PFriend对象与之对应,除了进行HTTP连接时不需要用到PFriend对象,其它进行的所有连接都是针对于某一个PFriend对象而进行的。希望互相连接的双方,彼此都必需包括有对方的PFriend对象,否则连接是无法被建立的。
   
    PPQ.DLL认为要传输的数据应该被分为2种,一种是指令,表示完成某一种功能,另一种是数据,它的实际意义由以前传递的指令来表明。指令和数据往往是相关连的,失去任何一方,都会失去其表示的有效意义,因此,这2种实际应该是一个整体,而这个整体在PPQ.DLL中表现出的实体,就是PBaseAct类。指令和数据被封装在一个PBaseAct类中,作为一个整体来传输,这就是指令对象。
    PBaseAct是所有可以转变为数据流进行传输的指令对象的基类,开发者需要自定义传输对象时,都需要从该类来派生出新的类。只有该派生类的实例才可以直接作为一个对象从SOCKET中进行传送与接收。
     
    建立连接应该是有目的性的,即建立连接应该是为了具体完成某一项工作,建立多个连接的目的是为了更好、更快地协同完成这项工作。在PPQ.DLL里,某一项工作用任务来表示,而任务的表现实体,就是PTask类。
    PTask类描述并记录了一个或一组具有相同连接类型的连接,它表明了要进行的一个任务。
     
    CStringEx类是针对于MFC的CString类的一个扩展,它的主要作用是传递大数据量的二进制流缓冲,通过操作符重载,CStringEx类可以使用"+="符号,直接追加一个字符串或一个int类型的数字。通过方法,甚至可以追加一个中间包含'\0'终结符的二进制的流缓冲。你可以使用CStringEx类在任何一个需要动态改变缓冲区大小的地方,代替原来的数据缓冲区,包括字符缓冲区、音频缓冲区、视频缓冲区等接收缓冲区,使用CStringEx就和使用一个char*是一样的,在使用过程中,你甚至可以直接得到CStringEx的数据缓冲区,将它转换成任意类型的缓冲区来使用。CStringEx对象用来保存指令对象中需要传送的大型数据,在构成指令对象时,如果指令对象需要发送比较长的数据(建议超过1K)时,都应使用CStringEx来保存,因为在传送指令对象时,对CStringEx对象作了优化处理,它的传送速度会比传送CString类型的对象要快得多。
   


四  创建PBaseAct派生类

    PPQ.DLL没有对外提供任何的指令对象,因此,使用DLL的第一件事情,就是创建自己的PBaseAct派生类。
    创建PBaseAct派生类其实很简单,按照以下步骤,你就可以轻松地创建出自己的派生类。
    1   在派生类中重载SelfSerialize()方法。
    2   在派生类中重载InitObject()方法。
    3   在派生类中重载GetClassID()方法,返回自定义类的ClassID。
    4   实现GOCALLBACK钩子函数,返回自定义对象的CRuntimeClass*指针。
    5   实现GETCALLBACK回调函数,对自定义对象进行处理。
        或者定义一个PMSGINFO结构,通过消息来处理自定义对象。
    6   如果在第5步中定义了一个PMSGINFO结构,则在派生类中重载基类PBaseAct中的
        virtual LPPMSGINFO  GetCallBackMsg()方法,返回指向PMSGINFO结构的指针。
        如果在第5步中实现了GETCALLBACK回调函数,则在派生类中重载基类PBaseact中的
        virtual GETCALLBACK* GetParseActFunPointer()方法,返回指向GETCALLBACK回调函
        数的指针。
        这两个函数你只需要重载一个,建议重载GETCallBackMsg()方法,通过消息来处理指令对象。
        如果两个函数都重载了,将优先处理回调函数。
    7   调用PDefine::SetUserGetObjectFunHook(),将在第4步中实现的GOCALLBACK钩子函数的地址
        作为参数传递。
        这一步应在程序的开始,还未用PPQ.DLL来传送任何消息之前就调用,否则PPQ.DLL无法解析该
        对象,PPQ.DLL在接收到未定义的指令对象时,会自动丢弃该指令对象。

 

五  在开始一个任务之前的准备工作

    PPQ.DLL会向DLL的外部传递一些很重要的消息,这些消息必需被响应,否则PPQ.DLL无法正常正作。
    另外,还有一些是必需赋值的静态属性,被定义在了PDefine类中。
   
    1   初始化PDefine::rSMsgInfo结构。
    2   实现回调函数GETPALCALLBACK,并将回调函数的地址赋给PDefine::pSGetFriendCallBackFun属性。
    3   实现回调函数GETFILENCALLBACK,并将调函数的地址赋给PDefine::pSGetFileNCallBackFun属性。
    4   为PDefine::szSelfFriendID属性赋初值,表明自己的ID。
    5   为以下消息创建响应函数:
        OMSG_CREATE_NEW_OBJ,
        OMSG_CREATE_NEW_THREAD,
        OMSG_ALL_CONNECT_END,
        OMSG_MISSION_END
    6   调用PDefine::SInitSocketStream()方法初始化Win Socket,使创建SOCKET连接成为可能。
    7   调用PDefine::SCreateTCPListenPort()方法创建TCP监听端口。
    8   调用PDefine::SSetUserGetObjectFunHook()方法设定钩子函数。
    9   为想建立连接的对方,建立PFriend对象,并填写上他ID、IP地址和TCP监听端口号。
   
    经过以上的工作,你已经为开始一个任务作好了充足的准备,在下一步,你就可以开始一个任务啦。


   
六  从URL下载一个文件

    最简单的任务就是从URL下载一个文件。PPQ.DLL封装了通过HTTP下载的全过程,你想在自己的软件中增加网络蚂蚁和FlashGet的功能吗?不要着急,使用PPQ.DLL,你只需要调用几个函数,就可以实现自动下载的过程。
   
    1   在堆中创建一个PRECVINFO结构。
            LPPRECVINFO lprInfo=new PRECVINFO();

    2   初始化结构,这步是必需的,每创建一个PRECVINFO结构,就应该调用以下的方法初始化结构。
            memset(lprInfo,0,DWORD(&lprInfo->nPort)-(DWORD)lprInfo);

    3   设定URL地址。
            lprInfo->strSrvURL  ="http://czdlpt.163.com/ptmini.exe";

    4   设定下载后文件被保存的路径,如果缺省,文件将被保存在当前目录下。
            lprInfo->strSaveBasePath="d:\\";

    5   创建一个PTask对象,准备开始一个新的任务。
            PTask* pTask=new PTask();

    6   设定启动多少个线程同时下载。
            pTask->SetMaxThreadNo(10);

    7   将PRECVINFO结构的地址赋给PTask对象。
            pTask->m_lprRecvInfo=lprInfo;

    8   开始执行一个HTTP下载任务。
            pTask->StartTask(TASK_HTTP_RECV);
   
    任务在执行过程中会向PDefine::rSMsgInfo结构中定义的窗体发送消息,以表明当前的任务执行状态。  任务结束后,窗体也会接收到消息以表明当前任务是否已经完成。关于这些消息的说明,请查阅PDefine.h中的消息的说明。
   
七  建立一个语音聊天
   
    想实现自己的IP Phone吗?想和你在国外的朋友互诉一下衷肠吗?不用再担心昂贵的国际长途费用,写个小的程序,一切就OK啦。你会发现,使用PPQ.DLL实现一个语音聊天非常简单。
       
    1   开始一个聊天任务,其中this->m_pFriend是一个已经创建好的PFriend对象。
        PTask* pTask=new PTask();
        pTask->m_pFriend=this->m_pFriend;
        pTask->StartTask(TASK_TCP_CHAT);
       
    2   启动声音播放设备
        PDefine::SPlaySound();
       
    3   初始化声音捕捉设备
        PDefine::SGetSoundCaptureDeviceList(NULL);
        PDefine::SInitSoundCaptureDevice(NULL);
        PDefine::SGetSoundCaptureAvailableFormats(NULL);
        PDefine::SCreateSoundCaptureBuffer(NULL);
       
    4   设置捕捉到的声音被传递给哪一个好友。
        PDefine::SSetSoundCaptureAcceptdFriend(this->m_pFriend);
       
    5   开始声音捕捉。
        PDefine::SRecordSound();

    经过以上的步骤,一个语音聊天已经被创建了,并且自动地开始捕捉声音,一旦有一个有效的声音被捕捉到,就会立即被传给指定的好友,接收到的声音被按照接收时的顺序排在一个队列中,声音播放设备会自动按照顺序播放。
    实际上,你已经可以同时和多人进行聊天,每个人都可以同时和你说话,但是在任意一个时刻你只能对其中一个人讲话。在集成IP组播以后,会提供方法,以实现同时将声音传递给多个好友。
    PPQ.DLL在语音的传输的实现上和其它程序实现语音传输的方式有很大的不同。大部分程序在实现语音传输时通常是启用2个TCP连接来完成(UDP方式除外),一个TCP连接用来传输控制命令或消息,一个TCP连接用来专门传输声音数据,因为它们的声音数据必需是连续传输的,即一旦启动语音聊天,即使你没有说话,也会有连续不断地数据被传送到连接方。
    PPQ.DLL通过一个简单的语音过滤功能,不再传送静音的数据,以减少不必要的数据传递量。另外,PPQ.DLL采用的是对象的传递方式,它只通过一个TCP连接来实现聊天,不管是文字聊天还是语音聊天,采用的都是同一个连接,你可以在聊天的过程中,一边进行语音聊天,一边通过聊天连接传送其它的数据,而彼此之间并不会互相干扰。
   
八  从另一个好友处下载一个文件
   
    通过在2个客户端之间建立一个连接,来完成指定文件的传送与接收,是P2P的基本功能。PPQ.DLL封装了整个过程,采用了多线程,断点续传的方式来实现文件的传送与接收,使处理标准和简单化。
   
    1   初始化开始一个传送或接收任务所必需的步骤
            LPPRECVINFO lprInfo=new PRECVINFO();
            memset(lprInfo,0,DWORD(&lprInfo->nPort)-(DWORD)lprInfo);
            lprInfo->strSaveBasePath="d:\\";
            PTask* pTask=new PTask();
            pTask->SetMaxThreadNo(5);
            pTask->m_lprRecvInfo=lprInfo;
           
    2   设定连接到哪一个好友,其中this->m_pFriend是一个已经创建好的PFriend对象。
            pTask->m_pFriend=this->m_pFriend;
   
    3   设定想下载的文件的数字标识
            pTask->m_dwFileID=100;
           
    4   开始一个通过TCP来接收文件的任务。
            pTask->StartTask(TASK_TCP_RECV);

    与好友之间的连接被自动地建立,并启动多线程来完成文件的接收。PPQ.DLL只有在连接被确定建立后,才会启动一个新的线程来接收数据,并不向FlashGet那样,只要有一个连接被建立,就会启动最大的线程个数,去偿试连接,结果经常是多个连接无法被建立,而线程却都被启动了,每个线程都在努力地偿试连接。PPQ.DLL在采用TCP和HTTP方式下载时,都遵循着这样一个原则:先偿试连接,等到连接被建立后才启动新的线程,等到开始传送有效数据后,才会去偿试进行下一个连接,以避免很多无效的线程被启动。
   
    到这里,应该对PPQ.DLL有了一个基本的认识,也能够使用PPQ.DLL完成基本的功能啦。PPQ.DLL提供了大量的接口和消息,要想充分地利用好PPQ.DLL,还需要详细地看一下关于消息和函数的说明,以实现更深层次的开发。
   
   
   
九  PPQ.DLL的回调(钩子)函数说明

*  函数名称:
*   typedef CRuntimeClass* (WINAPI *GOCALLBACK)(LPCTSTR lpszClassID)
*
* 参数:
*   LPCTSTR     lpszClassID    -唯一标识一个类的ID。
*
* 返回值:
*   CRuntimeClass*             -对象的CRuntimeClass*指针或NULL。
*
* 说明:
*   这是一个钩子函数。

    通过这个函数将得到用户自定义的PBaseAct派生类的CRuntimeClass*。
   
    注意:  这是一个钩子函数,每一次的设定,都会被记录下来,当调用第一个钩子函数否回为NULL时,会自动地调用第二个钩子函数。当钩子函数返回的值不为NULL时,其它的钩子函数将不会再被执行。

 举例如下,其中Self1Act是自己定义的PBaseAct的派生类;
 USelf1Act是其它开发者定义的PBaseAct的派生类,本地必需有这两个类的声明。
 
 CRuntimeClass* WINAPI GlobalFunUserGetObject(LPCTSTR lpszClassID)
 {
  DWORD dwLen=strlen(lpszClassID);
  if(memcmp(lpszClassID,"#MySelfDefineObj",dwLen)==0)
  {
   //如果是自己定义的对象。
   return RUNTIME_CLASS( Self1Act );
  }
  else if(memcmp(lpszClassID,"#User1DefineObj",dwLen)==0)
  {
   //加入对其他用户创建的对象的支持。
   return RUNTIME_CLASS( USelf1Act );
  }
  return NULL;
 }

    关于用户自定义对象的健状性问题与恶意性攻击的解决办法:
        如果你但心其他用户会发送一个非法的对象,但是使用了你在函数中规定的ClassID,你大可不必担心。

        DLL通过以下两种方法来防止这种恶意攻击行为:
        1   当双方建立连接的时候,都必需通过一个身份验证的过程,这个过程中双方传递的的指令对象是内部定义好的指令对象,而且整个过程是外部不可干预的,只有通过身份验证的双方才会建立起连接,也就是说,建立连接的双方都是相互信任的。

        2   如果通过身份验证的连接方要进行恶意攻击,DLL还会通过第二种办法自动丢弃这些错误的指令,绝对不会造成程序的非法操作。DLL在发现恶意性攻击时,会记录下攻击的次数,当攻击次数超过一定数量时,会向用户发出警告,告诉用户发出恶意攻击的连接方的ID,并自动断开该连接。
*
*
*   *   *   *   *   *   *   *   *   *   *   *   *
* 函数名称:
*   typedef PFriend* (WINAPI* GETPALCALLBACK)(LPCTSTR lpszFriendID)
*
* 参数:
*   LPCTSTR     lpszFriendID    -好友的ID标识
*
* 返回值:
*   PFriend*                             -指向好友对象的指针。
*
* 说明:
*   根据传递的好友ID返回指向具有该ID的PFriend对象的指针。
*
*
*   *   *   *   *   *   *   *   *   *   *   *   *
* 函数名称:
*   typedef void (WINAPI *GETFILENCALLBACK)(DWORD dwFileID,PFriend* pFriend,CString* pstrFileN)
*
* 参数:
*   DWORD       dwFileID        -请求的文件的数字标识。
*   PFrined*       pFriend           -指向请求该文件的好友对象的指针。
*   CString*       pstrFileN         -指向保存文件数字标识所对应的实际文件名的指针。
*
* 返回值:
*   无。
*
* 说明:
*   该函数将根据传递的dwFileID,来得到实际所对应的文件名。文件名保存在pstrFileN对象中。
    如果函数执行后*pstrFileN=="",表示获取实际文件失败。
    可以在这个回调函数中对文件编号进行校验,决定好友是否有权得到该文件。

    声明:  对于文件采用数字标识来表示,其最主要的目的是为了安全性,以及将来的扩展。数字标识如何与实际的文件名相对应,可以采用很多种办法,因此并没有包含在这个DLL中。

            关于数字标识与实际文件名的对应关系,将提供一个专门的类来实现,这只是一种解决办法,大家可以自己去偿试采用更好的办法来解决这个问题。

            下面是一种建立数字标识与实际文件名的对应关系的方式:

            采用数据库中记录的存储方式来保存每一个数字标识与实际文件名的对应关系,每一个数字标识与实际文件名的对应关系相当于一条记录,数据被集中保存在一个文件中,每一个记录均是定长的,48个字节。
            因为数字标识所表示的文件可以是本地机器上的任何一个目录下的任何一个文件,并不需要固定保存在某一个目录下,因此其路径长度+文件名长度可能远超过48个字节,这时的文件名如何记录呢?

            当要保存的长度超过48个字节时,采用2条或多条记录来保存相关内容,这时一个记录将占用2个或多个相关的数字标识号,被占用的数字标识号将不再分配给其它的记录来使用。因此,这种方式,实际可以保存任意长度的内容。
            查找任何一个指定ID所对应的文件名,都可以通过ID*48直接得到该ID所表示的记录在文件中的位置,因此,随机访问的速度为时间常数n。

            这样所表示的数字标识的最大序号可以达到4G(采用多文件保存),对于所表示的文件个数来讲,应该是足够啦,因为序号并不是递增的,任何一个中间被删除的记录,在下一次插入新记录时,就会被分配这个序号。
           
            对于删除和插入一条新的记录的方法,实际是很简单的,因为不需要扫描,因此它们的速度都是时间常数n。

            关于具体的实现办法,请关注即将发布的新类。
*
*
*   *   *   *   *   *   *   *   *   *   *   *   *
* 函数名称:
*   typedef void (WINAPI* GETCALLBACK)(WPARAM wParam,LPARAM lParam);
*
* 参数:
*   LPARAM       lParam            -指向PBaseAct派生类的一个对象的指针。
*
* 返回值:
*   BOOL                                 -成功处理返回非0值,否则返回0值。
*
* 说明:
*   该函数对用户的自定义对象进行处理,在处理完后需要delete传递的指令对象。
    你可以让所有的自定义对象去调用同一个处理函数,也可以让每一个自定义对象调用不同的函数。
    建议使用消息来代替该回调函数。

 举例如下,其中Self1Act、Self2Act是自己定义的PBaseAct的派生类:
 void WINAPI GlobalFunParseUserObj(WPARAM wParam,LPARAM lParam)
 {
  PBaseAct* pa   =(PBaseAct*)lParam;
  LPCTSTR  lpszClassID =pa->GetClassID();

  if(memcmp(lpszClassID,"#MySelfDefineObj-act1",dwLen)==0)
  {
   Self1Act* sa=(Self1Act*)pa;
   //在下面对该接收到的该指令进行处理。


  }
  else if(memcmp(lpszClassID,"#MySelfDefineObj-act2",dwLen)==0)
  {
   Self2Act* sa=(Self2Act*)pa;
   //在下面对该接收到的该指令进行处理。


  }
  //在函数的最后,一定要删除传递的指令对象。
  delete pa;
 }
*
*
*   *   *   *   *   *   *   *   *   *   *   *   *
* 函数名称:
*   typedef UINT (WINAPI *PFCALLBACK)(UINT nMsg,WPARAM wParam,LPARAM lParam)
*
* 参数:
*   UINT             nMsg            -发送的消息名称
*   WPARAM      wParam        -第一参数
*   LPARAM       lParam          -第二参数
*
* 返回值:
*   UINT                                -非0值表示成功,0表示失败。
*
* 说明:
*   在线程的工作过程中的通知用户的回调函数。最好不要使用这个函数,而用消息处理来代替它。
    这种类型的函数的指针被赋与PTask对象中的m_pCallBackFun变量。
    注意 :如果你想使用这个回调函数,不要在函数中处理需要长延时的操作。
                  如果在PTask中设定了回调函数,则不再向窗口发送消息。

本文地址:http://com.8s8s.com/it/it2359.htm