數據分發的開發思想發展過程[2004年4月26日 9:44 ]

頭說做個很簡單的轉發就行了(不是項目計劃的一部分),當時,我也以爲,很簡單就行,不就是把收到的數據,按照配置文件的信息,找到目標機器,在發送出去嗎?於是,我做。

1.  BCB下面的過程

A.         在主窗體裏面,拖一個TServerSocketTClientSocket。定義兩個結構,一個是用來保存收到的信息(變量裏面只定義一個這個結構的變量),一個用來保存找目標機器的配置信息列表。分別爲

TADDR{        //地址信息

              AnsiString strID;<?xml:namespace prefix = o />

              AnsiString strip;

              Int          strPort;

              }

TMSG{          //發送內容假設是這樣

              AnsiString strID;

              AnsiString strTitle;

              AnsiString strMemo;

              }

紅色部分是用來做標記的,收到的內容有strID這個標記的時候,就發往有strID這個標記的ADDR

當時思路是這樣的:定義一個變量 TMSG Gmsg;每次ServerSocket收到東西的時候都往這個地方寫,從AddrList裏面找到對應的ADDR然後用ClientSocket發送出去。

是很簡單,還真的很簡單。後來發現有個缺點(網絡基本沒做過,有問題,非常正常,況且沒有認真研究過BCB,工作需要,況且思想總是不斷深入的嘛!今天就來個總結):

當數據讀下來,還沒等ClientSocket發送出去,新的數據來了,於是,有寫MSG的衝突了。這衝突還不是一兩次的事情。(當然這裏還有其他問題,包括後面的幾個方案,但是等後面考慮到的時候再說,如果最後還有什麼問題,各位看客[有嗎?]再留言提出來,後來也發現這個衝突也是有辦法解決的,用TCriticalSection,顧名思義,臨界區域,或者TEvent事件觸發也可以,但是不徹底解決問題)。

B. 頭又說了,A方案不行,就用B方案啊!呵呵,就是做一個鏈表,或者用隊列更適合VC中用的是CList的,BCB中自然就用TList了,呵呵!真是天才,@_@.

每次發送的時候都從鏈表的最前面取,收到的時候,就從後面添加進去。我還真不是高手,做了出來,還是有衝突,怎麼辦。這時候,我想到了多線程,猶如一縷陽光,從漆黑的夜空裏投射下來,我看到了希望(暈,這麼爛的東西也寫得出來)。下面是C方案了,呵呵

C. 當時還沒有高明到想到服務多線程的程度,於是想到了,收到以後,從地址列表中找到地址信息。然後創建一個發送數據的有ClientSocket的線程,這時候,當然,窗體上的ClientSocket就沒有用了,每個線程必須用一個ClientSocket。這下,發送數據的時候,想發送多久就發送多久吧!對其他的數據分發沒有影響。衝突明顯減少了,可還是有。終於被我發現,TCriticalSection這個類,(劉歡的聲音響起:千萬裏我追尋着你。。。)。衝突的地方主要就是從地址列表中找地址的時候,於是把找地址的單獨分出一個函數來。然後

GcritSect->Acquire();

…….操作

CcritSect->Release();

對應的在VC中有

CCriticalSection m_critSect;

M_critSet->Lock();

操作

m_critSect->Unlock();

當然上面應該是全局函數,要不然對別的線程就不會有影響了。

於是測試,做下面環境配置。

<?xml:namespace prefix = v />

機器1

機器2

機器3

機器N

模擬一個發送數據的觸發一下

<?xml:namespace prefix = w />

a->b箭頭表示a轉發數據到b。這只要在配置文件設置一下就行了。沒錯,就是要的一個循環。多發幾條過去,還行。偷偷,說一下,不起眼的時候,還是會死一次,呵呵。

這裏有個發現,把線程中 FreeOnTerminate 設爲true。然後

    OnTerminate = TaskThreadTerminate;

就可以在函數TaskThreadTerminate函數中清除資源了。

D.最後,突然發現。那個ServerSocket不是Blocking的。Server Type :stNonBlocking 。狂暈。另外還有一個stThreadBlocking的是怎麼回事呢?應該就是所謂多線程服務器吧。

經過對原程序的改造,覺得它的流程應該如下:

1.  OnGetThread的事件中,給它new一個TServerClientThread派生出來的對象假設類爲TDisSvrCltThread

看幫助文檔中的,TServerClientThread的幫助的範例,是

  while (!Terminated && ClientSocket->Connected)

  {//省略了一些代碼

      Stream = new TWinSocketStream(ClientSocket, 60000);

      if (pStream->WaitForData(60000))

if pStream->Read(Buffer, 10) == 0)

       ClientSocket->Close();

只有當收到數據爲0的時候才關閉連接。

2.  TDisSvrCltThreadClientExecute函數中,執行等待連接,讀取數據。並且發送數據。

上面有三個誤區:

1.  上面的多線程並不是一般想的,客戶端每發送一條數據來,就新建一個線程,然後Terminate這個線程。而是,ClientExecute結束後,線程並不結束,掛在那裏,有新的連接近來,繼續運行ClientExecute。只有當有新的連接進來,但是前一個線程還在忙的時候,才新建一個線程。反正,總保持最少線程數量。這樣的設計的確好。

2.  我用了一下上面的代碼while(…)

發現,上面,要是跑批量的話,有一個連接進來後,就會一直在跑這個線程,雖然有新的線程產生。但是實際上,端口卻搶不到。於是,客戶端連不上是常有的事(或許只是我的片面之詞)。後來,我是不管怎麼樣,每次連接,接收數據以後,都把連接關掉(當然這裏對要保持連接的無用)。當然,同時也把while去掉了,每次都從ClientExecute運行起來。這樣做的結果,是多服務線程和單個差不多:ServerSocket->ActiveThreads可以看出來。

3.  pStream->WaitForData(60000)好像沒什麼效果,常常會出現收到數據爲0的時候,也就是說,常常因此ClientSocket->Close();而浪費時間。到CSDNBCB板塊,找人要了兩個stThreadBlocking是用的範例。發現有個範例用了兩次WaitForData,我也試了一下,還真有效。這下很少出現收到0字節的情況了。

 

至於,這種情況下的數據發送,剛開始看TServerClientThread裏面有ClientSocket的屬性。我還以爲直接可以用來發送數據的呢!後來大概知道了點。TServerClientThread裏面,當有連接進來的時候,採取的是主動連接的客戶端去取數據(應該沒錯吧!)。具體的技術細節,就不清楚了。呵呵。我還是用前面用來發數據的線程來做,改的東西也不用很多。

當然也考慮多線程訪問衝突的問題:

n         服務線程之間(雖然大部分時間只有一個服務線程,但還是可能的)。地址配置信息查找的函數作爲臨界資源的問題,用TCriticalSection。有個決定全區是否分發數據的變量,可以用volatile關鍵字。

n         服務線程把參數傳遞進發送線程的時候,用TEvent,這樣就不會發送線程還沒有把數據保存起來,服務線程就把資源給清除掉了。

現在的方案看起來完美多了,不是嗎?

 

E. VC做的解決方案。

CSocket是現在用VC做網絡常用的類,也用過,用的大概比較久了,都忘了怎麼用。還是用它做起了可以轉發的東西。雖然問題多多。剛做的時候就發現,用CString放進結構裏,和上面AnsiString一樣。Delete結構的時候會出現的錯誤。用char strID[6]這樣替換,那個錯誤竟然沒了。懷疑是CString,AnsiString放進結構裏就會出現問題(BCB中也出現了)。於是,說到莫名其妙的錯誤時,發表了關於AnsiString放進結構裏有問題的意見,遭到一片痛罵。回來趕緊一試,真的,簡簡單單做一個,竟然沒有問題。鬱悶!

CSocket做出來的也不是阻塞的,而且看起來好幾個文件,沒多大用處。還是自己學學用API寫得了。於是,有了兩個線程函數(應該差不多了)。一個SvrSocketThreadCltSocketThread

該說的就是SvrSocketThread了。常常見到的SvrThread的都是

while(true)

{

        if(bWantToExit)

               Exit;

        SocketAccept();

        Do SomeThing…..

}

後來又看到這種寫法

if(bWantTExit)

        Exit;

SocketAccept();

AfxBeginThread(…);

DoSomeThing();

ExitThread;

也有種終於被我發現的感覺,不用說什麼,應該就是後一種寫法好得多了。也許是程序員的天性,見到好的代碼就爽。後一種代碼,把後面DoSomeThing的時間也使用起來了。也許不用我說,第一種寫法,就是單線程服務的寫法。第二中,毫無疑問,是多線程的了。或許,我真的是我太孤陋寡聞了。這麼好的代碼現在才發現,也許,早就見過,只是沒用到就沒注意。對於VC中事件觸發,可以用SetEventCreateEventWaitForSingleObjectWaitForMultiObjectapi

VC做的這個程序也完成了。還是有幾個問題:暫且記下來,以備後查。

1.            Socket重新綁定端口的問題。第一次綁定一個端口後,想個這個socket重新綁定,雖然,closesocketWSACleanup也調用過了。可就是無法綁定另外一個端口。現在是要綁定另外的端口,就把端口close掉,重新用socket函數建一個。難道是close以後,一定要重新建一個socket才能綁定。也沒有看到其他清除socket的函數。另外,如果重新新建一個socket的時候綁定的端口是被佔用的,後面就再也起不來了,即使重新給了一個空的端口。

2.            ListView中,建了一個CEdit,設了Tabstop屬性,ListView設了ShowSelectAlways,可是使用CEdit的時候,ListView選定的地方總是難看的灰色,總不會是要我爲這點小問題重載CListCtrl吧。

3.            狀態欄左下角都會有一個“就緒”的狀態顯示,資源中也能找到這個字串。現在想在程序中動態設定,卻又不知道怎麼動手,現在能做的就是添加一個Pane,“就緒”不是Pane

 

F. 終於到了高潮部分了,這就是今天在做的:提供多服務端口,且可以動態申請服務的服務程序。和上面的不同描述如下:

1.  用服務製作,看了好些關於服務程序編寫的代碼,都是自己完整寫出來的。當然最完整的還是那個包括安裝、啓動、控制、關閉等的HelloWorld程序。今天發現ATL Com嚮導裏面有個Service Exe的選項,看裏面用的是CComModule爲基類。這不是挺好的嘛!這就簡單多了。看代碼註冊部分分爲ServerService,哎,失敗,竟然分不出他們的區別。註冊一下。看起來好像Server不在控制面板組件裏面註冊,而Service有。想知道dll的那種服務是怎麼做的,有人告訴我麼?我想應該都有ServiceMain導出的,可是我發現有個dll(系統的),用depends看,只有一個函數導出,卻還能正常在組件裏面控制,不是要有個Handler的嗎?

2.  線程結構是這樣的:有一個專門處理申請開通端口的服務線程(當然要對應一個端口),並通過這個服務線程對其他線程進行管理。其他線程,分別管理一個分發端口。

3.  比上面的多了一層,用ini做配置文件似乎就有點不夠了,這裏用xml作爲配置文件,也算是學習一下MSXML.DLL的用法。於是,一個關於XML的專門類又誕生了,謂之CXmlFile

4.  考慮寫日誌的方法分兩種,系統日誌和業務日誌,系統日誌自然是記到控制面板的日誌裏,業務日誌則,由另外一個服務程序來做,可以考慮是假冒的服務程序——有界面,但是隱藏了界面,爲的是有窗體,可以通過SendMessagePostMessage來發送日誌。內容的傳輸,大概也可以通過內存映射來做,只是就不能做到實時記錄了(可能嗎?)。不知道有沒有辦法通過知道進程ID,來發送消息的方法呢?考慮用另外的程序來做日誌,一是學習、二是直接記日誌到硬盤,輸入輸出的操作影響性能,業務日誌實在太多了。當然,在前面的幾個階段中已經考慮到了這一點,並不是每次記日誌都保存到文件裏,而是達到一定大小的時候才寫文件。

5.  服務都是要求比較小的,默認生成的項目也不支持大部分MFC,呵呵。CComModule應該也算是MFC裏面的吧!SDK的東西用得太少,用起來實在不順手。比如說CString::FormatCArray這些要用到,自己編,可就覺得煩了。只好加入頭文件。不過,大部分來說,用的還是SDK的東西,至少CXmlFile是。

2004年7月11日補:

重新綁定端口已經可以了,也做得差不多了,做成服務,還是沒完成,比較失敗,或許別人說得對,學習高級語言讓人變得浮躁。如果有人需要,我可以發送VC源代碼。

上面說到每次都啓動新線程的方法,其實也有缺點,就是啓動新線程的時候,必然會多耗一點資源,再就是後來又看到用完成端口進行網絡編程的,應該是Windows網絡編程中最好的一種方法了。下面頁面參考

理解I/O Completion Port    nonocast(原作)
http://community.csdn.net/Expert/topic/3056/3056877.xml?temp=.3150751

發佈了38 篇原創文章 · 獲贊 0 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章