封裝了P2P連線與資料傳送過程的DLL正式版-PPQ.DLL v1.0(一) (轉)

worldblog發表於2007-12-13
封裝了P2P連線與資料傳送過程的DLL正式版-PPQ.DLL v1.0(一) (轉)[@more@]

 
一  正式版中的改進功能:

  1 透過鉤子的設定,為使用其他開發者自定義類提供了完整的解決方案。
  2 去掉了版本號和物件標識的設定,增加了一個類的ClassID,作為唯一區分一個類的標識。
  3 消除了原先進行TCP時的繁雜的設定,現在下載或傳送,只需設定檔案數字標識即可,其它如檔名、副檔名,分割塊數、檔案大小、儲存的檔名等均可預設。
  4 增加了HTTP下載,DLL會根據端是否支援斷點續傳決定啟動單執行緒或多執行緒下載。支援重定向。使實現螞蟻和的相應功能變得非常容易。
  5 對語音傳輸作了進一步的改進,加強了靜音過濾功能,修正了一些潛在的。
  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進行檔案傳送與接收的全過程。只需要給出連線方的、埠號和想傳送(或下載)的檔案的數字標識號,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會自動轉變成對應的指令物件,以訊息或回撥函式的方式通知接收方,使整個的開發過程變得更加簡單和模組化。
 
  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組播。
  整合下載和。
  增加網路電話會議功能。
  提供一個新的類,實現透過檔案數字標識直接查詢到實際檔名,並提供設定的方案。
  整合影片捕捉與回放。
 
 
三  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型別的數字。透過方法,甚至可以追加一箇中間包含'終結符的二進位制的流緩衝。你可以使用CStringEx類在任何一個需要動態改變緩衝區大小的地方,代替原來的資料緩衝區,包括字元緩衝區、緩衝區、影片緩衝區等接收緩衝區,使用CStringEx就和使用一個char*是一樣的,在使用過程中,你甚至可以直接得到CStringEx的資料緩衝區,將它轉換成任意型別的緩衝區來使用。CStringEx物件用來儲存指令物件中需要傳送的大型資料,在構成指令物件時,如果指令物件需要傳送比較長的資料(建議超過1K)時,都應使用CStringEx來儲存,因為在傳送指令物件時,對CStringEx物件作了最佳化處理,它的傳送速度會比傳送CString型別的物件要快得多。
 


四  建立PBaseAct派生類

  PPQ.DLL沒有對外提供任何的指令物件,因此,使用DLL的第一件事情,就是建立自己的PBaseAct派生類。
  建立PBaseAct派生類其實很簡單,按照以下步驟,你就可以輕鬆地建立出自己的派生類。
  1  在派生類中過載SelfSerialize()方法。
  2  在派生類中過載Init()方法。
  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,D(&lprInfo->nPort)-(DWORD)lprInfo);

  3  設定URL地址。
  lprInfo->strSrvURL  ="";

  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* (WIN *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中設定了回撥函式,則不再向視窗傳送訊息。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-992698/,如需轉載,請註明出處,否則將追究法律責任。

相關文章