MMORPG大型游戲設計與開發(part5 of net)


上一部分將服務器的具體代碼的實現介紹給了大家,想必大家也了解到了服務器處理一次消息的復雜度。如果大家能夠將各個過程掌握清楚,就會發覺其實整個邏輯與交互過程是比較清晰的。那么服務器與服務器之間的通訊,其實也就是相當於客戶端與服務器的通訊又是如何實現的呢?本文將用一個實例來將這個過程展示給大家。

 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

  同時接收連接:

  下一部分將對網絡部分的設計進行總結,同時這些文章也會不斷更新。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM