最近幾天的閉關修練
,終于把OGF的網絡部分給補充完了,這樣大家就方便開發自己的網絡游戲.網絡應用方面分為客戶端與服務器端,上集我們先介紹客戶端的使用,下集再介紹服務器端的的開發.
客戶端的消息分發采用原有的隊列調配模式,機制上與計時器或動作引擎沒有太大的差異,無非創建了一個空窗口與SOCKET進行綁定,然后將SOCKET消息發送隊列后再分發.除此之外,提供了簡單的數據加/解密的支持,提供了SOCKET代理模式的支持.
好了,接下來就用實例來講解一下OGF的網絡應用:
1.修改GameFrame.cpp的GameInit方法,添加以下兩行代碼,以開啟OGF對網絡服務的支持:
//開啟網絡引擎
m_GameFrameSetting.bUseSocketEngine = true;
//關閉網絡數據加密
m_GameFrameSetting.bEncrypted = false;
注:只要開啟并連能服務器后,就可以在GameBody中得到網絡消息的通知,包括:Connected, Read, Close三種事件消息
2.布置我們的客戶端界面.首先要有兩個輸入框,一個是填IP地址(名為IP),另一個是填連接端口(名為Port);然后添加一個文本按鈕,以指示系統連接或斷開(名為Connect);最后要加入一個消息框(名為Message),用于顯示提示信息.以上的對象為簡單起見,均采用GameObjectText對象實現.在布局方面,大家可采用XML的方式去布置(方便很多),也可以像示例中所示,在OnLoading事件中初始化(較為復雜).代碼中無非是創建對象,設置大小字體,位置...所以代碼不帖出來了,有興趣就看代碼.
3.在MainSection類中添加以下私有變量:
TCHAR szServerIP[16]; //服務器IP
TCHAR szServerPort[6]; //服務器端口
IGameObjectText* pCurrentInput; //當前鍵盤焦點
4.由于還未做輸入框控件,因此程序中用文本對象模擬,這樣就要對文本框的鍵盤事件進行處理.鍵盤處理分為4種:回車,TAB,退格鍵和允許錄入的字符集.處理方面大概分為以下幾個判斷:
I.回車(nKey==13),模擬鼠標點擊連接按鈕的事件,所以基本上將鼠標點擊連接按鈕處理拷貝過來就可以了.
II.TAB(nKey==9),pCurrentInput是記錄當前擁有鍵盤焦點的對象,通過切換IP對象和PORT對象即可.
III.退格鍵(nKey==8),將當前鍵盤焦點對象對應的變量(szServerIP或szServerPort)最后的字符置為0;
IV.字符集,設置該輸入框能鍵入的字符范圍,也是將szServerIP或szServerPort最后的字符置為該字符,然后再在后補上0結束符
V.將szServerIP或szServerPort的內容輸出至相應的對象
5.連接按鈕和IP/PORT輸入框的鼠標點擊處理.點擊IP/PORT輸入框只需設置pCurrentInput為當前點擊對象即可,而點擊連接按鈕,我們要判斷當前是否連通,若是則作斷開網絡處理,否則使用szServerIP和szServerPort為參數進行網絡連接.連接按鈕的代碼如下:
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事件,我們要判斷連接是否成功,可根據iErrorCode是否為0來判斷,成功為0,否則為失敗.
連接成功后,返回計算機名,并成生XX connected to Server的字樣,以發送給服務器端,實現簡單的握手處理.
//回復服務器
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");
連接失敗,提示框則輸出錯誤代碼:
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事件中,先判斷指令標識是否可識別,然后將收到的內容輸出至提示框中,并回復服務器端已收到消息.
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);
//回復服務器
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事件.當網絡斷開,即提示網絡斷開,并設置連接按鈕上的文本.
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.至此,簡單的網絡消息應用的實例已完成了.雖然示例代碼比之前要多了點,但應該使用上還是算方便的.
Send函數的說明:
原形:virtual bool __cdecl SendData(WORD wMainCmdID, WORD wSubCmdID, void * pData, WORD wDataSize);
wMainCmdID與wSubCmdID實際上是OnEventClientSocketRead中的CMD_Command結構的際,以兩層關系指示發送內容的標識.
pData,待發送的數據,可以是任意的結構體和內存區域.wDataSize則指示數據的長度.
運行結果:


