一個多人在線的棋牌類網(wǎng)絡(luò)游戲的項目臨近尾聲,我參與了該項目的整個設(shè)計流程,并且完成了90%的核心代碼。關(guān)于這個項目,有很多地方值得聊一聊。本系列不打算把這個項目將得多么詳細(xì)規(guī)范,那是設(shè)計文檔應(yīng)該描述的,我打算只說說一些值得注意的地方。這個項目的一個特別之處是,客戶端是手機,用戶通過移動網(wǎng)絡(luò)與服務(wù)器通信。和PC相比,手機的處理能力極弱,而且網(wǎng)絡(luò)流量費用昂貴。因為除了要考慮普通網(wǎng)絡(luò)游戲的一些問題之外,這兩點也需要在設(shè)計中充分考慮。首先是開發(fā)語言的選擇,由于服務(wù)器是Linux的環(huán)境,MS的技術(shù)直接排除,至于MONO嘛,我實在不放心??晒┻x擇的是C++和Java,Java勝在網(wǎng)絡(luò)能力強大,開發(fā)周期短,有眾多框架和開源庫的支持,要寫出爛得不可接受的代碼也不容易;C++則勝在速度快。綜合各方面因素,C++更容易把這個項目變成一堆代碼噩夢,我們選擇了Java。

一、網(wǎng)絡(luò)
網(wǎng)絡(luò)游戲,首先面臨的問題當(dāng)然是如何進行網(wǎng)絡(luò)通信。首先考慮的是HTTP協(xié)議,因為所有的J2ME手機都支持這個,我們當(dāng)然想盡可能的兼容用戶。而且HTTP協(xié)議封裝程度已經(jīng)非常高了,不用去考慮線程、同步、狀態(tài)管理、連接池,不過HTTP協(xié)議有兩個不爽的地方:
協(xié)議無狀態(tài),這個問題已經(jīng)困擾過很多人很多次了。我曾考慮過的解決辦法是改造HTTP協(xié)議,在數(shù)據(jù)傳輸完成之后不關(guān)閉socket,但是這樣做工作量非常大,在項目周期中,基本上就是Mission impossible,不予考慮。那么客戶也就只能通過輪詢的方式向服務(wù)器請求數(shù)據(jù)。
網(wǎng)絡(luò)流量過大。就這個項目來說,網(wǎng)絡(luò)間傳遞的只是指令,但是每次傳遞都要加上一堆毫無用處的HTTP Head,再加上客戶端需要做輪詢,這個流量對于手機來說簡直恐怖,經(jīng)簡單測試,按照0.03元/K的GPRS網(wǎng)絡(luò)費用計算,一局牌居然要消耗1元多的費用(每秒輪詢),實在不可接受。也許我們可以采用流量費包月的資費方式,不過這個話題與技術(shù)無關(guān)。
以上問題導(dǎo)致我們選擇了Socket,這意味著我們將沒有一個web環(huán)境,很多東西都要靠自己去實現(xiàn):線程管理、客戶狀態(tài)監(jiān)控、對象池、控制臺……….網(wǎng)絡(luò)部分打算采用Java NIO來實現(xiàn),這是一種新的網(wǎng)絡(luò)監(jiān)聽方式,基于事件的異步通信,可以提高性能。每個客戶端連接之后,會有一個獨立的SocketChannel與它通信,這個SocketChannel會在用戶的整個生存周期中存在。用戶如果斷開連接,服務(wù)器會得到-1,并且會拋出Connection reset異常,通過捕獲這兩個特征,可以在用戶意外斷開連接后清理相關(guān)的資源。由于NIO是異步通信的,所以沒有復(fù)雜的線程管理。

二、通信協(xié)議
這個項目并沒有復(fù)雜的通信指令,命令數(shù)量很有限,但是還是有個關(guān)鍵問題需要關(guān)注:流量。為了盡量減小流量,我們使用字節(jié)代替字符串來保存系統(tǒng)指令,這樣可以使流量減少一半,比如使用一個字節(jié)來保存一張撲克牌,字節(jié)高位表示花色,字節(jié)低位表示數(shù)字,如果0代表黑桃,那么黑桃三就應(yīng)該是0x03,這個需要靠位操作來實現(xiàn):

請自行處理操作中的各種異常。

四、撲克牌的生成
游戲中需要為用戶生成隨機的撲克牌,首先我們需要初始化一副牌,放到一個Hashmap中,每張牌以一個字節(jié)表示,高為代表花色,的為代表數(shù)字,生成整副牌:

如何隨機地得到其中的N張牌呢?我們的做法是生成一個0-55的隨機數(shù),用這個隨機數(shù)作主鍵從Hashmap中獲得對象,取得之后,把該對象從隊列中刪除,以免重復(fù)取得。由于java中的隨機數(shù)是根據(jù)時間生成的,所以有可能導(dǎo)致用戶得到的牌不夠散,每個用戶都摸到一條龍豈不是笑話?所以在生成隨機數(shù)的時候我們加入了一個大素數(shù)來作運算:

五、線程
實際上本系統(tǒng)并沒有復(fù)雜的線程管理,但是我想提供一個控制臺讓管理員可以管理游戲主線程,可以讓它停止、中段、恢復(fù)、重啟動,本來的設(shè)計是管理員通過與線程A打交道,通過A去管理主線程B,但是熟悉java線程的朋友都知道,線程互相管理基本上就是不實際的,舉個最簡單的例子,A如何銷毀B?也許你會說調(diào)用B的destroy()方法就好了,網(wǎng)上很多講解java線程的資料也確實是這么說的,但是他們都是鬼扯的,自己去看看java源代碼吧,Thread.destroy()方法的實際代碼如下:

事實真相是,Thread.destroy()方法自始至終就沒有被實現(xiàn)過。所有寫文章,教別人用這個方法銷毀線程的人,都去撞墻吧,丟人丟大了。最好的辦法是A負(fù)責(zé)生成一個B并且啟動它,然后B自己管理生存周期,A和B通過使用可共享的方法來通信,這是sun推薦的做法。
六、異步消息
用戶玩牌的過程中,有很多東西需要記錄下來,比如記錄用戶的積分、等級變化,記錄玩牌日志供數(shù)據(jù)統(tǒng)計等,當(dāng)用戶數(shù)量很多的時候,在數(shù)據(jù)庫中記錄這些信息會很耗費資源,用戶玩了一局之后會可能會等待很長時間。解決這個問題的方法是利用J2EE的消息bean來提供異步通信的機制,需要記錄數(shù)據(jù)的時候,系統(tǒng)會封裝一個值對象,發(fā)送給J2EE容器,這個操作是很快的,完成之后就返回,用戶可以繼續(xù)操作,不用關(guān)心消息何時被處理。J2EE的消息框架具備如下特征:
消息一定會被閱讀,而且只閱讀一次。JMS框架有自己的算法,把消息緩沖到硬盤,就算J2EE服務(wù)器死掉,消息也不會丟失。
系統(tǒng)采用點對點的Queue消息隊列,可以保證同等優(yōu)先級的消息先進先出。
在Jboss 4.0中,部署消息Bean和Queue隊列,都比weblogic 8.1來的容易,只需要在jboss.xml中聲明消息目的地,如果jboss發(fā)現(xiàn)該目的地不存在的話,會自動建立一個,實在很簡單。關(guān)于消息bean的開發(fā)與部署。

義烏市森焱網(wǎng)絡(luò)公司專注開發(fā)棋牌游戲十一年,本著顧客第一,質(zhì)量第一,售后第一的理念用心做好每一款游戲,森焱有你們才精彩!??!

義烏市森焱網(wǎng)絡(luò)是棋牌游戲開發(fā)公司,棋牌源碼定制,俱樂部棋牌游戲,可為您量身定做H5源碼