章國(guó)雁
(安徽工商職業(yè)學(xué)院 信息工程學(xué)院,安徽 合肥230001)
網(wǎng)絡(luò)游戲(Online Game)是指由軟件程序和信息數(shù)據(jù)構(gòu)成,通常以客戶端、網(wǎng)頁(yè)瀏覽器和包括移動(dòng)電話、聯(lián)網(wǎng)游戲機(jī)等各類(lèi)信息設(shè)備的其他移動(dòng)終端為載體,以游戲運(yùn)營(yíng)商服務(wù)器為處理器,以互聯(lián)網(wǎng)為數(shù)據(jù)傳輸媒介的游戲產(chǎn)品及服務(wù)[1]。網(wǎng)絡(luò)游戲依據(jù)載體的不同,包括基于普通PC 機(jī)的端游、基于瀏覽器運(yùn)行的頁(yè)游、基于XBOX、PS4等的主機(jī)游戲、基于手機(jī)/iPad等移動(dòng)設(shè)備的手游。其中,端游由于運(yùn)行在PC 機(jī)平臺(tái)上,該種類(lèi)游戲一般制作較為精良,畫(huà)面效果較好,受眾玩家對(duì)游戲的要求也更高一些;頁(yè)游主要運(yùn)行在瀏覽器上,直接打開(kāi)即可運(yùn)行游戲,該種類(lèi)游戲往往畫(huà)面效果較為一般,但由于不用下載游戲文件,打開(kāi)網(wǎng)頁(yè)可直接開(kāi)始玩,較為便捷,受眾群體多為對(duì)游戲畫(huà)面和玩法要求不高的玩家;主機(jī)游戲的畫(huà)面效果最好,制作最為精良,但要求有配套的主機(jī)設(shè)備,游戲操作對(duì)玩家的專(zhuān)業(yè)性要求也更高;手游受益于近些年手機(jī)和iPad等移動(dòng)端用戶的大量增加,該種類(lèi)游戲玩法和畫(huà)面效果好于頁(yè)游,較好地利用了用戶的空余間隔,玩法較為簡(jiǎn)便,是目前為止發(fā)展較好的類(lèi)別。
網(wǎng)絡(luò)協(xié)議是保障網(wǎng)絡(luò)游戲基本通信的前提,合理地選擇網(wǎng)絡(luò)協(xié)議將使游戲更加高效、穩(wěn)定和安全[2]。傳輸控制協(xié)議(TCP)和用戶數(shù)據(jù)報(bào)協(xié)議(UDP)是網(wǎng)絡(luò)游戲研發(fā)中常用的兩個(gè)協(xié)議。
傳輸控制協(xié)議作為一種面向有連接的協(xié)議,位于傳輸層,服務(wù)可靠性非常高,為了便于傳輸,將大塊的數(shù)據(jù)包以報(bào)文段為單位進(jìn)行分割,方便數(shù)據(jù)管理。傳輸控制協(xié)議之所以擁有可靠性,是該協(xié)議能夠把數(shù)據(jù)無(wú)誤地傳到對(duì)方,針對(duì)丟包可以進(jìn)行重發(fā),對(duì)次序錯(cuò)誤的數(shù)據(jù)包進(jìn)行順序調(diào)整;還具有控制通信流量的功能,發(fā)送數(shù)據(jù)前先確認(rèn)通信對(duì)方是否存在,不存在不會(huì)發(fā)送數(shù)據(jù)。通常傳輸控制協(xié)議連接會(huì)有三次“握手”建立,斷開(kāi)連接要經(jīng)過(guò)四次。
用戶數(shù)據(jù)報(bào)協(xié)議是無(wú)連接、不可靠的協(xié)議,發(fā)送數(shù)據(jù)前不確認(rèn)通信對(duì)方是否存在,直接把數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)中,因此并不能確認(rèn)該數(shù)據(jù)是否到達(dá)終端節(jié)點(diǎn),也不能確認(rèn)數(shù)據(jù)到達(dá)的順序是否正確。
Socket(套接字)由端口號(hào)拼接IP 地址構(gòu)成,它是傳輸層實(shí)現(xiàn)端到端通信的一個(gè)端點(diǎn),每一次連接都有兩個(gè)端點(diǎn),套接字表示形式為主機(jī)IP 地址:主機(jī)16位端口號(hào)。假設(shè)一個(gè)IP地址為202.57.134.29,端口號(hào)為85 的主機(jī),那么其套接字形式為(202.57.134.29:85)。
Select 模式是 Winsock 中最常見(jiàn)的 I/O 模型[3]。它主要是使用select 函數(shù)來(lái)實(shí)現(xiàn)對(duì)I/O的管理,并判斷套接字上是否能寫(xiě)入或者存在數(shù)據(jù)[4]。當(dāng)系統(tǒng)內(nèi)核檢測(cè)到進(jìn)程的1個(gè)或者多個(gè)I/O條件準(zhǔn)備完畢時(shí),Select 多路復(fù)用模式會(huì)通知該進(jìn)程。該模型的優(yōu)點(diǎn)在于減少了系統(tǒng)開(kāi)銷(xiāo),系統(tǒng)不必頻繁創(chuàng)建和維護(hù)大量的進(jìn)程或者線程。
現(xiàn)在網(wǎng)絡(luò)游戲功能復(fù)雜,玩家需要存儲(chǔ)大量的數(shù)據(jù),除了基本的用戶賬號(hào)、密碼等信息外,還有游戲角色相關(guān)的大量數(shù)據(jù)需要在數(shù)據(jù)庫(kù)中長(zhǎng)期保存。由于網(wǎng)絡(luò)游戲?qū)Ξ?huà)面?zhèn)鬏數(shù)耐叫砸筝^高,如果數(shù)據(jù)庫(kù)采用同步的模式,網(wǎng)絡(luò)延遲較大??梢允褂梅植际綌?shù)據(jù)庫(kù),采用異步的方式對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作。
游戲服務(wù)端主要任務(wù)是接收和處理游戲客戶端發(fā)送過(guò)來(lái)的大量信息,以及存儲(chǔ)和讀取玩家賬號(hào)角色相關(guān)的數(shù)據(jù),依據(jù)這一任務(wù)內(nèi)涵,一款通用的小型網(wǎng)絡(luò)游戲服務(wù)端框架可以按圖1 方式實(shí)現(xiàn),采用Select 多路復(fù)用服務(wù)端架構(gòu),“網(wǎng)絡(luò)底層”模塊位于架構(gòu)的底層,主要功能為處理數(shù)據(jù)粘包半包、協(xié)議解析等,主要代碼封裝在NetManager 類(lèi)中進(jìn)行實(shí)現(xiàn);“消息處理”模塊屬于架構(gòu)的邏輯層,如客戶端向服務(wù)端發(fā)送“移動(dòng)”協(xié)議時(shí),服務(wù)端在該模塊中記錄玩家的位置信息,然后把“移動(dòng)”協(xié)議廣播給所有在線的客戶端,其他客戶端收到信息后根據(jù)最新的數(shù)據(jù)更新其對(duì)應(yīng)玩家的位置;“事件處理”模塊主要處理玩家的上線和下線操作,玩家新上線時(shí),首先需要讀取數(shù)據(jù)庫(kù)中保存的數(shù)據(jù),然后初始化角色信息,當(dāng)玩家下線時(shí),需要保存玩家的最新數(shù)據(jù)到數(shù)據(jù)庫(kù)中等?!皵?shù)據(jù)庫(kù)底層”模塊包含游戲數(shù)據(jù)的新增、讀取、修改、刪除等功能,例如賬戶登錄時(shí)密碼的校驗(yàn)、新玩家注冊(cè)時(shí)賬戶名的重復(fù)性檢測(cè)、玩家數(shù)據(jù)(如等級(jí))的讀取等,是服務(wù)端和數(shù)據(jù)庫(kù)本身數(shù)據(jù)通信的中介。“存儲(chǔ)處理”模塊主要是區(qū)分?jǐn)?shù)據(jù)的存儲(chǔ)形式,游戲中有些數(shù)據(jù),如玩家等級(jí)、金幣、裝備、經(jīng)驗(yàn)值等需要長(zhǎng)期保存,有些則可以臨時(shí)保存。
該框架在網(wǎng)絡(luò)帶寬較好地情況下,可承載1 萬(wàn)名左右玩家同時(shí)在線游戲,可保持較好的游戲體驗(yàn)。該服務(wù)端框架采用TCP 協(xié)議進(jìn)行通信,使用Json 進(jìn)行通信編碼,使用MySQL 數(shù)據(jù)庫(kù)保存玩家數(shù)據(jù)。
圖1 服務(wù)端框架
服務(wù)端程序具有粘包半包處理、協(xié)議解析、心跳機(jī)制、數(shù)據(jù)庫(kù)存儲(chǔ)等功能。
服務(wù)端框架中“網(wǎng)絡(luò)底層”模塊的核心部分主要實(shí)現(xiàn)創(chuàng)建監(jiān)聽(tīng)套接字listenSocket、管理客戶端狀態(tài)列表checkReadList、綁定、開(kāi)啟端口監(jiān)聽(tīng)(服務(wù)器啟動(dòng)成功)、Select 多路復(fù)用檢測(cè)可讀對(duì)象、新客戶端連接處理ReadListenSocket、客戶端消息處理ReadCliendfd、定時(shí)器等功能,服務(wù)端進(jìn)入StartServerLoop循環(huán)狀態(tài),其核心代碼如下:
采用Json 編碼解碼,其格式簡(jiǎn)介、層次結(jié)構(gòu)清晰,不但便于人去閱讀和編寫(xiě),也便于機(jī)器去解析和生成,采用Json 編碼的方式可以提升網(wǎng)絡(luò)數(shù)據(jù)的傳輸效率。
.net 內(nèi)置了編碼解碼Json 的方法“JavaScriptJ-sonSerializer”,需要手動(dòng)引用System.web.Extensions命名空間。Json編碼解碼的代碼如下:
如果客戶端掉線或者信號(hào)不好導(dǎo)致網(wǎng)絡(luò)斷開(kāi),服務(wù)端應(yīng)該能及時(shí)發(fā)現(xiàn)并釋放資源,對(duì)單個(gè)客戶端來(lái)說(shuō),資源的釋放作用不是很明顯,因?yàn)閱蝹€(gè)客戶端只有一個(gè)Socket 占用資源,而對(duì)服務(wù)端來(lái)說(shuō)卻是連接著幾萬(wàn)甚至幾十萬(wàn)的資源,如果不能主動(dòng)釋放斷開(kāi)的資源,將造成服務(wù)端資源被大量占用。
心跳機(jī)制用于避免出現(xiàn)類(lèi)似的情況,TCP 協(xié)議本身具有心跳機(jī)制,但需要等待2 小時(shí)才會(huì)釋放資源,這對(duì)網(wǎng)絡(luò)游戲來(lái)說(shuō),顯然是不適用的,因此目前業(yè)內(nèi)主要是自行實(shí)現(xiàn)心跳機(jī)制,客戶端在固定時(shí)間間隔給服務(wù)端發(fā)送PING 協(xié)議,服務(wù)端在固定時(shí)間間隔收到后響應(yīng)發(fā)送PONG 協(xié)議;假設(shè)服務(wù)端在設(shè)定時(shí)間間隔內(nèi)因?yàn)榫W(wǎng)絡(luò)不順暢或者客戶端掉線沒(méi)有收到PING 協(xié)議,則可以判斷客戶端已經(jīng)掉線,釋放回收該資源用于其他客戶端;同樣,假設(shè)客戶端較長(zhǎng)時(shí)間內(nèi)沒(méi)有收到PONG 協(xié)議,則認(rèn)為網(wǎng)絡(luò)斷開(kāi)或者服務(wù)端已經(jīng)宕機(jī),客戶端可以釋放自身的資源。
網(wǎng)絡(luò)游戲中的數(shù)據(jù)量十分龐大,玩家數(shù)據(jù)有些必須在數(shù)據(jù)庫(kù)中進(jìn)行長(zhǎng)期存儲(chǔ),有些只需要臨時(shí)存儲(chǔ)即可,例如用戶賬號(hào)和密碼、玩家的金幣、等級(jí)、裝備種類(lèi)、任務(wù)等,這些信息時(shí)刻受到玩家關(guān)注,需要長(zhǎng)期存儲(chǔ),方便玩家上線后進(jìn)行數(shù)據(jù)調(diào)用;但有些數(shù)據(jù),例如玩家的當(dāng)前位置信息只需臨時(shí)存儲(chǔ),玩家重新上線后會(huì)被重置。在Player對(duì)象中設(shè)定一個(gè)PlayerData 類(lèi)型的對(duì)象,用來(lái)保存所有需要存儲(chǔ)到數(shù)據(jù)庫(kù)的信息,以昵稱(chēng)、金幣、等級(jí)三個(gè)屬性為例。代碼如下:
游戲開(kāi)發(fā)中使用最多的是MySQL數(shù)據(jù)庫(kù),當(dāng)服務(wù)端與數(shù)據(jù)庫(kù)需要進(jìn)行數(shù)據(jù)交互時(shí),通過(guò)發(fā)送SQL語(yǔ)句進(jìn)行數(shù)據(jù)的操作,包括新增、修改、讀取、刪除等操作。配置MySQL數(shù)據(jù)庫(kù)分為兩個(gè)步驟,一是安裝MySQL 服務(wù)器,開(kāi)始監(jiān)聽(tīng)端口;二是使用第三方庫(kù)來(lái)編碼和解碼MySQL 特定形式的協(xié)議。采用Navicat 軟件來(lái)進(jìn)行數(shù)據(jù)庫(kù)管理,新建數(shù)據(jù)庫(kù)名為“game”,新建“account”表和“player”表,如圖2所示。
圖2 MySQL數(shù)據(jù)庫(kù)表
服務(wù)端框架搭建成功后,如圖3 所示,連接成功,服務(wù)端啟動(dòng),開(kāi)始對(duì)服務(wù)端進(jìn)行監(jiān)聽(tīng),客戶端使用Unity3D游戲引擎進(jìn)行功能測(cè)試,客戶端連接、賬號(hào)注冊(cè)、登錄、心跳機(jī)制、客戶端斷開(kāi)等功能正常運(yùn)行。
圖3 服務(wù)端測(cè)試
基于Select多路復(fù)用的通用服務(wù)端框架是一套功能較為完備的C#通用服務(wù)端程序,可用于小規(guī)模同時(shí)在線,為網(wǎng)絡(luò)游戲服務(wù)端開(kāi)發(fā)商業(yè)應(yīng)用提供了一定的技術(shù)參考。