討論區快速選單
知識庫快速選單
程式設計俱樂部Facebook粉絲團 掌握Salesforce雲端管理秘訣 政府補助!學嵌入式+物聯網
[ 回上頁 ] [ 討論區發言規則 ]
我的IOCP伺服器開發筆記!!
更改我的閱讀文章字型大小
作者 : ku72(ku72) 人氣指數超過10000點
[ 貼文 188 | 人氣 17428 | 評價 420 | 評價/貼文 2.23 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/8/1 上午 02:49:05
請板主大大幫忙把"WinAPI TransmitFile 的一些疑問?"這討論串刪除
因為我想將他移到此討論串內 感恩^^

這是一篇我在開發高效IOCP伺服器時所遇到或者思考的問題筆記,
提供給想開發IOCP伺服器但並不熟悉相關機制,
或者現有的IOCP伺服器上遇到跟我相同的問題的解答,
另外希望各位大大能夠給我一些相關的指導 ^^~!

由於最近參考了一些程式"設計"相關類的書籍,
發現自己以前寫的物件在設計上來說十分的不良,
所以利用現在待業中對它們進行修改與重新設計。

在新的伺服器架構中將伺服器分為:
ServerDecoder這是一個虛類,他提供伺服器對封包的處理接口,
這提供給使用者繼承開發對於網路封包的處理。
BaseServer這是一個虛類,它提供了網路相關操作的接口,
並實作了對於ServerDecoder物件的管理機制。

整體上來說,我希望的是在日後開發過程時,
我能選擇自己想要的繼承自BaseServer並實作網路操作接口的類別,
並且能夠任意替換而不影響我專案對該BaseServer繼承類的使用;
然後開發各式各樣的ServerDecoder類別來處理相對應的網路事件,
由於整體架構封裝了封包的長度與事件編號處理,
所以我只要開發類似LoginDecoder、LogoutDecoder...等等的物件,
之後在各ServerDecoder物件內處理操作,
以前我總是認為類別就是把全部都包在一起,
實際上我只要在ServerDecoder提供的call back函式對相關的物件進行操作即可,
例如:
DBUnit gDBUnit;
PlayerManager gPlayerManager;

class LoginDecoder: public ServerDecoder
{
public:

....

void Decoding(....)
{
if( gDBUnit.Login(...) )
{
gPlManager.Join(...);
}
}
};
而不是把PlayerManager、DBUnit給包進去LoginDecoder或BaseServer內。

在大致規畫完伺服器架構後
我開始開發第一個基於windows平台下的IOCP伺服器。

PS:如果我在架構上有不合理或可以改進的地方請大大們告訴我 ^^!!
作者 : ku72(ku72) 人氣指數超過10000點
[ 貼文 188 | 人氣 17428 | 評價 420 | 評價/貼文 2.23 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/8/1 上午 03:04:09
遇到的第一個問題,FIN_WAIT_2的資源占據!!

由於之前對SOCKET都使用closesocket進行關閉,
在使用WSASocket建立新的SOCKET來使用,
但是在windows底下,對SOCKET提供了重用機制,
允許使用者在關閉SOCKET後提供給AcceptEx重複使用;
要重用SOCKET則必須使用DisconnectEx並指定TF_REUSE_SOCKET旗標來關閉,
當我在Debug程式時發現,
當連線進入後我呼叫DisconnectEx竟然就停住了!!
這在我使用closesocket來關閉SOCKET時,是不曾發生的事,
(中間省略使用TransmitFile與誤以為是TIME_WAIT的白癡狀態 ㄖㄖa)
於是我使用了netstat來檢查SOCKET的狀態,
發現Server部分處於FIN_WAIT_2,Client處於CLOSE_WAIT狀態,

按照TCP關閉流程:
Server --> FIN --> Client [發送後Server處於FIN_WAIT_1狀態,Client收到後處於CLOSE_WAIT狀態]
Server <-- ACK <-- Client [發送時Client處於CLOSE_WAIT狀態,Server收到後處於FIN_WAIT_2狀態]
Server <-- FIN <-- Client [Client呼叫closesocket後傳送並傳送後處於LAST_ACK狀態,Server收到前處於FIN_WAIT_2狀態]
Server --> ACK --> Client [發送後Server處於TIME_WAIT狀態(2MSL後正式進入CLOSED狀態),Client收到後處於CLOSED狀態]

發現原來是Client也處於debug狀態所以並未進入呼叫closesocket的操作,
導致TCP一直等待client完成關閉操作;
於是我在心裡想了,那要是有人惡意這樣搞不就會大量占據伺服器資源??
於是開始向google大神請求問題的解決方法,
由於平時有燒香,所以goolge大神回應了我的請求,
答案貌似很不幸的....他回應了我.........無......解..........
我很肯定我頭上冒出了三條直線....
在我即將爆發之際...google大神又回應我可修改TCPFinWait2Delay來降低影響.....
該值代表在FIN_WAIT_2狀態下的等待時間,
將其設定為最小值30秒後,提高了1/8的系統解決能力(預設是240秒).......

PS:上一篇忘了打 本系列文章 版權所有 嚴禁轉載 只准貼連接..(大家一起為程式設計俱樂部加油吧)
作者 : ku72(ku72) 人氣指數超過10000點
[ 貼文 188 | 人氣 17428 | 評價 420 | 評價/貼文 2.23 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/8/1 上午 08:08:20
遇到的地2個問題 斷了沒 到底斷了沒? Keep a live 設置!!

Server在接受Client端的連線後,
狠常發生的就是Client端異常的離開了連線,
包括網路掉線、網路設備掛點之類的...
此時Server將會無法正確得知連線的斷開,
通過設置KEEPALIVE便可以讓TCP在給定的時間內進行檢測,
讓我們的伺服器可以更好的對資源進行管理;
設置方法如下:

tcp_keepalive SetIn;
//傳回的資料長度
u_long ulBytesReturn = 0;
//設置心跳包設置
SetIn.onoff = 1;
SetIn.keepalivetime = 300000;
SetIn.keepaliveinterval = 1000;
//設置心跳包
WSAIoctl(mSock, SIO_KEEPALIVE_VALS, (LPvoid)&In, sizeof(tcp_keepalive), NULL, 0, &ulBytesReturn, 0, 0);

其中SIO_KEEPALIVE_VALS與tcp_keepalive定義於Mstcpip.h內,
這裡比較注意的是,設置的檢測時間一定要大於TCPFinWait2Delay,
因為KEEPALIVE為TCP底層檢測,
他會修正FIN_WAIT_2的檢測狀態,
如果小於TCPFinWait2Delay的檢測時間,
將會使得惡意的第三者有漏洞可以鑽(詳情見上一篇)。
作者 : ku72(ku72) 人氣指數超過10000點
[ 貼文 188 | 人氣 17428 | 評價 420 | 評價/貼文 2.23 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/8/1 上午 11:00:08
遇到的第三個問題 多核多執行緒(線)的資源管理!!

看到這個問題應該有不少人腦袋想了,
還不簡單使用CRITICAL_SECTION來作資料的保護就好,
如果要追求效能最多是在建立時,
使用InitializeCriticalSectionAndSpinCount,
來避免短操作卻進入內核切換;
不過我在這卻想思考使用原子操作不加鎖(這包括原子自旋鎖),
來對資料進行保護,
windows提供了一系列的原子操作函式,
在這裡列出我使用了的幾種:

原子操作將*Target設定為Value並傳回舊*Target值。
long InterlockedExchange(long volatile *Target, long Value);
原子操作比對*Target與Comperand值相同則設定*Target為Exchange並傳回Comperand值失敗傳回Exchange值。
long InterlockedCompareExchange(long volatile *Destination, long Exchange, long Comperand);
原子操作,將*lpAddend值+1。
long InterlockedIncrement(long volatile *lpAddend);
原子操作,將*lpAddend值-1。
long InterlockedDecrement(long volatile *lpAddend);

比較建議大家自己在Google上找尋相關的組合語言代碼,
因為比使用API少了一些函式呼叫的操作,會比較快。

解決完資料的保護方式後,再來思考的就是執行緒串行化的問題,
即使使用原子操作,還是不可避免的會產生串行化問題,
因為原子操作只是系統上最小的鎖之一,但它還是無法避免該問題;
於是我開始修改對記憶體管理與程式流程,
來避免多數執行緒串行在某個原子操作之上;
修改記憶體管理方式是因為當我們進行動態記憶體配置時,
系統會自動幫我們加上一全局的鎖,
且記憶體配置是全部執行緒所會使用到的,
於是我讓執行緒各自擁有自己的記憶體池,從而避免配置記憶體的這個鎖,
基本上在伺服器運作到一定程度後,
各執行緒記憶體池內的記憶體應該足夠於執行緒的使用,
而不再向系統要求新的記憶體;
對流程方面我捨棄了一次投遞多數的WSARecv操作(這是部分網路專家建議),
但是我認為一次投遞多個WSARecv有以下幾個缺點:

1.導致多執行緒存取同一連線資源的串行化。
2.WSARecv若投遞非0長度將以頁倍數大小鎖定。
3.在Server關閉時會收到大量的錯誤完成操作返回。
4.當惡意第三者先傳遞一個會讓Server無法立即完成的操作,在大量投遞小封包(禁用Nagle)會導致Server資源大量被占據並可能崩潰。

網路上專家提到可以傳遞一個0大小的WSARecv操作,
然後在進行阻塞或非阻塞的封包接收(這也跟我一樣只投遞一個);
我的想法是在大部分伺服器上,
都是Client對Server發出請求,Server對Client發出資料,
對一個請求指令來說通常很難大於4096Bytes長度(通常的系統頁大小),
那我們一次投遞一個WSARecv操作接收到封包後,
很大機率為已包含此次完整的封包,
當然也有可能會是一個完整+部分下一個封包,
或部分封包+完整封包+部分封包...等,
這只要對該資料進行切割應該比在進行WSARecv操作來的快一點,
如果封包不能被一次的WSARecv操作全部接納,
我們也可以用接收到的封包資訊得知封包的大小,
進而再次投遞一個能完整接收剩餘封包的WSARecv操作。

如此一來大致上整個架構就很少因為內部原因從而發生大量串形化操作了。

這次有點雜亂XD 體諒我國中沒畢業 國文表達能力不好 呵呵 ....
作者 : jonay(jonay) VC++優秀好手C++ Builder優秀好手C++優秀好手貼文超過500則
[ 貼文 887 | 人氣 8025 | 評價 5030 | 評價/貼文 5.67 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/8/1 上午 11:59:47
真的不想打擊你
你寫的文章與IOCP沒有任何關係(只是一般的socket處理)

IOCP從win2000開始支援
也就是說已經問世10年以上了(可以算是老技術)
比較稀奇的是一直沒有經典的範例
只在少數人之中使用,沒多少人敢用在商業程式上(一來除錯不易,二來也沒這麼多client端連上來)
比較常看到的是與socket連用,但是它也可以關連到file handle(也就是檔案處理)

而它怎麼來的呢?
堶惕t有一些老技術的影子
一、異步(非同步)
二、overlapped IO(完成後通知功能)
三、Thread(利用少量的Thread,最完美情形下是不進行content switch)
四、message queue(所以locker不一定需要,前提是各socket單獨作業沒有共用區需競爭存取)

如果你的client數小於1000,用IOCP是浪費

至於你的server被攻擊,解決辦法有很多
一個client連上來時,會提供的資訊有IP和port(此時你可以記錄它連上來的時間)
正規的第二步是提供ID和Password

因此利用這些資訊你就可以進行許多處理
一、一秒內沒有提供ID和Password的client準備進行斷線
二、同一個IP的準備進行斷線
三、送一些假資料給它,讓它以為已連上線。同時將它的資訊送給反攻擊處理程式(如掃它的port)
四、設定最大連線數,超過該值全部直接斷線,單位時間內認証不過的全部斷線

作者 : ku72(ku72) 人氣指數超過10000點
[ 貼文 188 | 人氣 17428 | 評價 420 | 評價/貼文 2.23 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/8/1 下午 12:37:22

>真的不想打擊你
>你寫的文章與IOCP沒有任何關係(只是一般的socket處理)

很抱歉 可能是因為這是我第N次進行IOCP伺服器的開發
所以我並沒有對細節部分進行描述
這邊假設的是大家都對IOCP有一定的認知下寫撰寫的
主要是描述我在IOCP架構下
遇到的問體解決方法與架構上的規畫

>IOCP從win2000開始支援
>也就是說已經問世10年以上了(可以算是老技術)
>比較稀奇的是一直沒有經典的範例
>只在少數人之中使用,沒多少人敢用在商業程式上(一來除錯不易,二來也沒這麼多client端連上來)
>比較常看到的是與socket連用,但是它也可以關連到file handle(也就是檔案處理)

這部分我認為因該是說我們都沒遇到需要大連線的伺服器專題開發
當然在大連線數下linux下使用epoll會是比較好的選擇
這也是我的下一個開發方向
BaseServer只是個虛構的類可以讓我使用不同方式實作
讓終端使用者只須修改建立的類別就好而無須修改程式內容

>而它怎麼來的呢?
>堶惕t有一些老技術的影子
>一、異步(非同步)
>二、overlapped IO(完成後通知功能)
>三、Thread(利用少量的Thread,最完美情形下是不進行content switch)
>四、message queue(所以locker不一定需要,前提是各socket單獨作業沒有共用區需競爭存取)
>
>如果你的client數小於1000,用IOCP是浪費

這我比較覺得應該是只有單核用IOCP才是浪費,
另外此伺服器並不假設運作在各socket單獨作業下,
所以對資源有進行另外的管理。

>至於你的server被攻擊,解決辦法有很多
>一個client連上來時,會提供的資訊有IP和port(此時你可以記錄它連上來的時間)
>正規的第二步是提供ID和Password
>因此利用這些資訊你就可以進行許多處理
>一、一秒內沒有提供ID和Password的client準備進行斷線
>二、同一個IP的準備進行斷線
>三、送一些假資料給它,讓它以為已連上線。同時將它的資訊送給反攻擊處理程式(如掃它的port)
>四、設定最大連線數,超過該值全部直接斷線,單位時間內認証不過的全部斷線

您提供的這個攻擊屬於正規的事件,
但是我在上面提到的都是比較冷門的事件,
例如FIN_WAIT_2這屬於TCP底層的運作,
你closesocket發送FIN包過去對方不回你ASK跟FIN包你就會占據該資源,
由於這是處於TCP層的工作,
在應用層並不會發覺到,你closesocket一樣會立即返回,
只是資源被占據在系統內而已,當在資源不足時伺服器在掛掉而已。

另外 IOCP是一個成熟的技術,
網路上有很多相關的程式碼,不懂得上Google都可以隨便下載個幾個來看,
但是上面只會告訴你IOCP的固定流程罷了;
我寫這文章主要是想針對開發IOCP伺服器下,
會遇到的問題與怎樣才是比較好的架構規畫下進行撰寫的,
並不會提供完整的程式碼,
因為那不切實際,只會被無限copy罷了(當然也可能很爛沒人copy),
只想針對觀念上的問題進行討論。

作者 : ku72(ku72) 人氣指數超過10000點
[ 貼文 188 | 人氣 17428 | 評價 420 | 評價/貼文 2.23 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/8/1 下午 01:04:36
另外補充一下,我會提到的問題,
大部分都是在IOCP下比較容易發生的(這指的是繁忙的網路操作下),
這些問題當然也是SOCKET的問題,
但是如果在windows下使用select、WSAAsyncSelect...等等,
進行伺服器開發由於對於連接的處理能力有限,
所以通常很難遇到的問題進行說明。
並不是我提的完全跟IOCP沒關係特此聲明。
作者 : ku72(ku72) 人氣指數超過10000點
[ 貼文 188 | 人氣 17428 | 評價 420 | 評價/貼文 2.23 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/8/1 下午 01:14:25
可能程式開發比較少遇到,
但是網管類的人原因該很常遇到,
諸如伺服器卡一堆TIME_WAIT、CLOSE_WAIT、FIN_WAIT_2...,
導致伺服器資源不足的問題,
這些問題可能發生在第三方提供的伺服器程式(Apache 、MySQL..等等),
因為這些都是TCP底層的通訊問題,
當然,程式開發者也可以無視這些問題,
畢竟這不屬於應用層面的處理,
但我覺得如果像我現在處於研究的態度來作開發的話,
盡可能對問題找出解決或改善的方法,
並竭盡自己的能力找出效能最大化的方式才是應有的態度;
雖然這通常都不會被使用到ㄖㄖa..
作者 : ku72(ku72) 人氣指數超過10000點
[ 貼文 188 | 人氣 17428 | 評價 420 | 評價/貼文 2.23 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/8/1 下午 03:27:31
遇到的第4個問題 WSASend的投遞!!

在這部分我思考的是,系統非分頁集區的極限與伺服器實際上的運作狀態,
在一般或者是未來雲端運用上的伺服器,
都是以客戶發送資料量較小的指令或請求,
而伺服器進行運算再返回較大資料量的資訊,
在這樣的前提下,
我發覺一次對一個連線只進行一次WSASend操作,
等待完成後在進行下一個雖然可以解決非分頁集區的占用,
但實際上不符合伺服器的運作,
因為在某些應用上,Server通常都會對Client傳遞多次的資料(例如遊戲伺服器),
在這前提下對連線同時發出多次的WSASend能大幅提高伺服器的效能,
當然還是在硬體的限制之內,
這時問題又來了,在Windows下send發送方將占據發送資料大小的非分頁集區,
如果惡意的第三者利用程式漏洞導致伺服器大量對連線進行WSASend操作,
從而占據非分頁集區的全部資源,
此時將使系統產生很多奇怪的問題.....
目前網路上大置的方法是對WSASend作次數限制,
或者是一開始提到的建立發送資料串列,
一次一個慢慢投遞到沒有資料需要投遞為止,
而我使用的是對資料量的限制(以頁為單位),
限制值是仰賴實體記憶體和當前連線數量做修正,
並且為了防止對方不接收伺服器發出的資料,
對每次的WSASend都將記錄他的完成時間,這有助於找出惡意的第三者。

這邊要提到的是,之前在遊戲公司中,
常常有玩家會爭議說遊戲伺服器把他踢斷的導致他的損失要求賠償,
我不知道其他公司是否能提出資料確認斷線的原因,
但當時我所在的公司對這方面並沒有能力提出,
我在規畫這個伺服器的架構時將這問題加入了解決,
在每個連線結束後,伺服器都會提供一個CONNECTIONINFO資訊,
提供給處理連線結束處理的ServerDecoder,
裡面就包含了此次連線關閉的原因與此次連線的資料傳遞記錄(可選)讓使用者來作記錄。
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/8/2 上午 05:34:52
如樓主所願, 我將原PO的文章刪掉了.

這個討論串很值得討論, 但應該已不是侷限於IOCP了, 比較偏於底層socket的問題. 這點個人也有很多沒仔細去研究過. 或許大家可以提供經驗參考一下, 尤其是在寫on-line game的人.

FIN_WAIT_2, 在等待對方FIN封包, CLOSE_WAITING, 在等待己方呼叫closesocket. 後面的討論, 已經不是這些低層的protocal了. 如何防止惡意攻擊, 或是更深層的運用. 期待&學習ing... ^^";
作者 : ku72(ku72) 人氣指數超過10000點
[ 貼文 188 | 人氣 17428 | 評價 420 | 評價/貼文 2.23 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/8/2 上午 08:41:05
感謝青衫大大^^
後面比較偏離協議問題是因為有人反映跟IOCP沒關係
所以我便先把跟IOCP有關的先整理上來
其實我比較想先把在IOCP下TCP傳輸
會遇到的問題先提出來討論完後
在對IOCP的開發問題做討論的
由於這是類似筆記般記錄心得或感想之類的東西
所以有些雜亂 傷害大家的腦細胞了 XD
作者 : ku72(ku72) 人氣指數超過10000點
[ 貼文 188 | 人氣 17428 | 評價 420 | 評價/貼文 2.23 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/8/2 下午 01:07:02
這篇是我目前的一個疑惑所以提出來想請教大家
我們知道對IOCP投遞WSASend操作後
會在資料複製到SOCKET緩衝區後產生完成操作
對於只針對一個對象的Send操作並沒有太大爭議
之前我對於要發送給複數以上的連接同樣的資料時
都要求使用者必須對各連接分別使用SendData操作
該函式將產生IODATA並將要傳送的資料複製進去
然後做為WSASend的參數
並在完成操作產生後後提供給使用者處理
目前想的是在提供一個新的函式
允許使用者對數個連接進行Send操作
該函式配置一個通用的IODATA並複製資料
作為資料的來源
再來對各連接進行WSASend操作時
所配置的IODATA不進行資料複製
只單純將資料來源指向通用的IODATA存放資料的位置
如此一來可以減少大量的資料複製
但卡在的是當完成操作返回時
如果把資料提供給使用者進行處理
萬一使用者把資料內容做修改
這樣一來就會導致未成功發送的連接發送到錯誤資料
目前考慮的解決方法
1是在該傳送資料給複數對象的函式註解上說明
該函式發送的資料在收到傳送完成通知後禁止修改其內容
2是提供該函式但還是對每個連接提供一份資料
大大們建議哪一個方法 還是有更好的方法哩??
作者 : ku72(ku72) 人氣指數超過10000點
[ 貼文 188 | 人氣 17428 | 評價 420 | 評價/貼文 2.23 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/8/7 下午 04:27:50
大家好像對這不感興趣 且我在這次的規劃上也走錯了方向
在這裡就簡單的把這話題做個結束吧
原本我打算開發一個高效的IOCP伺服器
但是今天與之前認識的前輩一番討論後
發現自己走錯了方向
目前伺服器機器價格不高
追求單一伺服器上程式的效能其實意義不大
與其思考一個伺服器能達到多少效能
還不如思考如何建置一個多伺服器架構的伺服器群組
當然在這次的開發中我也理解的TCP使用上的一些竅門
可以在日後開發伺服器時不再跳入陷阱
下一個新目標打算對群組伺服器架構上的規畫嚕

作者 : ku72(ku72) 人氣指數超過10000點
[ 貼文 188 | 人氣 17428 | 評價 420 | 評價/貼文 2.23 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/8/14 下午 08:09:08
附上這一兩個禮拜的開發結果
http://www.funp.net/736378
提供給對網路程式開發不熟的參考(全中文註解XD),
如果有人要使用的話請遵循MIT授權規範。

命名空間為 Lion

分為兩大類別 NetControl、BaseServer。

NetControl為Client端的應用物件,其延伸物件為使用IOCP、Epoll、Select開發的類別;
分別對應於Windows、Linux、Windows&Linux,
允許建立大量的(1000以上)的Client連線,當然也可以只指定使用一個執行緒處理一個連接;
這是我開發來作為遊戲Client端的物件,
雖然遊戲Client可以簡單使用socket來作處理,
但是考慮到當需要作壓力測試時,
可以很方便的將Client程式碼移植到機器人Client內,
並且就算是一個連線使用此物件也不會有效率降低的問題,
只是在使用上必須依照此物件的規範。

BaseServer為伺服器端的應用物件,其延伸物件為使用IOCP、Epoll、Select開發的類別;
分別對應於Windows、Linux、Windows&Linux,
支援連接其他伺服器、連線的傳遞/接收資料量記錄、連線關閉原因記錄和一些小功能,
但沒有使用無鎖編程,因為那版本以後我可能會用在商業軟體上。

以上兩類物件詳細資訊請參閱程式碼內註解比較容易理解,
因為要全部寫完字數應該不夠 XD
另外雖然提供的程式碼都經過我的測試並在VC2008與GCC編譯通過,
但不保證沒有BUG因為我不是神
有發現BUG請自行修正就好 應為這並不是專業網路庫
所以並不會有特定的人來維護他
並且我的下一版可能又是其它的花樣 so...
作者 : ku72(ku72) 人氣指數超過10000點
[ 貼文 188 | 人氣 17428 | 評價 420 | 評價/貼文 2.23 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/8/14 下午 08:29:06
連結已移除 抱歉 授權資訊沒弄好
好像不是直接說就行 弄好了再告訴大家
作者 : jackting(MagicJack)
[ 貼文 123 | 人氣 0 | 評價 410 | 評價/貼文 3.33 | 送出評價 10 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/8 上午 10:42:13

>遇到的第一個問題,FIN_WAIT_2的資源占據!!
...
>
>按照TCP關閉流程:
>Server --> FIN --> Client [發送後Server處於FIN_WAIT_1狀態,Client收到後處於CLOSE_WAIT狀態]
>Server <-- ACK <-- Client [發送時Client處於CLOSE_WAIT狀態,Server收到後處於FIN_WAIT_2狀態]
>Server <-- FIN <-- Client [Client呼叫closesocket後傳送並傳送後處於LAST_ACK狀態,Server收到前處於FIN_WAIT_2狀態]
>Server --> ACK --> Client [發送後Server處於TIME_WAIT狀態(2MSL後正式進入CLOSED狀態),Client收到後處於CLOSED狀態]
>

其實有偷機作法:
如果有 Server/Client 有一方可以確定已經完全沒有資料要傳了, 可以直接送出 RST flag 代替 FIN flag.
由其是 由 Server 端已經確認 Client 端要求的資料都已經送完了, 那就直接送出 RST 吧.
我可以確定有許多 Web Server 都這樣玩. (因為最容易被惡搞)
作者 : jackting(MagicJack)
[ 貼文 123 | 人氣 0 | 評價 410 | 評價/貼文 3.33 | 送出評價 10 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/8 上午 10:44:47
>如果有 Server/Client 有一方可以確定已經完全沒有資料要傳了, 可以直接送出 RST flag 代替 FIN flag.

Sorry, 打太快, 必需確認 "雙方" 都沒資料了.
作者 : jackting(MagicJack)
[ 貼文 123 | 人氣 0 | 評價 410 | 評價/貼文 3.33 | 送出評價 10 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/9/8 下午 05:05:13

>這篇是我目前的一個疑惑所以提出來想請教大家
>我們知道對IOCP投遞WSASend操作後
>會在資料複製到SOCKET緩衝區後產生完成操作
>對於只針對一個對象的Send操作並沒有太大爭議
>之前我對於要發送給複數以上的連接同樣的資料時
>都要求使用者必須對各連接分別使用SendData操作
>該函式將產生IODATA並將要傳送的資料複製進去
>然後做為WSASend的參數
>並在完成操作產生後後提供給使用者處理
>目前想的是在提供一個新的函式
>允許使用者對數個連接進行Send操作
>該函式配置一個通用的IODATA並複製資料
>作為資料的來源
>再來對各連接進行WSASend操作時
>所配置的IODATA不進行資料複製
>只單純將資料來源指向通用的IODATA存放資料的位置
>如此一來可以減少大量的資料複製
>但卡在的是當完成操作返回時
>如果把資料提供給使用者進行處理
>萬一使用者把資料內容做修改
>這樣一來就會導致未成功發送的連接發送到錯誤資料
>目前考慮的解決方法
>1是在該傳送資料給複數對象的函式註解上說明
>該函式發送的資料在收到傳送完成通知後禁止修改其內容
>2是提供該函式但還是對每個連接提供一份資料
>大大們建議哪一個方法 還是有更好的方法哩??


資料需要複製到 SOCKET 緩衝區, 是因為 TCP 的重傳機制的需求, 而且每一個 socket 後續要處理 TCP header 及 IP header 相關的計算
為了簡化 scoket 的實作所以才在前頭把下層協定的資料 copy 到 socket buffer.

我想你也可以為 這個目的 自己寫一個看看, 但是你接著會面臨的是和 windows 的 socket bug 修正同步的問題 (當然有可能 window 的 bug 你的 code 不會發生)

另外一種方法是不要用 socket 直接組封包發送, 就可以避開 socket buffer copy 的問題. (如果不需要 TCP 的 ACK 機制)
還有一種 TCP zero copy 的機制, 可以研究看看或許可以合用 (sorry, 我還沒空存細研究)
 板主 : 青衫 , Raymond
 > Visual C++ - 討論區
 - 最近熱門問答精華集
 - 全部歷史問答精華集
 - Visual C++ - 知識庫
  ■ 全站最新Post列表
  ■ 我的文章收藏
  ■ 我最愛的作者
  ■ 全站文章收藏排行榜
  ■ 全站最愛作者排行榜
  ■  月熱門主題
  ■  季熱門主題
  ■  熱門主題Top 20
  ■  本區Post排行榜
  ■  本區評價排行榜
  ■  全站專家名人榜
  ■  全站Post排行榜
  ■  全站評價排行榜
  ■  全站人氣排行榜
 請輸入關鍵字 
  開始搜尋
 
Top 10
評價排行
Visual C++
1 青衫 11070 
2 Raymond 10090 
3 Clier 7630 
4 小約翰 2500 
5 Cog 2030 
6 coco 1870 
7 aming 1410 
8 牧童哥 1400 
9 r2109 1380 
10 Akira 1350 
Visual C++
  專家等級 評價  
  一代宗師 10000  
  曠世奇才 5000  
  頂尖高手 3000  
  卓越專家 1500  
  優秀好手 750  
Microsoft Internet Explorer 6.0. Screen 1024x768 pixel. High Color (16 bit).
2000-2019 程式設計俱樂部 http://www.programmer-club.com.tw/
0.21875