最近幾天的閉關(guān)修練
,終于把OGF的網(wǎng)絡(luò)部分給補(bǔ)充完了,這樣大家就方便開發(fā)自己的網(wǎng)絡(luò)游戲.網(wǎng)絡(luò)應(yīng)用方面分為客戶端與服務(wù)器端,上集我們先介紹客戶端的使用,下集再介紹服務(wù)器端的的開發(fā).
客戶端的消息分發(fā)采用原有的隊(duì)列調(diào)配模式,機(jī)制上與計(jì)時(shí)器或動(dòng)作引擎沒有太大的差異,無非創(chuàng)建了一個(gè)空窗口與SOCKET進(jìn)行綁定,然后將SOCKET消息發(fā)送隊(duì)列后再分發(fā).除此之外,提供了簡(jiǎn)單的數(shù)據(jù)加/解密的支持,提供了SOCKET代理模式的支持.
好了,接下來就用實(shí)例來講解一下OGF的網(wǎng)絡(luò)應(yīng)用:
1.修改GameFrame.cpp的GameInit方法,添加以下兩行代碼,以開啟OGF對(duì)網(wǎng)絡(luò)服務(wù)的支持:
//開啟網(wǎng)絡(luò)引擎
m_GameFrameSetting.bUseSocketEngine = true;
//關(guān)閉網(wǎng)絡(luò)數(shù)據(jù)加密
m_GameFrameSetting.bEncrypted = false;
注:只要開啟并連能服務(wù)器后,就可以在GameBody中得到網(wǎng)絡(luò)消息的通知,包括:Connected, Read, Close三種事件消息
2.布置我們的客戶端界面.首先要有兩個(gè)輸入框,一個(gè)是填I(lǐng)P地址(名為IP),另一個(gè)是填連接端口(名為Port);然后添加一個(gè)文本按鈕,以指示系統(tǒng)連接或斷開(名為Connect);最后要加入一個(gè)消息框(名為Message),用于顯示提示信息.以上的對(duì)象為簡(jiǎn)單起見,均采用GameObjectText對(duì)象實(shí)現(xiàn).在布局方面,大家可采用XML的方式去布置(方便很多),也可以像示例中所示,在OnLoading事件中初始化(較為復(fù)雜).代碼中無非是創(chuàng)建對(duì)象,設(shè)置大小字體,位置...所以代碼不帖出來了,有興趣就看代碼.
3.在MainSection類中添加以下私有變量:
TCHAR szServerIP[16]; //服務(wù)器IP
TCHAR szServerPort[6]; //服務(wù)器端口
IGameObjectText* pCurrentInput; //當(dāng)前鍵盤焦點(diǎn)
4.由于還未做輸入框控件,因此程序中用文本對(duì)象模擬,這樣就要對(duì)文本框的鍵盤事件進(jìn)行處理.鍵盤處理分為4種:回車,TAB,退格鍵和允許錄入的字符集.處理方面大概分為以下幾個(gè)判斷:
I.回車(nKey==13),模擬鼠標(biāo)點(diǎn)擊連接按鈕的事件,所以基本上將鼠標(biāo)點(diǎn)擊連接按鈕處理拷貝過來就可以了.
II.TAB(nKey==9),pCurrentInput是記錄當(dāng)前擁有鍵盤焦點(diǎn)的對(duì)象,通過切換IP對(duì)象和PORT對(duì)象即可.
III.退格鍵(nKey==8),將當(dāng)前鍵盤焦點(diǎn)對(duì)象對(duì)應(yīng)的變量(szServerIP或szServerPort)最后的字符置為0;
IV.字符集,設(shè)置該輸入框能鍵入的字符范圍,也是將szServerIP或szServerPort最后的字符置為該字符,然后再在后補(bǔ)上0結(jié)束符
V.將szServerIP或szServerPort的內(nèi)容輸出至相應(yīng)的對(duì)象
5.連接按鈕和IP/PORT輸入框的鼠標(biāo)點(diǎn)擊處理.點(diǎn)擊IP/PORT輸入框只需設(shè)置pCurrentInput為當(dāng)前點(diǎn)擊對(duì)象即可,而點(diǎn)擊連接按鈕,我們要判斷當(dāng)前是否連通,若是則作斷開網(wǎng)絡(luò)處理,否則使用szServerIP和szServerPort為參數(shù)進(jìn)行網(wǎng)絡(luò)連接.連接按鈕的代碼如下:
if(szObjectName=="Connect"){
IGameObjectText* pGOText = GET_OBJECTPTR_INTERFACE(pGameObject,IGameObjectText);
CString szCaption = pGOText->GetText();
if(szCaption=="Connect"){
WORD wPort = atoi(szServerPort);
m_lpFMHandles->pClientSocket->Connect(szServerIP, wPort);
pCurrentInput = NULL;
}
else{
m_lpFMHandles->pClientSocket->CloseSocket(true);
pCurrentInput = NULL;
}
}
6.重載GameBody的OnEventClientSocketConnect事件,我們要判斷連接是否成功,可根據(jù)iErrorCode是否為0來判斷,成功為0,否則為失敗.
連接成功后,返回計(jì)算機(jī)名,并成生XX connected to Server的字樣,以發(fā)送給服務(wù)器端,實(shí)現(xiàn)簡(jiǎn)單的握手處理.
//回復(fù)服務(wù)器
TCHAR szHostName[32];
gethostname(szHostName, 32);
TCHAR szConnectMsg[64];
sprintf(szConnectMsg, "%s connected!\0", szHostName);
m_lpFMHandles->pClientSocket->SendData(10049, 2, szConnectMsg, strlen(szConnectMsg));
然后在提示框中輸出"Connected to Server!",最后將連接接按鈕上的文本改為"Disconnect".
IGameObject *pIGameObject = m_pCurrentSection->GetGameView()->GetObject("default\\default\\Message");
IGameObjectText *pText = GET_OBJECTPTR_INTERFACE(pIGameObject, IGameObjectText);
pText->SetText("Connected to Server!");
pIGameObject = m_pCurrentSection->GetGameView()->GetObject("default\\default\\Connect");
pText = GET_OBJECTPTR_INTERFACE(pIGameObject, IGameObjectText);
pText->SetText("Disconnect");
連接失敗,提示框則輸出錯(cuò)誤代碼:
IGameObject *pIGameObject = m_pCurrentSection->GetGameView()->GetObject("default\\default\\Message");
IGameObjectText *pText = GET_OBJECTPTR_INTERFACE(pIGameObject, IGameObjectText);
TCHAR szError[128];
sprintf(szError, "Connect error:%d.", iErrorCode);
pText->SetText(szError);
7.重載GameBody的OnEventClientSocketRead事件中,先判斷指令標(biāo)識(shí)是否可識(shí)別,然后將收到的內(nèi)容輸出至提示框中,并回復(fù)服務(wù)器端已收到消息.
switch(Command.wMainCmdID){
case 10049:
if(Command.wSubCmdID==3){
IGameObject *pIGameObject = m_pCurrentSection->GetGameView()->GetObject("default\\default\\Message");
IGameObjectText *pText = GET_OBJECTPTR_INTERFACE(pIGameObject, IGameObjectText);
pText->SetText((LPCTSTR)pBuffer, wDataSize);
//回復(fù)服務(wù)器
TCHAR szHostName[32];
gethostname(szHostName, 32);
TCHAR szReply[64];
sprintf(szReply, "%s recved text!\0", szHostName);
m_lpFMHandles->pClientSocket->SendData(10049, 4, szReply, strlen(szReply));
}
break;
}
8.重載GameBody的OnEventClientSocketClose事件.當(dāng)網(wǎng)絡(luò)斷開,即提示網(wǎng)絡(luò)斷開,并設(shè)置連接按鈕上的文本.
IGameObject *pIGameObject = m_pCurrentSection->GetGameView()->GetObject("default\\default\\Message");
IGameObjectText *pText = GET_OBJECTPTR_INTERFACE(pIGameObject, IGameObjectText);
pText->SetText("Server Closeed!");
pIGameObject = m_pCurrentSection->GetGameView()->GetObject("default\\default\\Connect");
pText = GET_OBJECTPTR_INTERFACE(pIGameObject, IGameObjectText);
pText->SetText("Connect");
9.至此,簡(jiǎn)單的網(wǎng)絡(luò)消息應(yīng)用的實(shí)例已完成了.雖然示例代碼比之前要多了點(diǎn),但應(yīng)該使用上還是算方便的.
Send函數(shù)的說明:
原形:virtual bool __cdecl SendData(WORD wMainCmdID, WORD wSubCmdID, void * pData, WORD wDataSize);
wMainCmdID與wSubCmdID實(shí)際上是OnEventClientSocketRead中的CMD_Command結(jié)構(gòu)的際,以兩層關(guān)系指示發(fā)送內(nèi)容的標(biāo)識(shí).
pData,待發(fā)送的數(shù)據(jù),可以是任意的結(jié)構(gòu)體和內(nèi)存區(qū)域.wDataSize則指示數(shù)據(jù)的長度.
運(yùn)行結(jié)果:


