上一部分將服務器的具體代碼的實現介紹給了大家,想必大家也了解到了服務器處理一次消息的復雜度。如果大家能夠將各個過程掌握清楚,就會發覺其實整個邏輯與交互過程是比較清晰的。那么服務器與服務器之間的通訊,其實也就是相當於客戶端與服務器的通訊又是如何實現的呢?本文將用一個實例來將這個過程展示給大家。
CODE
bool ServerManager::connectserver() { uint8_t step = 0; __ENTER_FUNCTION bool result = false; pap_server_common_net::packets::serverserver::Connect* connectpacket = NULL; pap_common_net::socket::Base* billingsocket = NULL; const char *kServerIp = "192.168.200.100"; const uint16_t kServerPort = 12680; billingsocket = billing_serverconnection_.getsocket(); try { result = billingsocket->create(); if (!result) { step = 1; Assert(false); } result = billingsocket->connect( kServerIp, kServerPort); if (!result) { step = 2; printf("exception 2"); goto EXCEPTION; Assert(false); } result = billingsocket->set_nonblocking(); if (!result) { step = 3; printf("exception 3"); Assert(false); } result = billingsocket->setlinger(0); if (!result) { step = 4; printf("exception 4"); Assert(false); } g_log->fast_save_log(kBillingLogFile, "ServerManager::connectserver()" " ip:%s, port: %d, success", kServerIp, kServerPort); } catch(...) { step = 5; Assert(false); } result = addconnection( (pap_server_common_net::connection::Base*)&billing_serverconnection_); if (!result) { step = 6; Assert(false); } connectpacket = new pap_server_common_net::packets::serverserver::Connect(); connectpacket->set_serverid(9999); connectpacket->set_worldid(0); connectpacket->set_zoneid(0); result = billing_serverconnection_.sendpacket(connectpacket); SAFE_DELETE(connectpacket); if (!result) { step = 7; Assert(false); } g_log->fast_save_log(kBillingLogFile, "ServerManager::connectserver() is success!"); return true; EXCEPTION: g_log->fast_save_log( kBillingLogFile, "ServerManager::connectserver() have error, ip: %s, port: %d, step: %d", kServerIp, kServerPort, step); billing_serverconnection_.cleanup(); return false; __LEAVE_FUNCTION return false; }
我在這里簡單介紹下以上代碼:billing_serverconnection_是服務器的主連接,我們以這個連接連接到目標IP為192.168.200.100,port為12680的服務器。連接成功后,我們會發送一個連接信息的包到目標服務器上。可以看出來這個包里設置了三個參數,serverid(服務器id)、worldid(世界id)、zoneid(區域id)。至於這個方法需要放置的位置,必須置於服務器初始化完成后,因為我們要讓他創建一個可用的主套接字連接。
接着我們看看在服務器,執行完這個方法后又進行了哪些處理。
bool ServerManager::processoutput() { __ENTER_FUNCTION if (SOCKET_INVALID == maxfd_&& SOCKET_INVALID == minfd_) return false; uint16_t i; uint16_t connectioncount = billingconnection::Manager::getcount(); for (i = 0; i < connectioncount; ++i) { if (ID_INVALID == connectionids_[i]) continue; billingconnection::Server* serverconnection = NULL; //serverconnection = g_connectionpool->get(connectionids_[i]); serverconnection = &billing_serverconnection_; Assert(serverconnection); int32_t socketid = serverconnection->getsocket()->getid(); if (socketid_ == socketid) continue; if (FD_ISSET(socketid, &writefds_[kSelectUse])) { if (serverconnection->getsocket()->iserror()) { removeconnection(serverconnection); } else { try { if (!serverconnection->processoutput()) removeconnection(serverconnection); } catch(...) { removeconnection(serverconnection); } } } } return true; __LEAVE_FUNCTION return false; }
bool ServerManager::processcommand() { __ENTER_FUNCTION if (SOCKET_INVALID == maxfd_&& SOCKET_INVALID == minfd_) return false; uint16_t i; uint16_t connectioncount = billingconnection::Manager::getcount(); for (i = 0; i < connectioncount; ++i) { if (ID_INVALID == connectionids_[i]) continue; billingconnection::Server* serverconnection = NULL; //serverconnection = g_connectionpool->get(connectionids_[i]); serverconnection = &billing_serverconnection_; Assert(serverconnection); int32_t socketid = serverconnection->getsocket()->getid(); if (socketid_ == socketid) continue; if (serverconnection->getsocket()->iserror()) { removeconnection(serverconnection); } else { //connection is ok try { if (!serverconnection->processcommand(false)) removeconnection(serverconnection); } catch(...) { removeconnection(serverconnection); } } } return true; __LEAVE_FUNCTION return false; }
特別注意代碼中的注釋部分,因為驗證服務器是等待連接的,沒有這種主動連接別的服務器的需要,所以整個功能設計上沒有這部分,這個連接的方法也是我臨時添加上的。既然是驗證服務器的連接發送出去的包,那么對應處理輸出流和命令處理自然也應該是這個連接,否則我們的包就發送不出去了。
下面我們就來看看服務器端對這個包的處理代碼(handler):
#include "server/common/net/packets/serverserver/connect.h" #include "server/billing/connection/server.h" #include "server/common/base/log.h" namespace pap_server_common_net { namespace packets { namespace serverserver { uint32_t ConnectHandler::execute(Connect* packet, connection::Base* connection) { __ENTER_FUNCTION g_log->fast_save_log(kBillingLogFile, "ConnectHandler::execute(...) serverid: %d ...success", packet->get_serverid()); return kPacketExecuteStatusContinue; __LEAVE_FUNCTION return kPacketExecuteStatusError; } } //namespace serverserver } //namespace packets } //namespace pap_server_common_net
包收到后,我們需要進行一段邏輯處理,這個包是最簡單的連接,只是對包里面這個服務器id進行了輸出處理。
RESULT
服務器啟動:
這個例子中,我以兩個服務器作為客戶端,分別在windows和linux平台下。
LINUX
配置:
連接失敗:
連接成功:
服務器接收成功:
WINDOWS
配置:
連接失敗:
連接成功:
服務器接收成功:
SERVER
同時接收連接:
下一部分將對網絡部分的設計進行總結,同時這些文章也會不斷更新。