線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:1947
推到 Plurk!
推到 Facebook!

封裝了P2P連接與資料傳送過程的DLL正式版-PPQ.DLL wanghao0727(原住)-1

 
jackkcg
站務副站長


發表:891
回覆:1050
積分:848
註冊:2002-03-23

發送簡訊給我
#1 引用回覆 回覆 發表時間:2002-11-28 03:06:55 IP:61.64.xxx.xxx 未訂閱
此為轉貼資料 http://www.csdn.net/develop/article/14/14623.shtm 封裝了P2P連接與資料傳送過程的DLL正式版-PPQ.DLL v1.0(一) wanghao0727(原作) 關鍵字 P2P,DLL,網路編程,序列化物件 一 正式版中的改進功能: 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中設定了回調函數,則不再向視窗發送消息。 封裝了P2P連接與資料傳送過程的DLL正式版-PPQ.DLL v1.0(二) wanghao0727(原作) 關鍵字 P2P,DLL,網路編程,序列化物件 十 PPQ.DLL的消息 * * 對除特別聲明外的所有消息都適應的規範 * * 當接收到一個以PTask物件的指標作?lParam參數的消息時,你可以決定是否要繼續使用該物件。 * * 如果想繼續使用該物件,則先調用InitValue()方法,InitValue()不會改變原有的賦值。 * * 如果你想繼續工作,則不用改動已經賦過值的屬性,而直接調用StartTask()方法。 * * 如果你想完成其他的工作,你必需要設定相應的屬性的值,就象你新創建了一個該PTask物件一樣,然後再調用StartTask()方法。 * 如果不想繼續使用該物件,你需要delete這個物件。如果你在delete這個物件這前,不希望刪除PRECVINFO結構,你可以保存該指向該結構的指標,然後設定m_lprRecvInfo=NULL。 * * PTask物件中的m_dwIdentify屬性保存的是DLL外部在創建PTask物件時?PTask物件設定的標識,通過該標識應該可以正確地得到一個仍然有效的PTask物件。也就是說,當接到該標識的參數時,該標識所表示的PTask物件不一定還是有效的,這個PTask物件可能已經被delete啦,你必需通過檢測,以確保PTask物件是有效的,才能正確地引用這個物件,否則應該丟棄剛收到的這個消息。這點一定要注意。 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 消息名稱: OMSG_ALL_CONNECT_END * * 類型: 必需回應的消息。 * * 接收消息的表單: * 在PDefine::rSMsgInfo結構中定義的表單。 * * 參數: * LPARAM lParam -指向PTask物件的指標 * * 說明: * 表明一個PTask物件的所有的連接請求都已終止,但是線程組中的線程並未終止。 接到該指令時不能delete這個PTask物件。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_CREATE_NEW_OBJ * * 類型: 必需回應的消息。 * * 接收消息的表單: * 在PDefine::rSMsgInfo結構中定義的表單。 * * 說明: 在消息函數執行體中直接調用PDefine::SCreateNewObj()靜態方法, 傳遞wParam和lParam參數。其他不必作任何事情。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_CREATE_NEW_THREAD * * 類型: 必需回應的消息。 * * 接收消息的表單: * 在PDefine::rSMsgInfo結構中定義的表單。 * * 參數: * LPARAM lParam -指向PTask物件的指標 * * 說明: * 你可以通過判斷PTask->m_dwIdentify==0來得知該PTask物件是否已設定了標識。 如果值?0,你需要?這個物件設定一個唯一的標識。而且你還需要去判斷物件的類型,如果類型?TASK_TCP_RECV,則要在堆裏去創建一個PRECVINFO物件,並且初始化這個PRECVINFO結構,並將指標賦與PTask->lprRecvInfo變數。 最後在消息的回應函數中調用PDefine::SCreateNewThread()靜態方法,傳遞wParam 和lParam參數。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_MISSION_END * * 類型: 必需回應的消息。 * * 接收消息的表單: * 在PDefine::rSMsgInfo結構中定義的表單。 * * 參數: * WPARAM wParam -表明了任務的完成狀態。值?0時,表示任務已經完成。非0值,表示任務的當前狀態。 * LPARAM lParam -指向PTask物件的指標 * * 說明: * 表明一個PTask物件的任務已經完成或終止。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_NEW_TCP_CONNECTED * * 類型: 必需回應的消息。 * * 接收消息的表單: * 在PDefine::rSMsgInfo結構中定義的表單。 * * 參數: * WPARAM wParam -用來得到SOCKET控制碼的關鍵參數。 * LPARAM lParam -指向請求該連接的PFriend物件的指標 * * 說明: * 一個由連接方請求的TCP連接被建立。關於該Socket的所有操作,將轉移給DLL外部處理。在回應函數中需要調用PDefine::SGetSocketConnected(WPARAM wParam)靜態方法,將wParam作?參數傳遞,來返回已經建立好TCP連接的SOCKET控制碼。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_TCP_APPLY_CONNECTED * * 類型: 如果用到了TASK_TCP類型的連接,必需回應該消息。 * * 接收消息的表單: * 在PDefine::rSMsgInfo結構中定義的表單。 * * 參數: * WPARAM wParam -已經建立好TCP連接的SOCKET控制碼 * LPARAM lParam -指向建立連接的PFriend物件的指標 * * 說明: * 一個請求的類型?TASK_TCP的TCP連接已經被成功的建立。 關於該Socket的所有操作,轉移給DLL外部處理。 類型?TASK_TCP,表示對方也在使用這個DLL,你可以傳送指令物件。 雙方已經通過了身份校驗。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_TCP_USER_APPLY_CONNECTED * * 類型: 如果使用了TASK_TCP_USER類型的連接,必需回應該消息。 * * 接收消息的表單: * 在PDefine::rSMsgInfo結構中定義的表單。 * * 參數: * WPARAM wParam -已經建立好TCP連接的SOCKET控制碼 * LPARAM lParam -指向建立連接的PFriend物件的指標 * * 說明: * 一個請求的類型?TCP_USER_TYPE的TCP連接已經被成功的建立。 關於該Socket的所有操作,轉移給DLL外部處理。 類型?TASK_TCP_USER,表示對方沒有使用或不確定使用了這個DLL。 這個連接只是進行了創建,雙方還沒有進行任何的資料傳送。 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 消息名稱: OMSG_A_RECV_THREAD_END * * 類型: 可以回應的消息。 * * 接收消息的表單: * 在PTask物件的m_lprMsgInfo變數所指的結構中定義的表單。 * * 參數: * WPARAM wParam -低8位元表示該線程在PTask物件的線程組中的標識號 * LPARAM lParam -保存的是線程所在的PTask物件中的m_dwIdentify屬性的值 * * 說明: * 表明一個接收文件的線程結束。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_A_SEND_THREAD_END * * 類型: 可以回應的消息。 * * 接收消息的表單: * 在PTask物件的m_lprMsgInfo變數所指的結構中定義的表單。 * * 參數: * WPARAM wParam -低8位元表示該線程在PTask物件的線程組中的標識號 * LPARAM lParam -保存的是線程所在的PTask物件中的m_dwIdentify屬性的值 * * 說明: * 表明一個傳送文件的線程結束。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_CPATURE_SOUND_RELEASED * * 類型: 可以回應的消息。 * * 接收消息的表單: * 在PDefine::rSMsgInfo結構中定義的表單。 * * 參數: * 無意義 * * 說明: * 表明一個聲音捕捉設備被釋放,這個釋放並不是調用 PDefine::SReleaseSoundCaptureDevice()方法而?生的。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_CHAT_CONNECT_CLOSED * * 類型: 可以回應的消息。 * * 接收消息的表單: * 在PDefine::rSMsgInfo結構中定義的表單。 * * 參數: * LPARAM lParam -指向擁有該聊天連接的PFriend物件的指標 * * 說明: * 表明lParam所指定的PFriend物件的聊天Socket被關閉。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_CHAT_CONNECT_CREATED * * 類型: 可以回應的消息。 * * 接收消息的表單: * 在PDefine::rSMsgInfo結構中定義的表單。 * * 參數: * LPARAM lParam -指向擁有該聊天連接的PFriend物件的指標 * * 說明: * 表明一個聊天Socket被建立,並且已經連接到了好友物件上。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_CREATE_CONNECT_FAILED * * 類型: 可以回應的消息。 * * 接收消息的表單: * 在PTask物件的m_lprMsgInfo變數所指的結構中定義的表單。 * * 參數: * WPARAM wParam -錯誤的原因。查閱錯誤代碼的枚舉定義。 * LPARAM lParam -指向想建立連接的PFriend物件的指標 * * 說明: * 表明與PFreind物件無法建立連接,在建立連接時?生了一個錯誤。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_MAX_THREAD_NO_CREATED * * 類型: 可以回應的消息。 * * 接收消息的表單: * 在PTask物件的m_lprMsgInfo變數所指的結構中定義的表單。 * * 參數: * LPARAM lParam -保存的是線程所在的PTask物件中的m_dwIdentify屬性的值 * * 說明: * 表明lParam所指的PTask物件已經創建了最大的線程個數。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_RECV_DATA * * 類型: 可以回應的消息。 * * 接收消息的表單: * 在PTask物件的m_lprMsgInfo變數所指的結構中定義的表單。 * * 參數: * WPARAM wParam -低8位元表示該線程在PTask物件的線程組中的標識號 * LPARAM lParam -保存的是線程所在的PTask物件中的m_dwIdentify屬性的值 * * 說明: * 表明接收到了新的資料資訊。 在消息的回應函數中,你需要設定PTask中的屬性m_bIsSendMsg=true,否則無法再接收到該任務所發出的該消息。 在PTask中的m_dwCompletedSize屬性中保存的是總的接收的位元組數。 你可以調用PTask物件的GetThreadIDInArray((BYTE)wParam)來獲得該線程所在的PTHREADID結構的指標。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_RECV_NEW_BLOCK * * 類型: 可以回應的消息。 * * 接收消息的表單: * 在PTask物件的m_lprMsgInfo變數所指的結構中定義的表單。 * * 參數: * WPARAM wParam -低8位元表示該線程在PTask物件的線程組中的標識號 * LPARAM lParam -保存的是線程所在的PTask物件中的m_dwIdentify屬性的值 * * 說明: * 表明接收線程準備接收一個新分配的塊號。 你可以調用PTask物件的GetThreadIDInArray((BYTE)wParam)來獲得該線程所在的 PTHREADID結構的指標。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_SEND_DATA * * 類型: 可以回應的消息。 * * 接收消息的表單: * 在PTask物件的m_lprMsgInfo變數所指的結構中定義的表單。 * * 參數: * WPARAM wParam -低8位元表示該線程在PTask物件的線程組中的標識號 * LPARAM lParam -保存的是線程所在的PTask物件中的m_dwIdentify屬性的值 * * 說明: * 表明傳送了新的資料資訊。 在消息的回應函數中,你需要設定PTask中的屬性m_bIsSendMsg=true,否則無法再接收到該任務所發出的該消息。 在PTask中的m_dwCompletedSize屬性中保存的是總的傳送的位元組數。 你可以調用PTask物件的GetThreadIDInArray((BYTE)wParam)來獲得該線程所在的 PTHREADID結構的指標。 * * * * * * * * * * * * * * * * 消息名稱: OMSG_SEND_NEW_BLOCK * * 類型: 可以回應的消息。 * * 接收消息的表單: * 在PTask物件的m_lprMsgInfo變數所指的結構中定義的表單。 * * 參數: * WPARAM wParam -低8位元表示該線程在PTask物件的線程組中的標識號 * LPARAM lParam -保存的是線程所在的PTask物件中的m_dwIdentify屬性的值 * * 說明: * 表明傳送線程準備傳送一個新的部分。 你可以調用PTask物件的GetThreadIDInArray((BYTE)wParam)來獲得該線程所在的 PTHREADID結構的指標。 十一 PPQ.DLL中定義的結構說明 * * 結構名稱: PRECVINFO * * 結構指標定義: LPRECVINFO * * 屬性: * WORD wSplitNo -下載文件被分割的塊數。如果設定分割塊數?0, DLL會根據PDefine::dwSBlockMaxSize中的值自動計算分割塊數。 * CString strSaveName -被保存的文件的名稱和相對於基準路徑的路徑。如果strSaveName=_T(""),則檔案名等於傳送方的檔案名。該屬性的值可以包含一個"\\"結尾的路徑,這個路徑是相對於基準路徑的路徑。例如"pict\\",表示在基準路徑下的pict子目錄下去建立文件。也可以直接寫成"e:\\pict\\",而設置基準路徑?""。 * CString strSaveBasePath -表示被保存在本地的文件的基準路徑。路徑必需以"\\"結尾。 * CString strExpendName -被保存文件的副檔名。副檔名不包括"."符號。如果缺省,等於請求的檔案名的副檔名。 * CString strSrvURL -當類型?TASK_HTTP_RECV時,該屬性必需賦值。表示要連接的伺服器的URL位址。 * 下面這些屬性的值由DLL在使用過程中填充,不要去修改這些值。 * CString strAddr -表示伺服器的位址。 * UINT nPort -表示伺服器的埠號。 * CString strFileN -表示要下載的檔案名。 * CString strReferer -表示重定向已後的Referer的值。 * DWORD dwFileSize -文件的總的長度。 * DWORD dwBlockLen -每個塊的長度。 * char* lpszBlockMap -保存每個分割塊的狀態。第一次調用時,賦值?空。 * DWORD dwLowDateTime -被下載的文件在傳送方的最後修改日期的低32位。 * DWORD dwHighDateTime -被下載的文件在傳送方的最後修改日期的高32位。 * * 說明: * 當申請一個連接用於下載文件時,必需填寫該結構,並且將指標傳遞給PTask中的m_lprRecvInfo變數。在創建該結構後,一定要使用以下方法初始化結構: memset(m_lprRecvInfo,0,DWORD(&m_lprRecvInfo->nPort)-(DWORD)m_lprRecvInfo); 當類型?TASK_TCP_RECV時,所有的屬性都可以缺省。 當類型?TASK_HTTP_RECV時,必需設定strSrvURL屬性的值,表示想下載的文件的URL。 * * * * * * * * * * * * * * * * 結構名稱: PTHREADID * * 結構指標定義: LPPTHREADID * * 屬性: * bool bIsSuspend -線程是否被挂起。挂起時?true。 * bool bIsRun -線程是否運行。運行時?true。 * PTask* PTask -當前線程所歸屬的線程組物件。 * int nBlockNo -線程當前被分配的塊號。 * BYTE bySuffix -標識當前線程在線程組中的標識。 * CWinThread* pMyThread -指向這個線程的CWinThread物件的指標。 * SOCKET sock -傳遞給線程的已經創建好連接的SOCKET物件。 * CString strBlockFileN -當前分割塊所對應的檔案名。 * DWORD dwOffset -當前線程接收的起始偏移位置。這個偏移已經包含了被接收的長度。 * DWORD dwBlockLen -當前接收塊應該接收的長度。 * DWORD dwCompletedLen -當前塊已被接收或傳送的長度。 * HANDLE hEvent -用來同步的事件控制碼。 * * 說明: * 在這個結構中的值,都由DLL在運行過程中填充,不要試圖去修改這些值。 * * * * * * * * * * * * * * * * 結構名稱: PMSGINFO * * 結構指標定義: LPPMSGINFO * * 屬性: * CWnd* pWnd -接收消息的視窗的指標。 * UINT nMsg -發送的消息的標識。 * bool bIsAutoDel -標識該結構是否被自動刪除。如果值?false,需要你自己來刪除。值?true時,表示該結構所歸屬的物件一旦被刪除,該結構也被除。 * * 說明: * 保存向用戶發送消息時的視窗控制碼和消息的標識。 十二 PDefine類中的靜態函數說明 * 函數名稱: * SApplyAnPDHANDLE() * * 類型: * 靜態函數。 * * 參數: * 無。 * * 返回值: * PDHANDLE -物件的控制碼。 * * 說明: * 該函數返回一個用來處理資料流程的物件的控制碼。 * * * * * * * * * * * * * * * * 函數名稱: * SClosePDHANDLE(); * 類型: * * 參數: * PDHANDLE handle -物件的控制碼。 * 返回值: * 無。 * * 說明: * 該函數關閉一個處理物件流的物件的控制碼,以釋放所佔用的資源。 * * * 函數名稱: * SCoalitionFile() * *c
------
**********************************************************
哈哈&兵燹
最會的2大絕招 這個不會與那個也不會 哈哈哈 粉好

Delphi K.Top的K.Top分兩個字解釋Top代表尖端的意思,希望本討論區能提供Delphi的尖端新知
K.表Knowlege 知識,就是本站的標語:Open our mind
系統時間:2024-04-20 0:31:44
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!